02110-1301, USA. */
#include <config.h>
+
#include "repeat.h"
-#include <libpspp/message.h>
+
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
-#include <libpspp/alloc.h>
-#include <language/command.h>
+
#include <data/dictionary.h>
-#include "intprops.h"
-#include <libpspp/message.h>
-#include <language/line-buffer.h>
+#include <data/procedure.h>
+#include <data/settings.h>
+#include <libpspp/getl.h>
+#include <language/command.h>
#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <libpspp/alloc.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <libpspp/pool.h>
-#include <data/settings.h>
#include <libpspp/str.h>
#include <data/variable.h>
+#include "intprops.h"
+
#include "gettext.h"
#define _(msgid) gettext (msgid)
-#include <libpspp/debug-print.h>
-
/* Defines a list of lines used by DO REPEAT. */
struct line_list
{
/* A DO REPEAT...END REPEAT block. */
struct repeat_block
{
+ struct getl_interface parent ;
+
struct pool *pool; /* Pool used for storage. */
+ struct dataset *ds; /* The dataset for this block */
struct line_list *first_line; /* First line in line buffer. */
struct line_list *cur_line; /* Current line in line buffer. */
int loop_cnt; /* Number of loops. */
bool print; /* Print lines as executed? */
};
-static bool parse_specification (struct repeat_block *);
-static bool parse_lines (struct repeat_block *);
+static bool parse_specification (struct lexer *, struct repeat_block *);
+static bool parse_lines (struct lexer *, struct repeat_block *);
static void create_vars (struct repeat_block *);
-static int parse_ids (struct repeat_entry *);
-static int parse_numbers (struct repeat_entry *);
-static int parse_strings (struct repeat_entry *);
+static int parse_ids (struct lexer *, const struct dictionary *dict,
+ struct repeat_entry *, struct pool *);
+
+static int parse_numbers (struct lexer *, struct repeat_entry *,
+ struct pool *);
+
+static int parse_strings (struct lexer *, struct repeat_entry *,
+ struct pool *);
+
+static void do_repeat_filter (struct getl_interface *, struct string *);
+static bool do_repeat_read (struct getl_interface *, struct string *);
+static void do_repeat_close (struct getl_interface *);
+static bool always_false (const struct getl_interface *i UNUSED);
-static void do_repeat_filter (struct string *line, void *block);
-static bool do_repeat_read (struct string *line, char **file_name,
- int *line_number, void *block);
-static void do_repeat_close (void *block);
int
-cmd_do_repeat (void)
+cmd_do_repeat (struct lexer *lexer, struct dataset *ds)
{
struct repeat_block *block;
block = pool_create_container (struct repeat_block, pool);
+ block->ds = ds;
- if (!parse_specification (block) || !parse_lines (block))
+ if (!parse_specification (lexer, block) || !parse_lines (lexer, block))
goto error;
create_vars (block);
block->cur_line = NULL;
block->loop_idx = -1;
- getl_include_filter (do_repeat_filter, do_repeat_close, block);
- getl_include_function (do_repeat_read, NULL, block);
+
+ block->parent.read = do_repeat_read;
+ block->parent.close = do_repeat_close;
+ block->parent.filter = do_repeat_filter;
+ block->parent.interactive = always_false;
+
+ getl_include_source ( (struct getl_interface *) block);
return CMD_SUCCESS;
/* Parses the whole DO REPEAT command specification.
Returns success. */
static bool
-parse_specification (struct repeat_block *block)
+parse_specification (struct lexer *lexer, struct repeat_block *block)
{
char first_name[LONG_NAME_LEN + 1];
{
struct repeat_entry *e;
struct repeat_entry *iter;
+ struct dictionary *dict = dataset_dict (block->ds);
int count;
/* Get a stand-in variable name and make sure it's unique. */
- if (!lex_force_id ())
+ if (!lex_force_id (lexer))
return false;
- if (dict_lookup_var (default_dict, tokid))
+ if (dict_lookup_var (dict, lex_tokid (lexer)))
msg (SW, _("Dummy variable name \"%s\" hides dictionary "
"variable \"%s\"."),
- tokid, tokid);
+ lex_tokid (lexer), lex_tokid (lexer));
for (iter = block->macros; iter != NULL; iter = iter->next)
- if (!strcasecmp (iter->id, tokid))
+ if (!strcasecmp (iter->id, lex_tokid (lexer)))
{
- msg (SE, _("Dummy variable name \"%s\" is given twice."), tokid);
+ msg (SE, _("Dummy variable name \"%s\" is given twice."),
+ lex_tokid (lexer));
return false;
}
list. */
e = pool_alloc (block->pool, sizeof *e);
e->next = block->macros;
- strcpy (e->id, tokid);
+ strcpy (e->id, lex_tokid (lexer));
block->macros = e;
/* Skip equals sign. */
- lex_get ();
- if (!lex_force_match ('='))
+ lex_get (lexer);
+ if (!lex_force_match (lexer, '='))
return false;
/* Get the details of the variable's possible values. */
- if (token == T_ID)
- count = parse_ids (e);
- else if (lex_is_number ())
- count = parse_numbers (e);
- else if (token == T_STRING)
- count = parse_strings (e);
+ if (lex_token (lexer) == T_ID)
+ count = parse_ids (lexer, dict, e, block->pool);
+ else if (lex_is_number (lexer))
+ count = parse_numbers (lexer, e, block->pool);
+ else if (lex_token (lexer) == T_STRING)
+ count = parse_strings (lexer, e, block->pool);
else
{
- lex_error (NULL);
+ lex_error (lexer, NULL);
return false;
}
if (count == 0)
return false;
+ if (lex_token (lexer) != '/' && lex_token (lexer) != '.')
+ {
+ lex_error (lexer, NULL);
+ return false;
+ }
/* If this is the first variable then it defines how many
replacements there must be; otherwise enforce this number of
return false;
}
- lex_match ('/');
+ lex_match (lexer, '/');
}
- while (token != '.');
+ while (lex_token (lexer) != '.');
return true;
}
/* Read all the lines we are going to substitute, inside the DO
REPEAT...END REPEAT block. */
static bool
-parse_lines (struct repeat_block *block)
+parse_lines (struct lexer *lexer, struct repeat_block *block)
{
char *previous_file_name;
struct line_list **last_line;
for (;;)
{
- const char *cur_file_name;
- int cur_line_number;
+ const char *cur_file_name = getl_source_name ();
+ int cur_line_number = getl_source_location ();
struct line_list *line;
+ struct string cur_line_copy;
bool dot;
- if (!getl_read_line (NULL))
+ if (! lex_get_line_raw (lexer))
return false;
/* If the current file has changed then record the fact. */
- getl_location (&cur_file_name, &cur_line_number);
- if (previous_file_name == NULL
+ if (cur_file_name &&
+ (previous_file_name == NULL
|| !strcmp (cur_file_name, previous_file_name))
+ )
previous_file_name = pool_strdup (block->pool, cur_file_name);
- ds_rtrim_spaces (&getl_buf);
- dot = ds_chomp (&getl_buf, get_endcmd ());
- if (recognize_do_repeat (ds_c_str (&getl_buf)))
+ ds_init_string (&cur_line_copy, lex_entire_line_ds (lexer) );
+ ds_rtrim (&cur_line_copy, ss_cstr (CC_SPACES));
+ dot = ds_chomp (&cur_line_copy, get_endcmd ());
+
+ if (recognize_do_repeat (ds_cstr (&cur_line_copy)))
nesting_level++;
- else if (recognize_end_repeat (ds_c_str (&getl_buf), &block->print))
+ else if (recognize_end_repeat (ds_cstr (&cur_line_copy), &block->print))
{
if (nesting_level-- == 0)
{
- lex_discard_line ();
+ lex_discard_line (lexer);
+ ds_destroy (&cur_line_copy);
return true;
}
}
if (dot)
- ds_putc (&getl_buf, get_endcmd ());
+ ds_put_char (&cur_line_copy, get_endcmd ());
line = *last_line = pool_alloc (block->pool, sizeof *line);
line->next = NULL;
line->file_name = previous_file_name;
line->line_number = cur_line_number;
- line->line = pool_strdup (block->pool, ds_c_str (&getl_buf));
+ line->line = pool_strdup (block->pool, ds_cstr (&cur_line_copy) );
last_line = &line->next;
+
+ ds_destroy (&cur_line_copy);
}
- lex_discard_line ();
+ lex_discard_line (lexer);
return true;
}
{
/* Ignore return value: if the variable already
exists there is no harm done. */
- dict_create_var (default_dict, iter->replacement[i], 0);
+ dict_create_var (dataset_dict (block->ds), iter->replacement[i], 0);
}
}
}
/* Parses a set of ids for DO REPEAT. */
static int
-parse_ids (struct repeat_entry *e)
+parse_ids (struct lexer *lexer, const struct dictionary *dict,
+ struct repeat_entry *e, struct pool *pool)
{
- size_t i;
size_t n = 0;
-
e->type = VAR_NAMES;
- e->replacement = NULL;
-
- do
- {
- char **names;
- size_t nnames;
-
- if (!parse_mixed_vars (&names, &nnames, PV_NONE))
- return 0;
-
- e->replacement = xnrealloc (e->replacement,
- nnames + n, sizeof *e->replacement);
- for (i = 0; i < nnames; i++)
- {
- e->replacement[n + i] = xstrdup (names[i]);
- free (names[i]);
- }
- free (names);
- n += nnames;
- }
- while (token != '/' && token != '.');
-
- return n;
+ return parse_mixed_vars_pool (lexer, dict, pool,
+ &e->replacement, &n, PV_NONE) ? n : 0;
}
-/* Stores VALUE into *REPL. */
-static inline void
-store_numeric (char **repl, long value)
+/* Adds STRING to E's list of replacements, which has *USED
+ elements and has room for *ALLOCATED. Allocates memory from
+ POOL. */
+static void
+add_replacement (char *string,
+ struct repeat_entry *e, struct pool *pool,
+ size_t *used, size_t *allocated)
{
- *repl = xmalloc (INT_STRLEN_BOUND (value) + 1);
- sprintf (*repl, "%ld", value);
+ if (*used == *allocated)
+ e->replacement = pool_2nrealloc (pool, e->replacement, allocated,
+ sizeof *e->replacement);
+ e->replacement[(*used)++] = string;
}
/* Parses a list of numbers for DO REPEAT. */
static int
-parse_numbers (struct repeat_entry *e)
+parse_numbers (struct lexer *lexer, struct repeat_entry *e, struct pool *pool)
{
- /* First and last numbers for TO, plus the step factor. */
- long a, b;
-
- /* Alias to e->replacement. */
- char **array;
-
- /* Number of entries in array; maximum number for this allocation
- size. */
- int n, m;
-
- n = m = 0;
+ size_t used = 0;
+ size_t allocated = 0;
+
e->type = OTHER;
- e->replacement = array = NULL;
+ e->replacement = NULL;
do
{
+ long a, b, i;
+
/* Parse A TO B into a, b. */
- if (!lex_force_int ())
+ if (!lex_force_int (lexer))
return 0;
- a = lex_integer ();
+ a = lex_integer (lexer);
- lex_get ();
- if (token == T_TO)
+ lex_get (lexer);
+ if (lex_token (lexer) == T_TO)
{
- lex_get ();
- if (!lex_force_int ())
+ lex_get (lexer);
+ if (!lex_force_int (lexer))
return 0;
- b = lex_integer ();
-
- lex_get ();
+ b = lex_integer (lexer);
+ if (b < a)
+ {
+ msg (SE, _("%ld TO %ld is an invalid range."), a, b);
+ return 0;
+ }
+ lex_get (lexer);
}
- else b = a;
+ else
+ b = a;
- if (n + (abs (b - a) + 1) > m)
- {
- m = n + (abs (b - a) + 1) + 16;
- e->replacement = array = xnrealloc (array,
- m, sizeof *e->replacement);
- }
+ for (i = a; i <= b; i++)
+ add_replacement (pool_asprintf (pool, "%ld", i),
+ e, pool, &used, &allocated);
- if (a == b)
- store_numeric (&array[n++], a);
- else
- {
- long iter;
-
- if (a < b)
- for (iter = a; iter <= b; iter++)
- store_numeric (&array[n++], iter);
- else
- for (iter = a; iter >= b; iter--)
- store_numeric (&array[n++], iter);
- }
- lex_match (',');
+ lex_match (lexer, ',');
}
- while (token != '/' && token != '.');
- e->replacement = xrealloc (array, n * sizeof *e->replacement);
+ while (lex_token (lexer) != '/' && lex_token (lexer) != '.');
- return n;
+ return used;
}
/* Parses a list of strings for DO REPEAT. */
int
-parse_strings (struct repeat_entry *e)
+parse_strings (struct lexer *lexer, struct repeat_entry *e, struct pool *pool)
{
- char **string;
- int n, m;
-
+ size_t used = 0;
+ size_t allocated = 0;
+
e->type = OTHER;
- string = e->replacement = NULL;
- n = m = 0;
+ e->replacement = NULL;
do
{
- if (token != T_STRING)
+ char *string;
+
+ if (lex_token (lexer) != T_STRING)
{
- int i;
msg (SE, _("String expected."));
- for (i = 0; i < n; i++)
- free (string[i]);
- free (string);
return 0;
}
- if (n + 1 > m)
- {
- m += 16;
- e->replacement = string = xnrealloc (string,
- m, sizeof *e->replacement);
- }
- string[n++] = lex_token_representation ();
- lex_get ();
+ string = lex_token_representation (lexer);
+ pool_register (pool, free, string);
+ add_replacement (string, e, pool, &used, &allocated);
- lex_match (',');
+ lex_get (lexer);
+ lex_match (lexer, ',');
}
- while (token != '/' && token != '.');
- e->replacement = xnrealloc (string, n, sizeof *e->replacement);
+ while (lex_token (lexer) != '/' && lex_token (lexer) != '.');
- return n;
+ return used;
}
\f
int
-cmd_end_repeat (void)
+cmd_end_repeat (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
{
msg (SE, _("No matching DO REPEAT."));
return CMD_CASCADING_FAILURE;
return NULL;
}
-/* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
+/* Makes appropriate DO REPEAT macro substitutions within the
+ repeated lines. */
static void
-do_repeat_filter (struct string *line, void *block_)
+do_repeat_filter (struct getl_interface *block_, struct string *line)
{
- struct repeat_block *block = block_;
+ struct repeat_block *block = (struct repeat_block *) block_;
bool in_apos, in_quote;
char *cp;
struct string output;
bool dot;
- ds_init (&output, ds_capacity (line));
+ ds_init_empty (&output);
/* Strip trailing whitespace, check for & remove terminal dot. */
while (isspace (ds_last (line)))
dot = ds_chomp (line, get_endcmd ());
in_apos = in_quote = false;
- for (cp = ds_c_str (line); cp < ds_end (line); )
+ for (cp = ds_cstr (line); cp < ds_end (line); )
{
if (*cp == '\'' && !in_quote)
in_apos = !in_apos;
in_quote = !in_quote;
if (in_quote || in_apos || !lex_is_id1 (*cp))
- ds_putc (&output, *cp++);
+ ds_put_char (&output, *cp++);
else
{
const char *start = cp;
const char *substitution = find_substitution (block,
start, end - start);
if (substitution != NULL)
- ds_puts (&output, substitution);
+ ds_put_cstr (&output, substitution);
else
- ds_concat (&output, start, end - start);
+ ds_put_substring (&output, ss_buffer (start, end - start));
cp = end;
}
}
if (dot)
- ds_putc (&output, get_endcmd ());
+ ds_put_char (&output, get_endcmd ());
ds_swap (line, &output);
ds_destroy (&output);
Puts the line in OUTPUT, sets the file name in *FILE_NAME and
line number in *LINE_NUMBER. Returns true if a line was
obtained, false if the source is exhausted. */
-static bool
-do_repeat_read (struct string *output, char **file_name, int *line_number,
- void *block_)
+static bool
+do_repeat_read (struct getl_interface *b, struct string *output)
{
- struct repeat_block *block = block_;
+ struct repeat_block *block = (struct repeat_block *) b;
struct line_list *line;
if (block->cur_line == NULL)
}
line = block->cur_line;
- ds_assign_c_str (output, line->line);
- *file_name = line->file_name;
- *line_number = -line->line_number;
+ ds_assign_cstr (output, line->line);
block->cur_line = line->next;
return true;
}
/* Frees a DO REPEAT block.
Called by getl to close out the DO REPEAT block. */
static void
-do_repeat_close (void *block_)
+do_repeat_close (struct getl_interface *block_)
{
- struct repeat_block *block = block_;
+ struct repeat_block *block = (struct repeat_block *) block_;
pool_destroy (block->pool);
}
+
+
+static bool
+always_false (const struct getl_interface *i UNUSED)
+{
+ return false;
+}