X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Frepeat.c;h=1ab3b9c2ff7f49b4d392655aa336bd9087e0cab1;hb=774441e68b4d2e3a4b5c6975e9614dcd4369955e;hp=0a7da0a6cb3237d95b2ac6f3758af60d75a3214e;hpb=65e61cc92b48297625bc71cf31b8a19e301eb6c1;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index 0a7da0a6cb..1ab3b9c2ff 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -1,21 +1,18 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* 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 . */ #include @@ -32,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -41,6 +38,7 @@ #include #include "intprops.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -52,11 +50,10 @@ struct repeat_line 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 +enum repeat_macro_type { VAR_NAMES, OTHER @@ -72,7 +69,7 @@ struct repeat_macro }; /* A DO REPEAT...END REPEAT block. */ -struct repeat_block +struct repeat_block { struct getl_interface parent; @@ -96,19 +93,19 @@ static void create_vars (struct repeat_block *); static struct repeat_macro *find_macro (struct repeat_block *, struct substring name); -static int parse_ids (struct lexer *, const struct dictionary *dict, +static int parse_ids (struct lexer *, const struct dictionary *dict, struct repeat_macro *, struct pool *); -static int parse_numbers (struct lexer *, struct repeat_macro *, +static int parse_numbers (struct lexer *, struct repeat_macro *, struct pool *); -static int parse_strings (struct lexer *, struct repeat_macro *, +static int parse_strings (struct lexer *, struct repeat_macro *, struct pool *); static void do_repeat_filter (struct getl_interface *, - struct string *, enum getl_syntax); + struct string *); static bool do_repeat_read (struct getl_interface *, - struct string *, enum getl_syntax *); + 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 *); @@ -128,7 +125,7 @@ cmd_do_repeat (struct lexer *lexer, struct dataset *ds) if (!parse_specification (lexer, block) || !parse_lines (lexer, block)) goto error; - + create_vars (block); block->parent.read = do_repeat_read; @@ -139,7 +136,11 @@ cmd_do_repeat (struct lexer *lexer, struct dataset *ds) block->parent.location = do_repeat_location; if (!ll_is_empty (&block->lines)) - getl_include_source (lex_get_source_stream (lexer), &block->parent); + 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); @@ -153,7 +154,7 @@ cmd_do_repeat (struct lexer *lexer, struct dataset *ds) /* 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 repeat_block *block) { struct substring first_name; @@ -168,12 +169,12 @@ parse_specification (struct lexer *lexer, struct repeat_block *block) 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\"."), + 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."), + msg (SE, _("Dummy variable name `%s' is given twice."), lex_tokid (lexer)); return false; } @@ -194,7 +195,7 @@ parse_specification (struct lexer *lexer, struct repeat_block *block) 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) + else if (lex_is_string (lexer)) count = parse_strings (lexer, macro, block->pool); else { @@ -203,7 +204,7 @@ parse_specification (struct lexer *lexer, struct repeat_block *block) } if (count == 0) return false; - if (lex_token (lexer) != '/' && lex_token (lexer) != '.') + if (lex_token (lexer) != '/' && lex_token (lexer) != '.') { lex_error (lexer, NULL); return false; @@ -219,8 +220,8 @@ parse_specification (struct lexer *lexer, struct repeat_block *block) } 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."), (int) ss_length (first_name), ss_data (first_name), block->loop_cnt, @@ -242,7 +243,7 @@ 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; @@ -282,13 +283,13 @@ recognize_end_repeat (struct substring line, bool *print) return false; *print = recognize_keyword (&line, "print"); - return true; + return true; } /* 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_lines (struct lexer *lexer, struct repeat_block *block) { char *previous_file_name; int nesting_level; @@ -301,17 +302,16 @@ parse_lines (struct lexer *lexer, struct repeat_block *block) 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)) + if (!lex_get_line_raw (lexer)) 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 && + 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); @@ -321,19 +321,26 @@ parse_lines (struct lexer *lexer, struct repeat_block *block) 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, + lex_preprocess_line (&text, + lex_current_syntax_mode (lexer), &command_ends_before_line, &command_ends_after_line); if (recognize_do_repeat (ds_ss (&text))) - nesting_level++; + { + if (settings_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) + && nesting_level-- == 0) { lex_discard_line (lexer); + ds_destroy (&text); return true; } ds_destroy (&text); @@ -348,7 +355,7 @@ static void create_vars (struct repeat_block *block) { struct repeat_macro *macro; - + ll_for_each (macro, struct repeat_macro, ll, &block->macros) if (macro->type == VAR_NAMES) { @@ -367,12 +374,12 @@ create_vars (struct repeat_block *block) /* Parses a set of ids for DO REPEAT. */ static int -parse_ids (struct lexer *lexer, const struct dictionary *dict, +parse_ids (struct lexer *lexer, const struct dictionary *dict, struct repeat_macro *macro, struct pool *pool) { char **replacements; size_t n, i; - + macro->type = VAR_NAMES; if (!parse_mixed_vars_pool (lexer, dict, pool, &replacements, &n, PV_NONE)) return 0; @@ -389,7 +396,7 @@ parse_ids (struct lexer *lexer, const struct dictionary *dict, static void add_replacement (struct substring replacement, struct repeat_macro *macro, struct pool *pool, - size_t *used, size_t *allocated) + size_t *used, size_t *allocated) { if (*used == *allocated) macro->replacements = pool_2nrealloc (pool, macro->replacements, allocated, @@ -397,35 +404,46 @@ add_replacement (struct substring replacement, 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 lexer *lexer, struct repeat_macro *macro, struct pool *pool) +parse_numbers (struct lexer *lexer, struct repeat_macro *macro, + struct pool *pool) { size_t used = 0; size_t allocated = 0; - + macro->type = OTHER; macro->replacements = NULL; do { - long a, b, i; + bool integer_value_seen; + double a, b, i; /* Parse A TO B into a, b. */ - if (!lex_force_int (lexer)) + if (!lex_force_num (lexer)) return 0; - a = lex_integer (lexer); + + if ( (integer_value_seen = lex_is_integer (lexer) ) ) + a = lex_integer (lexer); + else + a = lex_number (lexer); lex_get (lexer); if (lex_token (lexer) == T_TO) { + 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) + if (b < a) { - msg (SE, _("%ld TO %ld is an invalid range."), a, b); + msg (SE, _("%g TO %g is an invalid range."), a, b); return 0; } lex_get (lexer); @@ -434,7 +452,7 @@ parse_numbers (struct lexer *lexer, struct repeat_macro *macro, struct pool *poo b = a; for (i = a; i <= b; i++) - add_replacement (ss_cstr (pool_asprintf (pool, "%ld", i)), + add_replacement (ss_cstr (pool_asprintf (pool, "%g", i)), macro, pool, &used, &allocated); lex_match (lexer, ','); @@ -450,15 +468,15 @@ parse_strings (struct lexer *lexer, struct repeat_macro *macro, struct pool *poo { size_t used = 0; size_t allocated = 0; - + macro->type = OTHER; macro->replacements = NULL; do { char *string; - - if (lex_token (lexer) != T_STRING) + + if (!lex_force_string (lexer)) { msg (SE, _("String expected.")); return 0; @@ -492,13 +510,13 @@ find_substitution (struct repeat_block *block, struct substring name) return macro ? macro->replacements[block->loop_idx] : name; } -/* Makes appropriate DO REPEAT macro substitutions within the +/* 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) +do_repeat_filter (struct getl_interface *interface, struct string *line) { - struct repeat_block *block = (struct repeat_block *) block_; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); bool in_apos, in_quote, dot; struct substring input; struct string output; @@ -508,8 +526,7 @@ do_repeat_filter (struct getl_interface *block_, /* Strip trailing whitespace, check for & remove terminal dot. */ ds_rtrim (line, ss_cstr (CC_SPACES)); - dot = ds_chomp (line, get_endcmd ()); - + dot = ds_chomp (line, settings_get_endcmd ()); input = ds_ss (line); in_apos = in_quote = false; while ((c = ss_first (input)) != EOF) @@ -518,13 +535,13 @@ do_repeat_filter (struct getl_interface *block_, in_apos = !in_apos; else if (c == '"' && !in_apos) in_quote = !in_quote; - - if (in_quote || in_apos || !lex_is_id1 (c)) + + if (in_quote || in_apos || !lex_is_id1 (c)) { ds_put_char (&output, c); - ss_advance (&input, 1); + ss_advance (&input, 1); } - else + else { struct substring id; ss_get_chars (&input, lex_id_get_length (input), &id); @@ -532,16 +549,17 @@ do_repeat_filter (struct getl_interface *block_, } } if (dot) - ds_put_char (&output, get_endcmd ()); + ds_put_char (&output, settings_get_endcmd ()); ds_swap (line, &output); ds_destroy (&output); } static struct repeat_line * -current_line (const struct getl_interface *interface) +current_line (const struct getl_interface *interface) { - struct repeat_block *block = (struct repeat_block *) 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); @@ -550,15 +568,16 @@ current_line (const struct getl_interface *interface) /* 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 +static bool do_repeat_read (struct getl_interface *interface, - struct string *output, enum getl_syntax *syntax) + struct string *output) { - struct repeat_block *block = (struct repeat_block *) interface; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); struct repeat_line *line; block->cur_line = ll_next (block->cur_line); - if (block->cur_line == ll_null (&block->lines)) + if (block->cur_line == ll_null (&block->lines)) { block->loop_idx++; if (block->loop_idx >= block->loop_cnt) @@ -569,21 +588,21 @@ do_repeat_read (struct getl_interface *interface, 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_) +do_repeat_close (struct getl_interface *interface) { - struct repeat_block *block = (struct repeat_block *) block_; + struct repeat_block *block + = UP_CAST (interface, struct repeat_block, parent); pool_destroy (block->pool); } -static bool +static bool always_false (const struct getl_interface *i UNUSED) { return false; @@ -592,7 +611,7 @@ always_false (const struct getl_interface *i UNUSED) /* 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) +do_repeat_name (const struct getl_interface *interface) { struct repeat_line *line = current_line (interface); return line ? line->file_name : NULL; @@ -601,7 +620,7 @@ do_repeat_name (const struct getl_interface *interface) /* 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) +do_repeat_location (const struct getl_interface *interface) { struct repeat_line *line = current_line (interface); return line ? line->line_number : -1;