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=e76c4903dea02e6b07c653201a9ff38dfef4f2ce;hpb=4881184f738deb7a4c5b843a361d4225f4e3d6c0;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index e76c4903de..f71409147c 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -30,13 +30,16 @@ #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) @@ -44,9 +47,10 @@ struct dummy_var { struct hmap_node hmap_node; - char *name; + struct substring name; char **values; size_t n_values; + int start_ofs, end_ofs; }; static bool parse_specification (struct lexer *, struct dictionary *, @@ -62,33 +66,22 @@ static bool parse_strings (struct lexer *, struct dummy_var *); int cmd_do_repeat (struct lexer *lexer, struct dataset *ds) { - struct hmap dummies; - bool ok; - - if (!parse_specification (lexer, dataset_dict (ds), &dummies)) - return CMD_CASCADING_FAILURE; - - ok = parse_commands (lexer, &dummies); - + struct hmap dummies = HMAP_INITIALIZER (dummies); + bool ok = parse_specification (lexer, dataset_dict (ds), &dummies); + ok = parse_commands (lexer, &dummies) && ok; destroy_dummies (&dummies); return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; } -static unsigned int -hash_dummy (const char *name, size_t name_len) -{ - return hash_case_bytes (name, name_len, 0); -} - static const struct dummy_var * -find_dummy_var (struct hmap *hmap, const char *name, size_t name_len) +find_dummy_var (struct hmap *hmap, struct substring name) { const struct dummy_var *dv; HMAP_FOR_EACH_WITH_HASH (dv, struct dummy_var, hmap_node, - hash_dummy (name, name_len), hmap) - if (strcasecmp (dv->name, name)) + utf8_hash_case_substring (name, 0), hmap) + if (!utf8_sscasecmp (dv->name, name)) return dv; return NULL; @@ -102,32 +95,32 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, { struct dummy_var *first_dv = NULL; - hmap_init (dummies); do { - struct dummy_var *dv; - const char *name; - bool ok; + int start_ofs = lex_ofs (lexer); /* Get a stand-in variable name and make sure it's unique. */ 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))) + 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)) { - msg (SE, _("Dummy variable name `%s' is given twice."), name); + lex_error (lexer, _("Dummy variable name `%s' is given twice."), + name.string); 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))); + 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); @@ -135,6 +128,7 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, goto error; /* Get the details of the variable's possible values. */ + 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)) @@ -143,7 +137,7 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, ok = parse_strings (lexer, dv); else { - lex_error (lexer, NULL); + lex_error (lexer, _("Syntax error expecting substitution values.")); goto error; } if (!ok) @@ -151,9 +145,10 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, assert (dv->n_values > 0); if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD) { - lex_error (lexer, NULL); + 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. */ @@ -161,10 +156,19 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, first_dv = dv; else if (first_dv->n_values != dv->n_values) { - 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); + 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; } @@ -178,15 +182,20 @@ parse_specification (struct lexer *lexer, struct dictionary *dict, return true; error: + lex_discard_rest_of_command (lexer); + while (lex_match (lexer, T_ENDCMD)) + continue; destroy_dummies (dummies); + hmap_init (dummies); return false; } static size_t count_values (struct hmap *dummies) { - const struct dummy_var *dv; - dv = HMAP_FIRST (struct dummy_var, hmap_node, dummies); + if (hmap_is_empty (dummies)) + return 0; + const struct dummy_var *dv = HMAP_FIRST (struct dummy_var, hmap_node, dummies); return dv->n_values; } @@ -195,26 +204,19 @@ do_parse_commands (struct substring s, enum segmenter_mode mode, struct hmap *dummies, struct string *outputs, size_t n_outputs) { - struct segmenter segmenter; - - segmenter_init (&segmenter, mode); - + struct segmenter segmenter = segmenter_init (mode, false); while (!ss_is_empty (s)) { enum segment_type type; - int n; - - n = segmenter_push (&segmenter, s.string, s.length, &type); + int n = segmenter_push (&segmenter, s.string, s.length, true, &type); assert (n >= 0); if (type == SEG_DO_REPEAT_COMMAND) { for (;;) { - int k; - - k = segmenter_push (&segmenter, s.string + n, s.length - n, - &type); + int k = segmenter_push (&segmenter, s.string + n, s.length - n, + true, &type); if (type != SEG_NEWLINE && type != SEG_DO_REPEAT_COMMAND) break; @@ -226,13 +228,10 @@ do_parse_commands (struct substring s, enum segmenter_mode mode, } else if (type != SEG_END) { - 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++) + 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 @@ -246,67 +245,49 @@ do_parse_commands (struct substring s, enum segmenter_mode mode, static bool parse_commands (struct lexer *lexer, struct hmap *dummies) { - enum lex_syntax_mode syntax_mode; - enum segmenter_mode mode; - struct string *outputs; - struct string input; - size_t n_values; - char *file_name; - int line_number; - bool ok; - size_t i; - - 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); - - ds_init_empty (&input); + 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); } - if (ds_is_empty (&input)) - ds_put_byte (&input, '\n'); - ds_put_byte (&input, '\0'); - n_values = count_values (dummies); - outputs = xmalloc (n_values * sizeof *outputs); - for (i = 0; i < n_values; i++) + 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]); - syntax_mode = lex_get_syntax_mode (lexer); - if (syntax_mode == LEX_SYNTAX_AUTO) - mode = SEG_MODE_AUTO; - else if (syntax_mode == LEX_SYNTAX_INTERACTIVE) - mode = SEG_MODE_INTERACTIVE; - else if (syntax_mode == LEX_SYNTAX_BATCH) - mode = SEG_MODE_BATCH; - else - NOT_REACHED (); - do_parse_commands (ds_ss (&input), mode, dummies, outputs, n_values); + do_parse_commands (ds_ss (&input), lex_get_syntax_mode (lexer), + dummies, outputs, n_values); ds_destroy (&input); while (lex_match (lexer, T_ENDCMD)) continue; - ok = (lex_force_match_id (lexer, "END") - && lex_force_match_id (lexer, "REPEAT")); - if (ok) - lex_match_id (lexer, "PRINT"); /* XXX */ - + 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 (i = 0; i < n_values; i++) + for (size_t i = 0; i < n_values; i++) { struct string *output = &outputs[n_values - i - 1]; - struct lex_reader *reader; - - reader = lex_reader_for_substring_nocopy (ds_ss (output)); + if (print) + { + 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)); + } + 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); @@ -324,12 +305,10 @@ destroy_dummies (struct hmap *dummies) HMAP_FOR_EACH_SAFE (dv, next, struct dummy_var, hmap_node, dummies) { - size_t i; - hmap_delete (dummies, &dv->hmap_node); - free (dv->name); - for (i = 0; i < dv->n_values; i++) + ss_dealloc (&dv->name); + for (size_t i = 0; i < dv->n_values; i++) free (dv->values[i]); free (dv->values); free (dv); @@ -369,31 +348,29 @@ parse_numbers (struct lexer *lexer, struct dummy_var *dv) if (lex_next_token (lexer, 1) == T_TO) { - long int a, b; - long int i; - if (!lex_is_integer (lexer)) { - msg (SE, _("Ranges may only have integer bounds.")); + lex_error (lexer, _("Ranges may only have integer bounds.")); return false; } - a = lex_integer (lexer); + long a = lex_integer (lexer); lex_get (lexer); lex_get (lexer); - if (!lex_force_int (lexer)) + if (!lex_force_int_range (lexer, NULL, a, LONG_MAX)) return false; - b = lex_integer (lexer); + long b = lex_integer (lexer); if (b < a) { - msg (SE, _("%ld TO %ld is an invalid range."), a, b); + lex_next_error (lexer, -2, 0, + _("%ld TO %ld is an invalid range."), a, b); return false; } lex_get (lexer); - for (i = a; i <= b; i++) + for (long i = a; i <= b; i++) add_replacement (dv, xasprintf ("%ld", i), &allocated); } else @@ -421,9 +398,7 @@ parse_strings (struct lexer *lexer, struct dummy_var *dv) do { if (!lex_force_string (lexer)) - { - return false; - } + return false; add_replacement (dv, token_to_string (lex_next (lexer, 0)), &allocated); @@ -436,8 +411,8 @@ parse_strings (struct lexer *lexer, struct dummy_var *dv) } 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; }