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=7341f0760bcf48960a01ca3f378cc18f666c9abe;hpb=cd221d80fafb54e550398c6de105d4c1b7f02ba0;p=pspp diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index 7341f0760b..f71409147c 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -34,6 +34,7 @@ #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" @@ -46,10 +47,10 @@ struct dummy_var { struct hmap_node hmap_node; - char *name; - size_t name_len; + struct substring name; char **values; size_t n_values; + int start_ofs, end_ofs; }; static bool parse_specification (struct lexer *, struct dictionary *, @@ -65,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 utf8_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 (!utf8_strncasecmp (dv->name, dv->name_len, name, name_len)) + utf8_hash_case_substring (name, 0), hmap) + if (!utf8_sscasecmp (dv->name, name)) return dv; return NULL; @@ -105,35 +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); - - size_t name_len = strlen (name); - if (find_dummy_var (dummies, name, name_len)) + 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 = xmemdup0 (name, name_len); - dv->name_len = name_len; - 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); @@ -141,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)) @@ -149,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) @@ -157,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. */ @@ -167,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; } @@ -184,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; } @@ -201,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, true, &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, - true, &type); + int k = segmenter_push (&segmenter, s.string + n, s.length - n, + true, &type); if (type != SEG_NEWLINE && type != SEG_DO_REPEAT_COMMAND) break; @@ -232,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 @@ -252,23 +245,10 @@ 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)); @@ -276,37 +256,36 @@ parse_commands (struct lexer *lexer, struct hmap *dummies) lex_get (lexer); } - 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]; + 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); @@ -326,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); @@ -373,7 +350,7 @@ parse_numbers (struct lexer *lexer, struct dummy_var *dv) { if (!lex_is_integer (lexer)) { - msg (SE, _("Ranges may only have integer bounds.")); + lex_error (lexer, _("Ranges may only have integer bounds.")); return false; } @@ -387,7 +364,8 @@ parse_numbers (struct lexer *lexer, struct dummy_var *dv) 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); @@ -420,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); @@ -435,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 %s."), "DO REPEAT"); + lex_ofs_error (lexer, 0, 1, _("No matching %s."), "DO REPEAT"); return CMD_CASCADING_FAILURE; }