-/* 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 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 <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/cast.h>
+#include <libpspp/ll.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 "xalloc.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 line repeated by DO REPEAT. */
+struct repeat_line
{
- struct line_list *next; /* Next line. */
- char *file_name; /* File name. */
+ struct ll ll; /* In struct repeat_block line_list. */
+ const char *file_name; /* File name. */
int line_number; /* Line number. */
- char *line; /* Contents. */
+ struct substring text; /* Contents. */
};
/* The type of substitution made for a DO REPEAT macro. */
-enum repeat_entry_type
+enum repeat_macro_type
{
VAR_NAMES,
OTHER
};
/* Describes one DO REPEAT macro. */
-struct repeat_entry
+struct repeat_macro
{
- 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. */
+ struct ll ll; /* In struct repeat_block macros. */
+ enum repeat_macro_type type; /* Types of replacements. */
+ struct substring name; /* Macro name. */
+ struct substring *replacements; /* Macro replacement. */
};
/* A DO REPEAT...END REPEAT block. */
-struct repeat_block
+struct repeat_block
{
+ struct getl_interface parent;
+
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. */
+ struct dataset *ds; /* The dataset for this block */
+
+ struct ll_list lines; /* Lines in buffer. */
+ struct ll *cur_line; /* Last line output. */
int loop_cnt; /* Number of loops. */
int loop_idx; /* Number of loops so far. */
- struct repeat_entry *macros; /* Pointer to macro table. */
+
+ struct ll_list macros; /* Table of macros. */
+
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 struct repeat_macro *find_macro (struct repeat_block *,
+ struct substring name);
+
+static int parse_ids (struct lexer *, const struct dictionary *dict,
+ struct repeat_macro *, struct pool *);
-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);
+static int parse_numbers (struct lexer *, struct repeat_macro *,
+ struct pool *);
+
+static int parse_strings (struct lexer *, struct repeat_macro *,
+ 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 *);
+static const char *do_repeat_name (const struct getl_interface *);
+static int do_repeat_location (const struct getl_interface *);
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;
+ ll_init (&block->lines);
+ block->cur_line = ll_null (&block->lines);
+ block->loop_idx = 0;
+ ll_init (&block->macros);
- 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;
+ block->parent.name = do_repeat_name;
+ block->parent.location = do_repeat_location;
+
+ if (!ll_is_empty (&block->lines))
+ getl_include_source (lex_get_source_stream (lexer),
+ &block->parent,
+ lex_current_syntax_mode (lexer),
+ lex_current_error_mode (lexer)
+ );
+ else
+ pool_destroy (block->pool);
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 substring first_name;
block->loop_cnt = 0;
- block->macros = NULL;
do
{
- struct repeat_entry *e;
- struct repeat_entry *iter;
+ struct repeat_macro *macro;
+ 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);
- for (iter = block->macros; iter != NULL; iter = iter->next)
- if (!strcasecmp (iter->id, tokid))
+ lex_tokid (lexer), lex_tokid (lexer));
+ if (find_macro (block, ss_cstr (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;
}
- /* 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;
+ /* Make a new macro. */
+ macro = pool_alloc (block->pool, sizeof *macro);
+ ss_alloc_substring_pool (¯o->name, ss_cstr (lex_tokid (lexer)),
+ block->pool);
+ ll_push_tail (&block->macros, ¯o->ll);
/* 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, macro, block->pool);
+ else if (lex_is_number (lexer))
+ count = parse_numbers (lexer, macro, block->pool);
+ else if (lex_token (lexer) == T_STRING)
+ count = parse_strings (lexer, macro, 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
if (block->loop_cnt == 0)
{
block->loop_cnt = count;
- strcpy (first_name, e->id);
+ first_name = macro->name;
}
else if (block->loop_cnt != count)
{
- msg (SE, _("Dummy variable \"%s\" had %d "
- "substitutions, so \"%s\" must also, but %d "
+ msg (SE, _("Dummy variable \"%.*s\" had %d "
+ "substitutions, so \"%.*s\" must also, but %d "
"were specified."),
- first_name, block->loop_cnt, e->id, count);
+ (int) ss_length (first_name), ss_data (first_name),
+ block->loop_cnt,
+ (int) ss_length (macro->name), ss_data (macro->name),
+ count);
return false;
}
- lex_match ('/');
+ lex_match (lexer, '/');
}
- while (token != '.');
+ while (lex_token (lexer) != '.');
return true;
}
-/* 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)
+/* Finds and returns a DO REPEAT macro with the given NAME, or
+ NULL if there is none */
+static struct repeat_macro *
+find_macro (struct repeat_block *block, struct substring name)
{
- const char *end;
+ struct repeat_macro *macro;
- while (isspace ((unsigned char) *cp))
- cp++;
+ ll_for_each (macro, struct repeat_macro, ll, &block->macros)
+ if (ss_equals (macro->name, name))
+ return macro;
- end = lex_skip_identifier (cp);
- if (end != cp
- && lex_id_match_len (keyword, strlen (keyword), cp, end - cp))
- return end;
- else
- return NULL;
+ return NULL;
}
-/* Returns CP, advanced past a '+' or '-' if present. */
-static const char *
-skip_indentor (const char *cp)
+/* Advances LINE past white space and an identifier, if present.
+ Returns true if KEYWORD matches the identifer, false
+ otherwise. */
+static bool
+recognize_keyword (struct substring *line, const char *keyword)
{
- if (*cp == '+' || *cp == '-')
- cp++;
- return cp;
+ struct substring id;
+ ss_ltrim (line, ss_cstr (CC_SPACES));
+ ss_get_chars (line, lex_id_get_length (*line), &id);
+ return lex_id_match (ss_cstr (keyword), id);
}
/* Returns true if LINE contains a DO REPEAT command, false
otherwise. */
static bool
-recognize_do_repeat (const char *line)
+recognize_do_repeat (struct substring line)
{
- const char *cp = recognize_keyword (skip_indentor (line), "do");
- return cp != NULL && recognize_keyword (cp, "repeat") != NULL;
+ return (recognize_keyword (&line, "do")
+ && recognize_keyword (&line, "repeat"));
}
/* 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)
+recognize_end_repeat (struct substring line, bool *print)
{
- const char *cp = recognize_keyword (skip_indentor (line), "end");
- if (cp == NULL)
+ if (!recognize_keyword (&line, "end")
+ || !recognize_keyword (&line, "repeat"))
return false;
- cp = recognize_keyword (cp, "repeat");
- if (cp == NULL)
- return false;
-
- *print = recognize_keyword (cp, "print");
- return true;
+ *print = recognize_keyword (&line, "print");
+ 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;
int nesting_level;
previous_file_name = NULL;
- block->first_line = NULL;
- last_line = &block->first_line;
nesting_level = 0;
for (;;)
{
const char *cur_file_name;
- int cur_line_number;
- struct line_list *line;
- bool dot;
+ struct repeat_line *line;
+ struct string text;
+ bool command_ends_before_line, command_ends_after_line;
- if (!getl_read_line (NULL))
+ /* Retrieve an input line and make a copy of it. */
+ if (!lex_get_line_raw (lexer))
return false;
+ ds_init_string (&text, lex_entire_line_ds (lexer));
- /* 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))
+ /* Record file name. */
+ cur_file_name = getl_source_name (lex_get_source_stream (lexer));
+ if (cur_file_name != NULL &&
+ (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))
+ /* Create a line structure. */
+ line = pool_alloc (block->pool, sizeof *line);
+ line->file_name = previous_file_name;
+ line->line_number = getl_source_location (lex_get_source_stream (lexer));
+ ss_alloc_substring_pool (&line->text, ds_ss (&text), block->pool);
+
+
+ /* Check whether the line contains a DO REPEAT or END
+ REPEAT command. */
+ lex_preprocess_line (&text,
+ lex_current_syntax_mode (lexer),
+ &command_ends_before_line,
+ &command_ends_after_line);
+ if (recognize_do_repeat (ds_ss (&text)))
{
- if (nesting_level-- == 0)
- {
- lex_discard_line ();
- return true;
- }
+ if (settings_get_syntax () == COMPATIBLE)
+ msg (SE, _("DO REPEAT may not nest in compatibility mode."));
+ else
+ nesting_level++;
}
- 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;
- }
+ else if (recognize_end_repeat (ds_ss (&text), &block->print)
+ && nesting_level-- == 0)
+ {
+ lex_discard_line (lexer);
+ ds_destroy (&text);
+ return true;
+ }
+ ds_destroy (&text);
- lex_discard_line ();
- return true;
+ /* Add the line to the list. */
+ ll_push_tail (&block->lines, &line->ll);
+ }
}
/* 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)
+ struct repeat_macro *macro;
+
+ ll_for_each (macro, struct repeat_macro, ll, &block->macros)
+ if (macro->type == VAR_NAMES)
{
int i;
{
/* Ignore return value: if the variable already
exists there is no harm done. */
- dict_create_var (default_dict, iter->replacement[i], 0);
+ char *var_name = ss_xstrdup (macro->replacements[i]);
+ dict_create_var (dataset_dict (block->ds), var_name, 0);
+ free (var_name);
}
}
}
/* 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_macro *macro, struct pool *pool)
{
- size_t i;
- size_t n = 0;
-
- e->type = VAR_NAMES;
- e->replacement = NULL;
+ char **replacements;
+ size_t n, i;
- 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 != '.');
+ macro->type = VAR_NAMES;
+ if (!parse_mixed_vars_pool (lexer, dict, pool, &replacements, &n, PV_NONE))
+ return 0;
+ macro->replacements = pool_nalloc (pool, n, sizeof *macro->replacements);
+ for (i = 0; i < n; i++)
+ macro->replacements[i] = ss_cstr (replacements[i]);
return n;
}
-/* Stores VALUE into *REPL. */
-static inline void
-store_numeric (char **repl, long value)
+/* Adds REPLACEMENT to MACRO's list of replacements, which has
+ *USED elements and has room for *ALLOCATED. Allocates memory
+ from POOL. */
+static void
+add_replacement (struct substring replacement,
+ struct repeat_macro *macro, struct pool *pool,
+ size_t *used, size_t *allocated)
{
- *repl = xmalloc (INT_STRLEN_BOUND (value) + 1);
- sprintf (*repl, "%ld", value);
+ if (*used == *allocated)
+ macro->replacements = pool_2nrealloc (pool, macro->replacements, allocated,
+ sizeof *macro->replacements);
+ macro->replacements[(*used)++] = replacement;
}
-/* Parses a list of numbers for DO REPEAT. */
+/* Parses a list or range of numbers for DO REPEAT. */
static int
-parse_numbers (struct repeat_entry *e)
+parse_numbers (struct lexer *lexer, struct repeat_macro *macro,
+ struct pool *pool)
{
- /* First and last numbers for TO, plus the step factor. */
- long a, b;
-
- /* Alias to e->replacement. */
- char **array;
+ size_t used = 0;
+ size_t allocated = 0;
- /* Number of entries in array; maximum number for this allocation
- size. */
- int n, m;
-
- n = m = 0;
- e->type = OTHER;
- e->replacement = array = NULL;
+ macro->type = OTHER;
+ macro->replacements = NULL;
do
{
+ bool integer_value_seen;
+ double a, b, i;
+
/* Parse A TO B into a, b. */
- if (!lex_force_int ())
+ if (!lex_force_num (lexer))
return 0;
- a = lex_integer ();
-
- lex_get ();
- if (token == T_TO)
- {
- lex_get ();
- if (!lex_force_int ())
- return 0;
- b = lex_integer ();
- lex_get ();
- }
- else b = a;
+ if ( (integer_value_seen = lex_is_integer (lexer) ) )
+ a = lex_integer (lexer);
+ else
+ a = lex_number (lexer);
- if (n + (abs (b - a) + 1) > m)
+ lex_get (lexer);
+ if (lex_token (lexer) == T_TO)
{
- m = n + (abs (b - a) + 1) + 16;
- e->replacement = array = xnrealloc (array,
- m, sizeof *e->replacement);
+ if ( !integer_value_seen )
+ {
+ msg (SE, _("Ranges may only have integer bounds"));
+ return 0;
+ }
+ lex_get (lexer);
+ if (!lex_force_int (lexer))
+ return 0;
+ b = lex_integer (lexer);
+ if (b < a)
+ {
+ msg (SE, _("%g TO %g is an invalid range."), a, b);
+ return 0;
+ }
+ lex_get (lexer);
}
-
- 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);
- }
+ b = a;
+
+ for (i = a; i <= b; i++)
+ add_replacement (ss_cstr (pool_asprintf (pool, "%g", i)),
+ macro, pool, &used, &allocated);
- 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_macro *macro, 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;
+ macro->type = OTHER;
+ macro->replacements = 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 (ss_cstr (string), macro, 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;
}
\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)
+/* Finds a DO REPEAT macro with the given NAME and returns the
+ appropriate substitution if found, or NAME otherwise. */
+static struct substring
+find_substitution (struct repeat_block *block, struct substring name)
{
- 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;
+ struct repeat_macro *macro = find_macro (block, name);
+ return macro ? macro->replacements[block->loop_idx] : name;
}
-/* 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 *interface, struct string *line)
{
- struct repeat_block *block = block_;
- bool in_apos, in_quote;
- char *cp;
+ struct repeat_block *block
+ = UP_CAST (interface, struct repeat_block, parent);
+ bool in_apos, in_quote, dot;
+ struct substring input;
struct string output;
- bool dot;
+ int c;
- ds_init (&output, ds_capacity (line));
+ ds_init_empty (&output);
/* 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 ());
-
+ ds_rtrim (line, ss_cstr (CC_SPACES));
+ dot = ds_chomp (line, settings_get_endcmd ());
+ input = ds_ss (line);
in_apos = in_quote = false;
- for (cp = ds_c_str (line); cp < ds_end (line); )
+ while ((c = ss_first (input)) != EOF)
{
- if (*cp == '\'' && !in_quote)
+ if (c == '\'' && !in_quote)
in_apos = !in_apos;
- else if (*cp == '"' && !in_apos)
+ else if (c == '"' && !in_apos)
in_quote = !in_quote;
-
- if (in_quote || in_apos || !lex_is_id1 (*cp))
- ds_putc (&output, *cp++);
- else
+
+ if (in_quote || in_apos || !lex_is_id1 (c))
{
- 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;
+ ds_put_char (&output, c);
+ ss_advance (&input, 1);
+ }
+ else
+ {
+ struct substring id;
+ ss_get_chars (&input, lex_id_get_length (input), &id);
+ ds_put_substring (&output, find_substitution (block, id));
}
}
if (dot)
- ds_putc (&output, get_endcmd ());
+ ds_put_char (&output, settings_get_endcmd ());
ds_swap (line, &output);
ds_destroy (&output);
}
-/* 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. */
+static struct repeat_line *
+current_line (const struct getl_interface *interface)
+{
+ struct repeat_block *block
+ = UP_CAST (interface, struct repeat_block, parent);
+ return (block->cur_line != ll_null (&block->lines)
+ ? ll_data (block->cur_line, struct repeat_line, ll)
+ : NULL);
+}
+
+/* Function called by getl to read a line. Puts the line in
+ OUTPUT and its syntax mode in *SYNTAX. 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_)
+do_repeat_read (struct getl_interface *interface,
+ struct string *output)
{
- struct repeat_block *block = block_;
- struct line_list *line;
+ struct repeat_block *block
+ = UP_CAST (interface, struct repeat_block, parent);
+ struct repeat_line *line;
- if (block->cur_line == NULL)
+ block->cur_line = ll_next (block->cur_line);
+ if (block->cur_line == ll_null (&block->lines))
{
block->loop_idx++;
if (block->loop_idx >= block->loop_cnt)
return false;
- block->cur_line = block->first_line;
+
+ block->cur_line = ll_head (&block->lines);
}
- line = block->cur_line;
- ds_assign_c_str (output, line->line);
- *file_name = line->file_name;
- *line_number = -line->line_number;
- block->cur_line = line->next;
+ line = current_line (interface);
+ ds_assign_substring (output, line->text);
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 *interface)
{
- struct repeat_block *block = block_;
+ struct repeat_block *block
+ = UP_CAST (interface, struct repeat_block, parent);
pool_destroy (block->pool);
}
+
+
+static bool
+always_false (const struct getl_interface *i UNUSED)
+{
+ return false;
+}
+
+/* Returns the name of the source file from which the previous
+ line was originally obtained, or a null pointer if none. */
+static const char *
+do_repeat_name (const struct getl_interface *interface)
+{
+ struct repeat_line *line = current_line (interface);
+ return line ? line->file_name : NULL;
+}
+
+/* Returns the line number in the source file from which the
+ previous line was originally obtained, or -1 if none. */
+static int
+do_repeat_location (const struct getl_interface *interface)
+{
+ struct repeat_line *line = current_line (interface);
+ return line ? line->line_number : -1;
+}