X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Frepeat.c;h=f71409147cd5f167deb680a523f75b6ae7d811d7;hb=5fa244a7b0fcbd971e760cb67644514fe22aa4a3;hp=ede37016ee816a05820c6e97a97ff0bbe873ec43;hpb=43b1296aafe7582e7dbe6c2b6a8b478d7d9b0fcf;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index ede37016ee..f71409147c 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2007, 2009-2012 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 @@ -16,607 +16,403 @@ #include -#include "repeat.h" - -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "intprops.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/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/hash-functions.h" +#include "libpspp/hmap.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "libpspp/str.h" +#include "libpspp/misc.h" +#include "output/output-item.h" + +#include "gl/ftoastr.h" +#include "gl/minmax.h" +#include "gl/xalloc.h" +#include "gl/xmemdup0.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* A line repeated by DO REPEAT. */ -struct repeat_line - { - struct ll ll; /* In struct repeat_block line_list. */ - const char *file_name; /* File name. */ - int line_number; /* Line number. */ - struct substring text; /* Contents. */ - enum getl_syntax syntax; /* Syntax mode. */ - }; - -/* The type of substitution made for a DO REPEAT macro. */ -enum repeat_macro_type - { - VAR_NAMES, - OTHER - }; - -/* Describes one DO REPEAT macro. */ -struct repeat_macro - { - 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 dummy_var { - struct getl_interface parent; - - struct pool *pool; /* Pool used for storage. */ - 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 ll_list macros; /* Table of macros. */ - - bool print; /* Print lines as executed? */ + struct hmap_node hmap_node; + struct substring name; + char **values; + size_t n_values; + int start_ofs, end_ofs; }; -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 struct repeat_macro *find_macro (struct repeat_block *, - struct substring name); +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); -static int parse_ids (struct lexer *, const struct dictionary *dict, - struct repeat_macro *, struct pool *); - -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 *, enum getl_syntax); -static bool do_repeat_read (struct getl_interface *, - struct string *, enum getl_syntax *); -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 *); +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 *); int 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 (lexer, block) || !parse_lines (lexer, block)) - goto error; + struct hmap dummies = HMAP_INITIALIZER (dummies); + bool ok = parse_specification (lexer, dataset_dict (ds), &dummies); + ok = parse_commands (lexer, &dummies) && ok; + destroy_dummies (&dummies); - create_vars (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; + return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; +} - if (!ll_is_empty (&block->lines)) - getl_include_source (lex_get_source_stream (lexer), &block->parent); - else - pool_destroy (block->pool); +static const struct dummy_var * +find_dummy_var (struct hmap *hmap, struct substring name) +{ + const struct dummy_var *dv; - return CMD_SUCCESS; + HMAP_FOR_EACH_WITH_HASH (dv, struct dummy_var, hmap_node, + utf8_hash_case_substring (name, 0), hmap) + if (!utf8_sscasecmp (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 lexer *lexer, struct repeat_block *block) +parse_specification (struct lexer *lexer, struct dictionary *dict, + struct hmap *dummies) { - struct substring first_name; + struct dummy_var *first_dv = NULL; - block->loop_cnt = 0; do { - struct repeat_macro *macro; - struct dictionary *dict = dataset_dict (block->ds); - int count; + int start_ofs = lex_ofs (lexer); /* Get a stand-in variable name and make sure it's unique. */ if (!lex_force_id (lexer)) - return false; - if (dict_lookup_var (dict, lex_tokid (lexer))) - msg (SW, _("Dummy variable name \"%s\" hides dictionary " - "variable \"%s\"."), - lex_tokid (lexer), lex_tokid (lexer)); - if (find_macro (block, ss_cstr (lex_tokid (lexer)))) - { - msg (SE, _("Dummy variable name \"%s\" is given twice."), - lex_tokid (lexer)); - return false; - } + goto error; + struct substring name = lex_tokss (lexer); + if (dict_lookup_var (dict, name.string)) + lex_msg (lexer, SW, + _("Dummy variable name `%s' hides dictionary variable `%s'."), + name.string, name.string); + if (find_dummy_var (dummies, name)) + { + lex_error (lexer, _("Dummy variable name `%s' is given twice."), + name.string); + goto error; + } /* 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); + struct dummy_var *dv = xmalloc (sizeof *dv); + *dv = (struct dummy_var) { + .name = ss_clone (name), + .start_ofs = start_ofs, + }; + hmap_insert (dummies, &dv->hmap_node, utf8_hash_case_substring (name, 0)); /* Skip equals sign. */ lex_get (lexer); - if (!lex_force_match (lexer, '=')) - return false; + if (!lex_force_match (lexer, T_EQUALS)) + goto error; /* Get the details of the variable's possible values. */ - if (lex_token (lexer) == T_ID) - count = parse_ids (lexer, dict, macro, block->pool); + bool ok; + if (lex_token (lexer) == T_ID || lex_token (lexer) == T_ALL) + ok = parse_ids (lexer, dict, dv); 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); + ok = parse_numbers (lexer, dv); + else if (lex_is_string (lexer)) + ok = parse_strings (lexer, dv); else { - lex_error (lexer, NULL); - return false; + lex_error (lexer, _("Syntax error expecting substitution values.")); + goto error; } - if (count == 0) - return false; - if (lex_token (lexer) != '/' && lex_token (lexer) != '.') + if (!ok) + goto error; + assert (dv->n_values > 0); + if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD) { - lex_error (lexer, NULL); - return false; + lex_error (lexer, _("Syntax error expecting `/' or end of command.")); + goto error; } + dv->end_ofs = lex_ofs (lexer) - 1; - /* 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) + /* 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) { - block->loop_cnt = count; - first_name = macro->name; - } - else if (block->loop_cnt != count) - { - msg (SE, _("Dummy variable \"%.*s\" had %d " - "substitutions, so \"%.*s\" must also, but %d " - "were specified."), - (int) ss_length (first_name), ss_data (first_name), - block->loop_cnt, - (int) ss_length (macro->name), ss_data (macro->name), - count); - return false; + msg (SE, _("Each dummy variable must have the same number of " + "substitutions.")); + + lex_ofs_msg (lexer, SN, first_dv->start_ofs, first_dv->end_ofs, + ngettext ("Dummy variable %s had %zu substitution.", + "Dummy variable %s had %zu substitutions.", + first_dv->n_values), + first_dv->name.string, first_dv->n_values); + lex_ofs_msg (lexer, SN, dv->start_ofs, dv->end_ofs, + ngettext ("Dummy variable %s had %zu substitution.", + "Dummy variable %s had %zu substitutions.", + dv->n_values), + dv->name.string, dv->n_values); + goto error; } - lex_match (lexer, '/'); + lex_match (lexer, T_SLASH); } - while (lex_token (lexer) != '.'); + while (!lex_match (lexer, T_ENDCMD)); - return true; -} + while (lex_match (lexer, T_ENDCMD)) + continue; -/* 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) -{ - struct repeat_macro *macro; - - ll_for_each (macro, struct repeat_macro, ll, &block->macros) - if (ss_equals (macro->name, name)) - return macro; + return true; - return NULL; +error: + lex_discard_rest_of_command (lexer); + while (lex_match (lexer, T_ENDCMD)) + continue; + destroy_dummies (dummies); + hmap_init (dummies); + return false; } -/* 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) +static size_t +count_values (struct hmap *dummies) { - 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); + if (hmap_is_empty (dummies)) + return 0; + const struct dummy_var *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 (struct substring line) +static void +do_parse_commands (struct substring s, enum segmenter_mode mode, + struct hmap *dummies, + struct string *outputs, size_t n_outputs) { - return (recognize_keyword (&line, "do") - && recognize_keyword (&line, "repeat")); -} + struct segmenter segmenter = segmenter_init (mode, false); + while (!ss_is_empty (s)) + { + enum segment_type type; + int n = segmenter_push (&segmenter, s.string, s.length, true, &type); + assert (n >= 0); -/* 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 (struct substring line, bool *print) -{ - if (!recognize_keyword (&line, "end") - || !recognize_keyword (&line, "repeat")) - return false; + if (type == SEG_DO_REPEAT_COMMAND) + { + for (;;) + { + int k = segmenter_push (&segmenter, s.string + n, s.length - n, + true, &type); + if (type != SEG_NEWLINE && type != SEG_DO_REPEAT_COMMAND) + break; - *print = recognize_keyword (&line, "print"); - return true; + n += k; + } + + do_parse_commands (ss_head (s, n), mode, dummies, + outputs, n_outputs); + } + else if (type != SEG_END) + { + const struct dummy_var *dv + = (type == SEG_IDENTIFIER ? find_dummy_var (dummies, ss_head (s, n)) + : NULL); + for (size_t 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)); + } + + ss_advance (&s, n); + } } -/* Read all the lines we are going to substitute, inside the DO - REPEAT...END REPEAT block. */ static bool -parse_lines (struct lexer *lexer, struct repeat_block *block) +parse_commands (struct lexer *lexer, struct hmap *dummies) { - char *previous_file_name; - int nesting_level; + char *file_name = xstrdup_if_nonnull (lex_get_file_name (lexer)); + int line_number = lex_ofs_start_point (lexer, lex_ofs (lexer)).line; + + struct string input = DS_EMPTY_INITIALIZER; + while (lex_is_string (lexer)) + { + ds_put_substring (&input, lex_tokss (lexer)); + ds_put_byte (&input, '\n'); + lex_get (lexer); + } + + size_t n_values = count_values (dummies); + struct string *outputs = xmalloc (n_values * sizeof *outputs); + for (size_t i = 0; i < n_values; i++) + ds_init_empty (&outputs[i]); + + do_parse_commands (ds_ss (&input), lex_get_syntax_mode (lexer), + dummies, outputs, n_values); - previous_file_name = NULL; - nesting_level = 0; + ds_destroy (&input); - for (;;) + while (lex_match (lexer, T_ENDCMD)) + continue; + + bool ok = lex_match_phrase (lexer, "END REPEAT"); + if (!ok) + lex_error (lexer, _("Syntax error expecting END REPEAT.")); + bool print = ok && lex_match_id (lexer, "PRINT"); + lex_discard_rest_of_command (lexer); + + for (size_t i = 0; i < n_values; i++) { - const char *cur_file_name; - struct repeat_line *line; - struct string text; - enum getl_syntax syntax; - bool command_ends_before_line, command_ends_after_line; - - /* Retrieve an input line and make a copy of it. */ - if (!lex_get_line_raw (lexer, &syntax)) - return false; - ds_init_string (&text, lex_entire_line_ds (lexer)); - - /* 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); - - /* 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); - line->syntax = syntax; - - /* Check whether the line contains a DO REPEAT or END - REPEAT command. */ - lex_preprocess_line (&text, syntax, - &command_ends_before_line, - &command_ends_after_line); - if (recognize_do_repeat (ds_ss (&text))) - { - if (get_syntax () == COMPATIBLE) - msg (SE, _("DO REPEAT may not nest in compatibility mode.")); - else - nesting_level++; - } - else if (recognize_end_repeat (ds_ss (&text), &block->print) - && nesting_level-- == 0) + struct string *output = &outputs[n_values - i - 1]; + if (print) { - lex_discard_line (lexer); - ds_destroy (&text); - return true; + struct substring s = output->ss; + ss_chomp_byte (&s, '\n'); + char *label = xasprintf (_("Expansion %zu of %zu"), i + 1, n_values); + output_item_submit ( + text_item_create_nocopy (TEXT_ITEM_LOG, ss_xstrdup (s), label)); } - ds_destroy (&text); - - /* Add the line to the list. */ - ll_push_tail (&block->lines, &line->ll); + const char *encoding = lex_get_encoding (lexer); + struct lex_reader *reader = lex_reader_for_substring_nocopy (ds_ss (output), encoding); + lex_reader_set_file_name (reader, file_name); + reader->line_number = line_number; + lex_include (lexer, reader); } + free (file_name); + free (outputs); + + return ok; } -/* Creates variables for the given DO REPEAT. */ static void -create_vars (struct repeat_block *block) +destroy_dummies (struct hmap *dummies) { - struct repeat_macro *macro; - - ll_for_each (macro, struct repeat_macro, ll, &block->macros) - if (macro->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. */ - char *var_name = ss_xstrdup (macro->replacements[i]); - dict_create_var (dataset_dict (block->ds), var_name, 0); - free (var_name); - } - } + struct dummy_var *dv, *next; + + HMAP_FOR_EACH_SAFE (dv, next, struct dummy_var, hmap_node, dummies) + { + hmap_delete (dummies, &dv->hmap_node); + + ss_dealloc (&dv->name); + for (size_t i = 0; i < dv->n_values; i++) + free (dv->values[i]); + free (dv->values); + free (dv); + } + hmap_destroy (dummies); } /* Parses a set of ids for DO REPEAT. */ -static int +static bool parse_ids (struct lexer *lexer, const struct dictionary *dict, - struct repeat_macro *macro, struct pool *pool) + struct dummy_var *dv) { - char **replacements; - size_t n, i; - - 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; + return parse_mixed_vars (lexer, dict, &dv->values, &dv->n_values, PV_NONE); } /* 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) +add_replacement (struct dummy_var *dv, char *value, size_t *allocated) { - if (*used == *allocated) - macro->replacements = pool_2nrealloc (pool, macro->replacements, allocated, - sizeof *macro->replacements); - macro->replacements[(*used)++] = replacement; + if (dv->n_values == *allocated) + dv->values = x2nrealloc (dv->values, allocated, sizeof *dv->values); + dv->values[dv->n_values++] = value; } /* Parses a list or range of numbers for DO REPEAT. */ -static int -parse_numbers (struct lexer *lexer, struct repeat_macro *macro, - struct pool *pool) +static bool +parse_numbers (struct lexer *lexer, struct dummy_var *dv) { - size_t used = 0; size_t allocated = 0; - 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_num (lexer)) - return 0; - - if ( (integer_value_seen = lex_is_integer (lexer) ) ) - a = lex_integer (lexer); - else - a = lex_number (lexer); + return false; - lex_get (lexer); - if (lex_token (lexer) == T_TO) - { - if ( !integer_value_seen ) + if (lex_next_token (lexer, 1) == T_TO) + { + if (!lex_is_integer (lexer)) { - msg (SE, _("Ranges may only have integer bounds")); - return 0; + lex_error (lexer, _("Ranges may only have integer bounds.")); + return false; } - lex_get (lexer); - if (!lex_force_int (lexer)) - return 0; - b = lex_integer (lexer); + + long a = lex_integer (lexer); + lex_get (lexer); + lex_get (lexer); + + if (!lex_force_int_range (lexer, NULL, a, LONG_MAX)) + return false; + + long b = lex_integer (lexer); if (b < a) { - msg (SE, _("%g TO %g is an invalid range."), a, b); - return 0; + lex_next_error (lexer, -2, 0, + _("%ld TO %ld is an invalid range."), a, b); + return false; } lex_get (lexer); - } + + for (long i = a; i <= b; i++) + add_replacement (dv, xasprintf ("%ld", i), &allocated); + } else - b = a; + { + char s[DBL_BUFSIZE_BOUND]; - for (i = a; i <= b; i++) - add_replacement (ss_cstr (pool_asprintf (pool, "%g", i)), - macro, pool, &used, &allocated); + c_dtoastr (s, sizeof s, 0, 0, lex_number (lexer)); + add_replacement (dv, xstrdup (s), &allocated); + lex_get (lexer); + } - lex_match (lexer, ','); + lex_match (lexer, T_COMMA); } - while (lex_token (lexer) != '/' && lex_token (lexer) != '.'); + while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD); - return used; + return true; } /* Parses a list of strings for DO REPEAT. */ -int -parse_strings (struct lexer *lexer, struct repeat_macro *macro, struct pool *pool) +static bool +parse_strings (struct lexer *lexer, struct dummy_var *dv) { - size_t used = 0; size_t allocated = 0; - macro->type = OTHER; - macro->replacements = NULL; - do { - char *string; - - if (lex_token (lexer) != T_STRING) - { - msg (SE, _("String expected.")); - return 0; - } + if (!lex_force_string (lexer)) + return false; - string = lex_token_representation (lexer); - pool_register (pool, free, string); - add_replacement (ss_cstr (string), macro, pool, &used, &allocated); + add_replacement (dv, token_to_string (lex_next (lexer, 0)), &allocated); lex_get (lexer); - lex_match (lexer, ','); + lex_match (lexer, T_COMMA); } - while (lex_token (lexer) != '/' && lex_token (lexer) != '.'); + while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD); - return used; + return true; } int -cmd_end_repeat (struct lexer *lexer UNUSED, struct dataset *ds UNUSED) +cmd_end_repeat (struct lexer *lexer, struct dataset *ds UNUSED) { - msg (SE, _("No matching DO REPEAT.")); + lex_ofs_error (lexer, 0, 1, _("No matching %s."), "DO REPEAT"); return CMD_CASCADING_FAILURE; } - -/* 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_macro *macro = find_macro (block, name); - return macro ? macro->replacements[block->loop_idx] : name; -} - -/* Makes appropriate DO REPEAT macro substitutions within the - repeated lines. */ -static void -do_repeat_filter (struct getl_interface *block_, - struct string *line, enum getl_syntax syntax UNUSED) -{ - struct repeat_block *block = (struct repeat_block *) block_; - bool in_apos, in_quote, dot; - struct substring input; - struct string output; - int c; - - ds_init_empty (&output); - - /* Strip trailing whitespace, check for & remove terminal dot. */ - ds_rtrim (line, ss_cstr (CC_SPACES)); - dot = ds_chomp (line, get_endcmd ()); - - input = ds_ss (line); - in_apos = in_quote = false; - while ((c = ss_first (input)) != EOF) - { - if (c == '\'' && !in_quote) - in_apos = !in_apos; - else if (c == '"' && !in_apos) - in_quote = !in_quote; - - if (in_quote || in_apos || !lex_is_id1 (c)) - { - 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_put_char (&output, get_endcmd ()); - - ds_swap (line, &output); - ds_destroy (&output); -} - -static struct repeat_line * -current_line (const struct getl_interface *interface) -{ - struct repeat_block *block = (struct repeat_block *) interface; - 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 getl_interface *interface, - struct string *output, enum getl_syntax *syntax) -{ - struct repeat_block *block = (struct repeat_block *) interface; - struct repeat_line *line; - - 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 = ll_head (&block->lines); - } - - line = current_line (interface); - ds_assign_substring (output, line->text); - *syntax = line->syntax; - return true; -} - -/* Frees a DO REPEAT block. - Called by getl to close out the DO REPEAT block. */ -static void -do_repeat_close (struct getl_interface *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; -} - -/* 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; -}