-/* PSPP - computes sample statistics.
- Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 1997-9, 2000, 2007, 2009-2011 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#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 <language/lexer/lexer.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
-#include <data/settings.h>
-#include <libpspp/str.h>
-#include <data/variable.h>
+
+#include "data/dataset.h"
+#include "data/dictionary.h"
+#include "data/settings.h"
+#include "language/command.h"
+#include "language/lexer/lexer.h"
+#include "language/lexer/segment.h"
+#include "language/lexer/token.h"
+#include "language/lexer/variable-parser.h"
+#include "libpspp/cast.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/hmap.h"
+#include "libpspp/message.h"
+#include "libpspp/str.h"
+
+#include "gl/ftoastr.h"
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
-/* Defines a list of lines used by DO REPEAT. */
-struct line_list
+struct dummy_var
{
- struct line_list *next; /* Next line. */
- char *file_name; /* File name. */
- int line_number; /* Line number. */
- char *line; /* Contents. */
+ struct hmap_node hmap_node;
+ char *name;
+ char **values;
+ size_t n_values;
};
-/* The type of substitution made for a DO REPEAT macro. */
-enum repeat_entry_type
- {
- VAR_NAMES,
- OTHER
- };
+static bool parse_specification (struct lexer *, struct dictionary *,
+ struct hmap *dummies);
+static bool parse_commands (struct lexer *, struct hmap *dummies);
+static void destroy_dummies (struct hmap *dummies);
-/* Describes one DO REPEAT macro. */
-struct repeat_entry
- {
- struct repeat_entry *next; /* Next entry. */
- enum repeat_entry_type type; /* Types of replacements. */
- char id[LONG_NAME_LEN + 1]; /* Macro identifier. */
- char **replacement; /* Macro replacement. */
- };
+static bool parse_ids (struct lexer *, const struct dictionary *,
+ struct dummy_var *);
+static bool parse_numbers (struct lexer *, struct dummy_var *);
+static bool parse_strings (struct lexer *, struct dummy_var *);
-/* A DO REPEAT...END REPEAT block. */
-struct repeat_block
- {
- struct pool *pool; /* Pool used for storage. */
- 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. */
- int loop_idx; /* Number of loops so far. */
- struct repeat_entry *macros; /* Pointer to macro table. */
- bool print; /* Print lines as executed? */
- };
+int
+cmd_do_repeat (struct lexer *lexer, struct dataset *ds)
+{
+ struct hmap dummies;
+ bool ok;
-static bool parse_specification (struct repeat_block *);
-static bool parse_lines (struct repeat_block *);
-static void create_vars (struct repeat_block *);
+ if (!parse_specification (lexer, dataset_dict (ds), &dummies))
+ return CMD_CASCADING_FAILURE;
-static int parse_ids (struct repeat_entry *);
-static int parse_numbers (struct repeat_entry *);
-static int parse_strings (struct repeat_entry *);
+ ok = parse_commands (lexer, &dummies);
-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);
+ destroy_dummies (&dummies);
-int
-cmd_do_repeat (void)
-{
- struct repeat_block *block;
+ return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+}
- block = pool_create_container (struct repeat_block, pool);
+static unsigned int
+hash_dummy (const char *name, size_t name_len)
+{
+ return hash_case_bytes (name, name_len, 0);
+}
- if (!parse_specification (block) || !parse_lines (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);
+static const struct dummy_var *
+find_dummy_var (struct hmap *hmap, const char *name, size_t name_len)
+{
+ const struct dummy_var *dv;
- return CMD_SUCCESS;
+ HMAP_FOR_EACH_WITH_HASH (dv, struct dummy_var, hmap_node,
+ hash_dummy (name, name_len), hmap)
+ if (strcasecmp (dv->name, name))
+ return dv;
- error:
- pool_destroy (block->pool);
- return CMD_CASCADING_FAILURE;
+ return NULL;
}
/* Parses the whole DO REPEAT command specification.
Returns success. */
static bool
-parse_specification (struct repeat_block *block)
+parse_specification (struct lexer *lexer, struct dictionary *dict,
+ struct hmap *dummies)
{
- char first_name[LONG_NAME_LEN + 1];
+ struct dummy_var *first_dv = NULL;
- block->loop_cnt = 0;
- block->macros = NULL;
+ hmap_init (dummies);
do
{
- struct repeat_entry *e;
- struct repeat_entry *iter;
- int count;
+ struct dummy_var *dv;
+ const char *name;
+ bool ok;
/* Get a stand-in variable name and make sure it's unique. */
- if (!lex_force_id ())
- return false;
- if (dict_lookup_var (default_dict, tokid))
- msg (SW, _("Dummy variable name \"%s\" hides dictionary "
- "variable \"%s\"."),
- tokid, tokid);
- for (iter = block->macros; iter != NULL; iter = iter->next)
- if (!strcasecmp (iter->id, tokid))
- {
- msg (SE, _("Dummy variable name \"%s\" is given twice."), tokid);
- return false;
- }
-
- /* Make a new stand-in variable entry and link it into the
- list. */
- e = pool_alloc (block->pool, sizeof *e);
- e->next = block->macros;
- strcpy (e->id, tokid);
- block->macros = e;
+ if (!lex_force_id (lexer))
+ goto error;
+ name = lex_tokcstr (lexer);
+ if (dict_lookup_var (dict, name))
+ msg (SW, _("Dummy variable name `%s' hides dictionary variable `%s'."),
+ name, name);
+ if (find_dummy_var (dummies, name, strlen (name)))
+ {
+ msg (SE, _("Dummy variable name `%s' is given twice."), name);
+ goto error;
+ }
+
+ /* Make a new macro. */
+ dv = xmalloc (sizeof *dv);
+ dv->name = xstrdup (name);
+ dv->values = NULL;
+ dv->n_values = 0;
+ hmap_insert (dummies, &dv->hmap_node, hash_dummy (name, strlen (name)));
/* Skip equals sign. */
- lex_get ();
- if (!lex_force_match ('='))
- return false;
+ lex_get (lexer);
+ if (!lex_force_match (lexer, T_EQUALS))
+ goto error;
/* 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 || lex_token (lexer) == T_ALL)
+ ok = parse_ids (lexer, dict, dv);
+ else if (lex_is_number (lexer))
+ ok = parse_numbers (lexer, dv);
+ else if (lex_is_string (lexer))
+ ok = parse_strings (lexer, dv);
else
{
- lex_error (NULL);
- return false;
+ lex_error (lexer, NULL);
+ goto error;
}
- if (count == 0)
- return false;
+ if (!ok)
+ goto error;
+ assert (dv->n_values > 0);
+ if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
- /* If this is the first variable then it defines how many
- replacements there must be; otherwise enforce this number of
- replacements. */
- if (block->loop_cnt == 0)
- {
- block->loop_cnt = count;
- strcpy (first_name, e->id);
- }
- else if (block->loop_cnt != count)
+ /* If this is the first variable then it defines how many replacements
+ there must be; otherwise enforce this number of replacements. */
+ if (first_dv == NULL)
+ first_dv = dv;
+ else if (first_dv->n_values != dv->n_values)
{
- msg (SE, _("Dummy variable \"%s\" had %d "
- "substitutions, so \"%s\" must also, but %d "
- "were specified."),
- first_name, block->loop_cnt, e->id, count);
- return false;
+ msg (SE, _("Dummy variable `%s' had %zu substitutions, so `%s' must "
+ "also, but %zu were specified."),
+ first_dv->name, first_dv->n_values,
+ dv->name, dv->n_values);
+ goto error;
}
- lex_match ('/');
+ lex_match (lexer, T_SLASH);
}
- while (token != '.');
+ while (!lex_match (lexer, T_ENDCMD));
- return true;
-}
+ while (lex_match (lexer, T_ENDCMD))
+ continue;
-/* If KEYWORD appears beginning at CP, possibly preceded by white
- space, returns a pointer to the character just after the
- keyword. Otherwise, returns a null pointer. */
-static const char *
-recognize_keyword (const char *cp, const char *keyword)
-{
- const char *end;
-
- while (isspace ((unsigned char) *cp))
- cp++;
+ return true;
- end = lex_skip_identifier (cp);
- if (end != cp
- && lex_id_match_len (keyword, strlen (keyword), cp, end - cp))
- return end;
- else
- return NULL;
+error:
+ destroy_dummies (dummies);
+ return false;
}
-/* Returns CP, advanced past a '+' or '-' if present. */
-static const char *
-skip_indentor (const char *cp)
+static size_t
+count_values (struct hmap *dummies)
{
- if (*cp == '+' || *cp == '-')
- cp++;
- return cp;
+ const struct dummy_var *dv;
+ dv = HMAP_FIRST (struct dummy_var, hmap_node, dummies);
+ return dv->n_values;
}
-/* Returns true if LINE contains a DO REPEAT command, false
- otherwise. */
-static bool
-recognize_do_repeat (const char *line)
+static void
+do_parse_commands (struct substring s, enum lex_syntax_mode syntax_mode,
+ struct hmap *dummies,
+ struct string *outputs, size_t n_outputs)
{
- const char *cp = recognize_keyword (skip_indentor (line), "do");
- return cp != NULL && recognize_keyword (cp, "repeat") != NULL;
-}
+ struct segmenter segmenter;
-/* Returns true if LINE contains an END REPEAT command, false
- otherwise. Sets *PRINT to true for END REPEAT PRINT, false
- otherwise. */
-static bool
-recognize_end_repeat (const char *line, bool *print)
-{
- const char *cp = recognize_keyword (skip_indentor (line), "end");
- if (cp == NULL)
- return false;
+ segmenter_init (&segmenter, syntax_mode);
- cp = recognize_keyword (cp, "repeat");
- if (cp == NULL)
- return false;
+ while (!ss_is_empty (s))
+ {
+ enum segment_type type;
+ int n;
- *print = recognize_keyword (cp, "print");
- return true;
-}
+ n = segmenter_push (&segmenter, s.string, s.length, &type);
+ assert (n >= 0);
-/* Read all the lines we are going to substitute, inside the DO
- REPEAT...END REPEAT block. */
-static bool
-parse_lines (struct repeat_block *block)
-{
- char *previous_file_name;
- struct line_list **last_line;
- int nesting_level;
+ if (type == SEG_DO_REPEAT_COMMAND)
+ {
+ for (;;)
+ {
+ int k;
- previous_file_name = NULL;
- block->first_line = NULL;
- last_line = &block->first_line;
- nesting_level = 0;
+ k = segmenter_push (&segmenter, s.string + n, s.length - n,
+ &type);
+ if (type != SEG_NEWLINE && type != SEG_DO_REPEAT_COMMAND)
+ break;
- for (;;)
- {
- const char *cur_file_name;
- int cur_line_number;
- struct line_list *line;
- bool dot;
-
- if (!getl_read_line (NULL))
- 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
- || !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)))
- nesting_level++;
- else if (recognize_end_repeat (ds_c_str (&getl_buf), &block->print))
+ n += k;
+ }
+
+ do_parse_commands (ss_head (s, n), syntax_mode, dummies,
+ outputs, n_outputs);
+ }
+ else if (type != SEG_END)
{
- if (nesting_level-- == 0)
- {
- lex_discard_line ();
- return true;
- }
+ const struct dummy_var *dv;
+ size_t i;
+
+ dv = (type == SEG_IDENTIFIER
+ ? find_dummy_var (dummies, s.string, n)
+ : NULL);
+ for (i = 0; i < n_outputs; i++)
+ if (dv != NULL)
+ ds_put_cstr (&outputs[i], dv->values[i]);
+ else
+ ds_put_substring (&outputs[i], ss_head (s, n));
}
- if (dot)
- ds_putc (&getl_buf, 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));
- last_line = &line->next;
- }
-
- lex_discard_line ();
- return true;
-}
-/* Creates variables for the given DO REPEAT. */
-static void
-create_vars (struct repeat_block *block)
-{
- struct repeat_entry *iter;
-
- for (iter = block->macros; iter; iter = iter->next)
- if (iter->type == VAR_NAMES)
- {
- int i;
-
- for (i = 0; i < block->loop_cnt; i++)
- {
- /* Ignore return value: if the variable already
- exists there is no harm done. */
- dict_create_var (default_dict, iter->replacement[i], 0);
- }
- }
+ ss_advance (&s, n);
+ }
}
-/* Parses a set of ids for DO REPEAT. */
-static int
-parse_ids (struct repeat_entry *e)
+static bool
+parse_commands (struct lexer *lexer, struct hmap *dummies)
{
+ struct string *outputs;
+ struct string input;
+ size_t input_len;
+ size_t n_values;
+ char *file_name;
+ int line_number;
+ bool ok;
size_t i;
- size_t n = 0;
- e->type = VAR_NAMES;
- e->replacement = NULL;
+ if (lex_get_file_name (lexer) != NULL)
+ file_name = xstrdup (lex_get_file_name (lexer));
+ else
+ file_name = NULL;
+ line_number = lex_get_first_line_number (lexer, 0);
- do
+ ds_init_empty (&input);
+ while (lex_is_string (lexer))
{
- 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;
+ ds_put_substring (&input, lex_tokss (lexer));
+ ds_put_byte (&input, '\n');
+ lex_get (lexer);
}
- while (token != '/' && token != '.');
+ if (ds_is_empty (&input))
+ ds_put_byte (&input, '\n');
+ ds_put_byte (&input, '\0');
+ input_len = ds_length (&input);
- return n;
-}
+ n_values = count_values (dummies);
+ outputs = xmalloc (n_values * sizeof *outputs);
+ for (i = 0; i < n_values; i++)
+ ds_init_empty (&outputs[i]);
-/* Stores VALUE into *REPL. */
-static inline void
-store_numeric (char **repl, long value)
-{
- *repl = xmalloc (INT_STRLEN_BOUND (value) + 1);
- sprintf (*repl, "%ld", value);
-}
+ do_parse_commands (ds_ss (&input), lex_get_syntax_mode (lexer),
+ dummies, outputs, n_values);
-/* Parses a list of numbers for DO REPEAT. */
-static int
-parse_numbers (struct repeat_entry *e)
-{
- /* First and last numbers for TO, plus the step factor. */
- long a, b;
+ ds_destroy (&input);
- /* Alias to e->replacement. */
- char **array;
+ while (lex_match (lexer, T_ENDCMD))
+ continue;
- /* Number of entries in array; maximum number for this allocation
- size. */
- int n, m;
+ ok = (lex_force_match_id (lexer, "END")
+ && lex_force_match_id (lexer, "REPEAT"));
+ if (ok)
+ lex_match_id (lexer, "PRINT"); /* XXX */
- n = m = 0;
- e->type = OTHER;
- e->replacement = array = NULL;
+ lex_discard_rest_of_command (lexer);
- do
+ for (i = 0; i < n_values; i++)
{
- /* Parse A TO B into a, b. */
- if (!lex_force_int ())
- return 0;
- a = lex_integer ();
+ struct string *output = &outputs[n_values - i - 1];
+ struct lex_reader *reader;
- lex_get ();
- if (token == T_TO)
- {
- lex_get ();
- if (!lex_force_int ())
- return 0;
- b = lex_integer ();
-
- lex_get ();
- }
- 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);
- }
-
- 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 (',');
+ reader = lex_reader_for_substring_nocopy (ds_ss (output));
+ lex_reader_set_file_name (reader, file_name);
+ reader->line_number = line_number;
+ lex_include (lexer, reader);
}
- while (token != '/' && token != '.');
- e->replacement = xrealloc (array, n * sizeof *e->replacement);
+ free (file_name);
- return n;
+ return ok;
}
-/* Parses a list of strings for DO REPEAT. */
-int
-parse_strings (struct repeat_entry *e)
+static void
+destroy_dummies (struct hmap *dummies)
{
- char **string;
- int n, m;
-
- e->type = OTHER;
- string = e->replacement = NULL;
- n = m = 0;
+ struct dummy_var *dv, *next;
- do
+ HMAP_FOR_EACH_SAFE (dv, next, struct dummy_var, hmap_node, dummies)
{
- if (token != T_STRING)
- {
- int i;
- msg (SE, _("String expected."));
- for (i = 0; i < n; i++)
- free (string[i]);
- free (string);
- return 0;
- }
+ size_t i;
- if (n + 1 > m)
- {
- m += 16;
- e->replacement = string = xnrealloc (string,
- m, sizeof *e->replacement);
- }
- string[n++] = lex_token_representation ();
- lex_get ();
+ hmap_delete (dummies, &dv->hmap_node);
- lex_match (',');
+ free (dv->name);
+ for (i = 0; i < dv->n_values; i++)
+ free (dv->values[i]);
+ free (dv->values);
+ free (dv);
}
- while (token != '/' && token != '.');
- e->replacement = xnrealloc (string, n, sizeof *e->replacement);
-
- return n;
-}
-\f
-int
-cmd_end_repeat (void)
-{
- msg (SE, _("No matching DO REPEAT."));
- return CMD_CASCADING_FAILURE;
+ hmap_destroy (dummies);
}
-\f
-/* Finds a DO REPEAT macro with name MACRO_NAME and returns the
- appropriate subsitution if found, or NULL if not. */
-static char *
-find_substitution (struct repeat_block *block, const char *name, size_t length)
-{
- struct repeat_entry *e;
- for (e = block->macros; e; e = e->next)
- if (!memcasecmp (e->id, name, length) && strlen (e->id) == length)
- return e->replacement[block->loop_idx];
-
- return NULL;
+/* Parses a set of ids for DO REPEAT. */
+static bool
+parse_ids (struct lexer *lexer, const struct dictionary *dict,
+ struct dummy_var *dv)
+{
+ return parse_mixed_vars (lexer, dict, &dv->values, &dv->n_values, PV_NONE);
}
-/* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
+/* Adds REPLACEMENT to MACRO's list of replacements, which has
+ *USED elements and has room for *ALLOCATED. Allocates memory
+ from POOL. */
static void
-do_repeat_filter (struct string *line, void *block_)
+add_replacement (struct dummy_var *dv, char *value, size_t *allocated)
{
- struct repeat_block *block = block_;
- bool in_apos, in_quote;
- char *cp;
- struct string output;
- bool dot;
-
- ds_init (&output, ds_capacity (line));
+ if (dv->n_values == *allocated)
+ dv->values = x2nrealloc (dv->values, allocated, sizeof *dv->values);
+ dv->values[dv->n_values++] = value;
+}
- /* Strip trailing whitespace, check for & remove terminal dot. */
- while (isspace (ds_last (line)))
- ds_truncate (line, ds_length (line) - 1);
- dot = ds_chomp (line, get_endcmd ());
+/* Parses a list or range of numbers for DO REPEAT. */
+static bool
+parse_numbers (struct lexer *lexer, struct dummy_var *dv)
+{
+ size_t allocated = 0;
- in_apos = in_quote = false;
- for (cp = ds_c_str (line); cp < ds_end (line); )
+ do
{
- if (*cp == '\'' && !in_quote)
- in_apos = !in_apos;
- else if (*cp == '"' && !in_apos)
- in_quote = !in_quote;
-
- if (in_quote || in_apos || !lex_is_id1 (*cp))
- ds_putc (&output, *cp++);
- else
+ if (!lex_force_num (lexer))
+ return false;
+
+ if (lex_next_token (lexer, 1) == T_TO)
{
- const char *start = cp;
- char *end = lex_skip_identifier (start);
- const char *substitution = find_substitution (block,
- start, end - start);
- if (substitution != NULL)
- ds_puts (&output, substitution);
- else
- ds_concat (&output, start, end - start);
- cp = end;
+ long int a, b;
+ long int i;
+
+ if (!lex_is_integer (lexer))
+ {
+ msg (SE, _("Ranges may only have integer bounds."));
+ return false;
+ }
+
+ a = lex_integer (lexer);
+ lex_get (lexer);
+ lex_get (lexer);
+
+ if (!lex_force_int (lexer))
+ return false;
+
+ b = lex_integer (lexer);
+ if (b < a)
+ {
+ msg (SE, _("%ld TO %ld is an invalid range."), a, b);
+ return false;
+ }
+ lex_get (lexer);
+
+ for (i = a; i <= b; i++)
+ add_replacement (dv, xasprintf ("%ld", i), &allocated);
+ }
+ else
+ {
+ char s[DBL_BUFSIZE_BOUND];
+
+ dtoastr (s, sizeof s, 0, 0, lex_number (lexer));
+ add_replacement (dv, xstrdup (s), &allocated);
+ lex_get (lexer);
}
+
+ lex_match (lexer, T_COMMA);
}
- if (dot)
- ds_putc (&output, get_endcmd ());
+ while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD);
- ds_swap (line, &output);
- ds_destroy (&output);
+ return true;
}
-/* Function called by getl to read a line.
- 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. */
+/* Parses a list of strings for DO REPEAT. */
static bool
-do_repeat_read (struct string *output, char **file_name, int *line_number,
- void *block_)
+parse_strings (struct lexer *lexer, struct dummy_var *dv)
{
- struct repeat_block *block = block_;
- struct line_list *line;
+ size_t allocated = 0;
- if (block->cur_line == NULL)
+ do
{
- block->loop_idx++;
- if (block->loop_idx >= block->loop_cnt)
- return false;
- block->cur_line = block->first_line;
+ if (!lex_force_string (lexer))
+ {
+ msg (SE, _("String expected."));
+ return false;
+ }
+
+ add_replacement (dv, token_to_string (lex_next (lexer, 0)), &allocated);
+
+ lex_get (lexer);
+ lex_match (lexer, T_COMMA);
}
- line = block->cur_line;
+ while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD);
- ds_assign_c_str (output, line->line);
- *file_name = line->file_name;
- *line_number = -line->line_number;
- 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_)
+\f
+int
+cmd_end_repeat (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
{
- struct repeat_block *block = block_;
- pool_destroy (block->pool);
+ msg (SE, _("No matching DO REPEAT."));
+ return CMD_CASCADING_FAILURE;
}