Improve error messages by citing syntax in more of them.
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 29 Aug 2022 17:02:48 +0000 (10:02 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 9 Sep 2022 18:12:47 +0000 (11:12 -0700)
A while back, PSPP gained the ability to cite particular tokens in
diagnostics, but few of the existing messages were enhanced to do
so.  This commit adds this to several more of them, in particular
all of the messages that used lex_error() and variations.

108 files changed:
doc/matrices.texi
src/data/dict-class.h
src/data/por-file-reader.c
src/language/command.c
src/language/control/define.c
src/language/control/do-if.c
src/language/control/loop.c
src/language/control/repeat.c
src/language/control/temporary.c
src/language/data-io/combine-files.c
src/language/data-io/data-list.c
src/language/data-io/data-reader.c
src/language/data-io/dataset.c
src/language/data-io/file-handle.c
src/language/data-io/get-data.c
src/language/data-io/inpt-pgm.c
src/language/data-io/list.c
src/language/data-io/matrix-data.c
src/language/data-io/placement-parser.c
src/language/data-io/print-space.c
src/language/data-io/print.c
src/language/data-io/save-translate.c
src/language/data-io/save.c
src/language/data-io/trim.c
src/language/dictionary/delete-variables.c
src/language/dictionary/missing-values.c
src/language/dictionary/modify-variables.c
src/language/dictionary/mrsets.c
src/language/dictionary/numeric.c
src/language/dictionary/rename-variables.c
src/language/dictionary/sys-file-info.c
src/language/dictionary/value-labels.c
src/language/dictionary/vector.c
src/language/dictionary/weight.c
src/language/expressions/evaluate.c
src/language/expressions/parse.c
src/language/lexer/format-parser.c
src/language/lexer/lexer.c
src/language/lexer/lexer.h
src/language/lexer/macro.c
src/language/lexer/value-parser.c
src/language/lexer/variable-parser.c
src/language/lexer/variable-parser.h
src/language/stats/aggregate.c
src/language/stats/autorecode.c
src/language/stats/crosstabs.c
src/language/stats/ctables.c
src/language/stats/descriptives.c
src/language/stats/examine.c
src/language/stats/flip.c
src/language/stats/graph.c
src/language/stats/matrix.c
src/language/stats/npar.c
src/language/stats/oneway.c
src/language/stats/quick-cluster.c
src/language/stats/rank.c
src/language/stats/regression.c
src/language/stats/reliability.c
src/language/stats/t-test-parser.c
src/language/tests/float-format.c
src/language/tests/moments-test.c
src/language/utilities/cd.c
src/language/utilities/date.c
src/language/utilities/host.c
src/language/utilities/permissions.c
src/language/utilities/set.c
src/language/xforms/compute.c
src/language/xforms/count.c
src/language/xforms/recode.c
src/language/xforms/sample.c
src/language/xforms/select-if.c
src/libpspp/message.h
tests/language/command.at
tests/language/control/define.at
tests/language/control/do-if.at
tests/language/control/do-repeat.at
tests/language/control/loop.at
tests/language/data-io/data-list.at
tests/language/data-io/dataset.at
tests/language/data-io/get-data-txt.at
tests/language/data-io/inpt-pgm.at
tests/language/data-io/matrix-data.at
tests/language/data-io/mconvert.at
tests/language/data-io/print.at
tests/language/dictionary/formats.at
tests/language/dictionary/missing-values.at
tests/language/dictionary/mrsets.at
tests/language/dictionary/rename-variables.at
tests/language/dictionary/value-labels.at
tests/language/expressions/evaluate.at
tests/language/expressions/parse.at
tests/language/lexer/lexer.at
tests/language/stats/aggregate.at
tests/language/stats/ctables.at
tests/language/stats/descriptives.at
tests/language/stats/flip.at
tests/language/stats/matrix.at
tests/language/stats/quick-cluster.at
tests/language/stats/rank.at
tests/language/stats/regression.at
tests/language/stats/reliability.at
tests/language/stats/t-test.at
tests/language/utilities/host.at
tests/language/utilities/insert.at
tests/language/utilities/permissions.at
tests/output/ascii.at
tests/output/pivot-table-test.c
utilities/pspp-convert.c

index 58dea2f31b5aa655c5f9f047e43d7000c756dd87..f366e5fc79f53e69d5508856be2472e6eaf06a97 100644 (file)
@@ -2184,7 +2184,7 @@ with the given @i{width}.  The input area must be a multiple of
 @code{F@i{width}.0} format.
 
 @item
-@code{FORMAT=@i{count}F} divides the input area into @i{count}
+@code{FORMAT="@i{count}F"} divides the input area into integer @i{count}
 equal-width fields per line.  The input area must be a multiple of
 @i{count} columns wide.  Another format type may be substituted for
 @code{F}.
index e3872e81c00fbbc5c7f133297429270d760be1a4..807a107f6311bf7a3c3b73e4ccc2723d55ad855a 100644 (file)
    masks. */
 enum dict_class
   {
-    DC_ORDINARY = 0x0001,       /* Ordinary identifier. */
-    DC_SYSTEM = 0x0002,         /* System variable. */
-    DC_SCRATCH = 0x0004,        /* Scratch variable. */
-    DC_ALL = 0x0007             /* All of the above. */
+    DC_ORDINARY = 1 << 0,       /* Ordinary identifier. */
+    DC_SYSTEM = 1 << 1,         /* System variable. */
+    DC_SCRATCH = 1 << 2,        /* Scratch variable. */
+#define DC_ALL (DC_ORDINARY | DC_SYSTEM | DC_SCRATCH)
   };
 
 enum dict_class dict_class_from_id (const char *name);
index 94a2faf33b1ff4849c1d19a7f4801f6ec28d4b28..8cea15e7496592588233047bcaf5d95e8e246bbf 100644 (file)
@@ -215,7 +215,7 @@ advance (struct pfm_reader *r)
       r->line_length = 0;
     }
   if (c == EOF)
-    error (r, _("unexpected end of file"));
+    error (r, _("Unexpected end of file"));
 
   if (r->trans != NULL)
     c = r->trans[c];
index bffd13ebb0d74b119de6d296b7e4f4fe3ba91f60..ab5bab904aa8911889075b0d04a7abedfad09c99 100644 (file)
@@ -127,7 +127,7 @@ static const struct command commands[] =
 static const size_t n_commands = sizeof commands / sizeof *commands;
 
 static bool in_correct_state (const struct command *, enum cmd_state);
-static void report_state_mismatch (const struct command *, enum cmd_state);
+static char *report_state_mismatch (const struct command *, enum cmd_state);
 static void set_completion_state (enum cmd_state);
 \f
 /* Command parser. */
@@ -205,25 +205,32 @@ do_parse_command (struct lexer *lexer,
                                        utf8_to_title (command->name),
                                        utf8_to_title (command->name)));
 
+  int end = n_tokens - 1;
   if (command->function == NULL)
     {
-      msg (SE, _("%s is not yet implemented."), command->name);
+      lex_ofs_error (lexer, 0, end, _("%s is not yet implemented."),
+                     command->name);
       result = CMD_NOT_IMPLEMENTED;
     }
   else if ((command->flags & F_TESTING) && !settings_get_testing_mode ())
     {
-      msg (SE, _("%s may be used only in testing mode."), command->name);
+      lex_ofs_error (lexer, 0, end, _("%s may be used only in testing mode."),
+                     command->name);
       result = CMD_FAILURE;
     }
   else if ((command->flags & F_ENHANCED) && settings_get_syntax () != ENHANCED)
     {
-      msg (SE, _("%s may be used only in enhanced syntax mode."),
-           command->name);
+      lex_ofs_error (lexer, 0, end,
+                     _("%s may be used only in enhanced syntax mode."),
+                     command->name);
       result = CMD_FAILURE;
     }
   else if (!in_correct_state (command, state))
     {
-      report_state_mismatch (command, state);
+      char *message = report_state_mismatch (command, state);
+      lex_ofs_error (lexer, 0, end, "%s", message);
+      free (message);
+
       result = CMD_FAILURE;
     }
   else
@@ -342,17 +349,18 @@ parse_command_name (struct lexer *lexer, int *n_tokens)
       ds_truncate (&s, ds_length (&s) - 2);
     }
 
+  *n_tokens = (word + 1) + missing_words;
   if (command == NULL)
     {
       if (ds_is_empty (&s))
-        lex_error (lexer, _("expecting command name"));
+        lex_error (lexer, _("Syntax error expecting command name."));
       else
-        msg (SE, _("Unknown command `%s'."), ds_cstr (&s));
+        lex_ofs_error (lexer, 0, *n_tokens - 1,
+                       _("Unknown command `%s'."), ds_cstr (&s));
     }
 
   ds_destroy (&s);
 
-  *n_tokens = (word + 1) + missing_words;
   return command;
 }
 
@@ -364,9 +372,9 @@ in_correct_state (const struct command *command, enum cmd_state state)
   return command->states & (1 << state);
 }
 
-/* Emits an appropriate error message for trying to invoke
+/* Returns an appropriate error message for trying to invoke
    COMMAND in STATE. */
-static void
+static char *
 report_state_mismatch (const struct command *command, enum cmd_state state)
 {
   assert (!in_correct_state (command, state));
@@ -380,56 +388,49 @@ report_state_mismatch (const struct command *command, enum cmd_state state)
         {
           /* One allowed state. */
         case S_INITIAL:
-          msg (SE, _("%s is allowed only before the active dataset has "
-                     "been defined."), command->name);
-          break;
+          return xasprintf (_("%s is allowed only before the active dataset has "
+                              "been defined."), command->name);
         case S_DATA:
-          msg (SE, _("%s is allowed only after the active dataset has "
-                     "been defined."), command->name);
-          break;
+          return xasprintf (_("%s is allowed only after the active dataset has "
+                              "been defined."), command->name);
         case S_INPUT_PROGRAM:
-          msg (SE, _("%s is allowed only inside %s."),
-               command->name, "INPUT PROGRAM");
-          break;
+          return xasprintf (_("%s is allowed only inside %s."),
+                            command->name, "INPUT PROGRAM");
         case S_FILE_TYPE:
-          msg (SE, _("%s is allowed only inside %s."), command->name, "FILE TYPE");
-          break;
+          return xasprintf (_("%s is allowed only inside %s."), command->name, "FILE TYPE");
 
           /* Two allowed states. */
         case S_INITIAL | S_DATA:
           NOT_REACHED ();
         case S_INITIAL | S_INPUT_PROGRAM:
-          msg (SE, _("%s is allowed only before the active dataset has been defined or inside %s."),
-              command->name, "INPUT PROGRAM");
-          break;
+          return xasprintf (_("%s is allowed only before the active dataset "
+                              "has been defined or inside %s."),
+                            command->name, "INPUT PROGRAM");
         case S_INITIAL | S_FILE_TYPE:
-          msg (SE, _("%s is allowed only before the active dataset has been defined or inside %s."),
-              command->name, "FILE TYPE");
-          break;
+          return xasprintf (_("%s is allowed only before the active dataset "
+                              "has been defined or inside %s."),
+                            command->name, "FILE TYPE");
         case S_DATA | S_INPUT_PROGRAM:
-          msg (SE, _("%s is allowed only after the active dataset has been defined or inside %s."),
-              command->name, "INPUT PROGRAM");
-          break;
+          return xasprintf (_("%s is allowed only after the active dataset "
+                              "has been defined or inside %s."),
+                            command->name, "INPUT PROGRAM");
         case S_DATA | S_FILE_TYPE:
-          msg (SE, _("%s is allowed only after the active dataset has been defined or inside %s."),
-              command->name, "FILE TYPE");
-          break;
+          return xasprintf (_("%s is allowed only after the active dataset "
+                              "has been defined or inside %s."),
+                            command->name, "FILE TYPE");
         case S_INPUT_PROGRAM | S_FILE_TYPE:
-          msg (SE, _("%s is allowed only inside %s or inside %s."), command->name,
-              "INPUT PROGRAM", "FILE TYPE");
-          break;
+          return xasprintf (_("%s is allowed only inside %s or inside %s."),
+                            command->name, "INPUT PROGRAM", "FILE TYPE");
 
           /* Three allowed states. */
         case S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE:
-          msg (SE, _("%s is allowed only after the active dataset has "
-                     "been defined, inside INPUT PROGRAM, or inside "
-                     "FILE TYPE."), command->name);
-          break;
+          return xasprintf (_("%s is allowed only after the active dataset has "
+                              "been defined, inside INPUT PROGRAM, or inside "
+                              "FILE TYPE."), command->name);
         case S_INITIAL | S_INPUT_PROGRAM | S_FILE_TYPE:
-          msg (SE, _("%s is allowed only before the active dataset has "
-                     "been defined, inside INPUT PROGRAM, or inside "
-                     "FILE TYPE."), command->name);
-          break;
+          return xasprintf (_("%s is allowed only before the active dataset "
+                              "has been defined, inside INPUT PROGRAM, or "
+                              "inside FILE TYPE."), command->name);
         case S_INITIAL | S_DATA | S_FILE_TYPE:
           NOT_REACHED ();
         case S_INITIAL | S_DATA | S_INPUT_PROGRAM:
@@ -445,32 +446,32 @@ report_state_mismatch (const struct command *command, enum cmd_state state)
       break;
 
     case CMD_STATE_INPUT_PROGRAM:
-      msg (SE, _("%s is not allowed inside %s."),
-           command->name, "INPUT PROGRAM");
-      break;
+      return xasprintf (_("%s is not allowed inside %s."),
+                        command->name, "INPUT PROGRAM");
 
     case CMD_STATE_FILE_TYPE:
-      msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
-      break;
+      return xasprintf (_("%s is not allowed inside %s."),
+                        command->name, "FILE TYPE");
 
     case CMD_STATE_NESTED_DATA:
     case CMD_STATE_NESTED_INPUT_PROGRAM:
       switch ((int) command->states & S_NESTED_ANY)
         {
         case 0:
-          msg (SE, _("%s is not allowed inside DO IF or LOOP."), command->name);
-          break;
+          return xasprintf (_("%s is not allowed inside DO IF or LOOP."),
+                            command->name);
 
         case S_NESTED_DATA:
-          msg (SE, _("In INPUT PROGRAM, "
-                     "%s is not allowed inside DO IF or LOOP."), command->name);
-          break;
+          return xasprintf (_("In INPUT PROGRAM, "
+                              "%s is not allowed inside DO IF or LOOP."),
+                            command->name);
 
         default:
           NOT_REACHED ();
         }
-      break;
     }
+
+  NOT_REACHED ();
 }
 \f
 /* Command name completion. */
index e1d9622ff4067b882a92a8bc259c1bdf8dac2025..27f5093504a1cc4fbd96082f718586cd1e2984e9 100644 (file)
@@ -77,8 +77,9 @@ dup_arg_type (struct lexer *lexer, bool *saw_arg_type)
 {
   if (*saw_arg_type)
     {
-      lex_error (lexer, _("Only one of !TOKENS, !CHAREND, !ENCLOSE, or "
-                          "!CMDEND is allowed."));
+      lex_next_error (lexer, -1, -1,
+                      _("Only one of !TOKENS, !CHAREND, !ENCLOSE, or "
+                        "!CMDEND is allowed."));
       return false;
     }
   else
@@ -98,13 +99,13 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
      macro-expanded. */
   if (lex_token (lexer) != T_STRING)
     {
-      lex_error (lexer, _("expecting identifier"));
+      lex_error (lexer, _("Syntax error expecting identifier."));
       return CMD_FAILURE;
     }
   const char *name = lex_tokcstr (lexer);
   if (!id_is_plausible (name + (name[0] == '!'), false))
     {
-      lex_error (lexer, _("expecting identifier"));
+      lex_error (lexer, _("Syntax error expecting identifier."));
       return CMD_FAILURE;
     }
 
@@ -117,6 +118,7 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
     goto error;
 
   size_t allocated_params = 0;
+  int keyword_ofs = 0;
   while (!lex_match (lexer, T_RPAREN))
     {
       if (m->n_params >= allocated_params)
@@ -132,8 +134,11 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (param_index > 0 && !m->params[param_index - 1].positional)
             {
-              lex_error (lexer, _("Positional parameters must precede "
-                                  "keyword parameters."));
+              lex_next_error (lexer, -1, -1,
+                              _("Positional parameters must precede "
+                                "keyword parameters."));
+              lex_ofs_msg (lexer, SN, keyword_ofs, keyword_ofs,
+                           _("Here is a previous keyword parameter."));
               goto error;
             }
 
@@ -142,6 +147,8 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
         }
       else
         {
+          if (keyword_ofs == 0)
+            keyword_ofs = lex_ofs (lexer);
           if (lex_token (lexer) == T_MACRO_ID)
             {
               lex_error (lexer, _("Keyword macro parameter must be named in "
@@ -173,8 +180,9 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
             {
               if (saw_default)
                 {
-                  lex_error (lexer,
-                             _("!DEFAULT is allowed only once per argument."));
+                  lex_next_error (
+                    lexer, -1, -1,
+                    _("!DEFAULT is allowed only once per argument."));
                   goto error;
                 }
               saw_default = true;
@@ -267,7 +275,8 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
     {
       if (lex_token (lexer) != T_STRING)
         {
-          lex_error (lexer, _("Expecting macro body or !ENDDEFINE"));
+          lex_error (lexer,
+                     _("Syntax error expecting macro body or !ENDDEFINE."));
           ds_destroy (&body);
           goto error;
         }
index 66c3e78e379b927275c3653afcb1f0c22258d189..162118488c60bcd987bbff41fabf8e9708578db7 100644 (file)
@@ -62,10 +62,12 @@ start_clause (struct lexer *lexer, struct dataset *ds,
       && !do_if->clauses[do_if->n_clauses - 1].condition)
     {
       if (condition)
-        msg (SE, _("ELSE IF is not allowed following ELSE "
-                   "within DO IF...END IF."));
+        lex_ofs_error (lexer, 0, 1,
+                       _("ELSE IF is not allowed following ELSE "
+                         "within DO IF...END IF."));
       else
-        msg (SE, _("Only one ELSE is allowed within DO IF...END IF."));
+        lex_ofs_error (lexer, 0, 0,
+                       _("Only one ELSE is allowed within DO IF...END IF."));
 
       msg_at (SN, do_if->clauses[do_if->n_clauses - 1].location,
               _("This is the location of the previous ELSE clause."));
index dfc4db2e8977505743466189ef5ebfde18b1bd49..13af8225467a8c548256cd6b540779815a287f10 100644 (file)
@@ -163,7 +163,7 @@ parse_if_clause (struct lexer *lexer, struct dataset *ds,
 {
   if (*condition != NULL)
     {
-      lex_sbc_only_once ("IF");
+      lex_sbc_only_once (lexer, "IF");
       return false;
     }
 
@@ -214,7 +214,7 @@ parse_index_clause (struct dataset *ds, struct lexer *lexer,
 
       if (*e != NULL)
         {
-          lex_sbc_only_once (e == &loop->last_expr ? "TO" : "BY");
+          lex_sbc_only_once (lexer, e == &loop->last_expr ? "TO" : "BY");
           return false;
         }
       *e = expr_parse (lexer, ds, VAL_NUMERIC);
index 8876c8fb9e2efeb9bbe9a90a5b284616e83d801c..cb6a6779fae4299cfe9bbf9fa3c1b721b37c77af 100644 (file)
@@ -123,7 +123,8 @@ parse_specification (struct lexer *lexer, struct dictionary *dict,
       size_t name_len = strlen (name);
       if (find_dummy_var (dummies, name, name_len))
         {
-          msg (SE, _("Dummy variable name `%s' is given twice."), name);
+          lex_error (lexer, _("Dummy variable name `%s' is given twice."),
+                     name);
           goto error;
         }
 
@@ -359,7 +360,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;
            }
 
@@ -373,7 +374,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);
index c7514413727424dd7c455aaf28f0e65a7c9d1a44..cdcfc7625097578c2a9a4b247bfceea7fd0ef94a 100644 (file)
 
 /* Parses the TEMPORARY command. */
 int
-cmd_temporary (struct lexer *lexer UNUSED, struct dataset *ds)
+cmd_temporary (struct lexer *lexer, struct dataset *ds)
 {
   if (!proc_in_temporary_transformations (ds))
     proc_start_temporary_transformations (ds);
   else
-    msg (SE, _("This command may only appear once between "
-               "procedures and procedure-like commands."));
+    lex_ofs_error (lexer, 0, 0,
+                   _("This command may only appear once between "
+                     "procedures and procedure-like commands."));
   return CMD_SUCCESS;
 }
index 27e511595e04f205ce4a4abef7063260ce822d8e..9f39cad7754f7af50949c0e4cfdc1f479936a5a8 100644 (file)
@@ -211,15 +211,18 @@ combine_files (enum comb_command_type command,
         {
           if (!dataset_has_source (ds))
             {
-              msg (SE, _("Cannot specify the active dataset since none "
-                         "has been defined."));
+              lex_next_error (lexer, -1, -1,
+                              _("Cannot specify the active dataset since none "
+                                "has been defined."));
               goto error;
             }
 
           if (proc_make_temporary_transformations_permanent (ds))
-            msg (SE, _("This command may not be used after TEMPORARY when "
-                       "the active dataset is an input source.  "
-                       "Temporary transformations will be made permanent."));
+            lex_next_error (lexer, -1, -1,
+                            _("This command may not be used after TEMPORARY "
+                              "when the active dataset is an input source.  "
+                              "Temporary transformations will be made "
+                              "permanent."));
 
           file->dict = dict_clone (dataset_dict (ds));
         }
@@ -244,16 +247,13 @@ combine_files (enum comb_command_type command,
         else if (lex_match_id (lexer, "IN"))
           {
             lex_match (lexer, T_EQUALS);
-            if (lex_token (lexer) != T_ID)
-              {
-                lex_error (lexer, NULL);
-                goto error;
-              }
+            if (!lex_force_id (lexer))
+              goto error;
 
             if (file->in_name)
               {
-                msg (SE, _("Multiple IN subcommands for a single FILE or "
-                           "TABLE."));
+                lex_error (lexer, _("Multiple IN subcommands for a single FILE "
+                                    "or TABLE."));
                 goto error;
               }
             file->in_name = xstrdup (lex_tokcstr (lexer));
@@ -279,7 +279,7 @@ combine_files (enum comb_command_type command,
 
          if (saw_by)
            {
-              lex_sbc_only_once ("BY");
+              lex_sbc_only_once (lexer, "BY");
              goto error;
            }
           saw_by = true;
@@ -325,7 +325,7 @@ combine_files (enum comb_command_type command,
         {
           if (first_name != NULL)
             {
-              lex_sbc_only_once ("FIRST");
+              lex_sbc_only_once (lexer, "FIRST");
               goto error;
             }
 
@@ -339,7 +339,7 @@ combine_files (enum comb_command_type command,
         {
           if (last_name != NULL)
             {
-              lex_sbc_only_once ("LAST");
+              lex_sbc_only_once (lexer, "LAST");
               goto error;
             }
 
index e464db1223ce607ad3492736c379eb3b92818efe..546e33f21e86424278256c4709ec7ef779597250 100644 (file)
@@ -120,7 +120,7 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
        {
           if (data_parser_get_records (parser) > 0)
             {
-              lex_sbc_only_once ("RECORDS");
+              lex_sbc_only_once (lexer, "RECORDS");
               goto error;
             }
          lex_match (lexer, T_EQUALS);
@@ -143,12 +143,14 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
        {
           if (!in_input_program ())
             {
-              msg (SE, _("The %s subcommand may only be used within %s."), "END", "INPUT PROGRAM");
+              lex_next_error (lexer, -1, -1,
+                              _("The %s subcommand may only be used within %s."),
+                              "END", "INPUT PROGRAM");
               goto error;
             }
          if (end)
            {
-              lex_sbc_only_once ("END");
+              lex_sbc_only_once (lexer, "END");
              goto error;
            }
 
@@ -186,8 +188,9 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
 
           if (has_type)
             {
-              msg (SE, _("Only one of FIXED, FREE, or LIST may "
-                         "be specified."));
+              lex_next_error (lexer, -1, -1,
+                              _("Only one of FIXED, FREE, or LIST may "
+                                "be specified."));
               goto error;
             }
           has_type = true;
@@ -446,7 +449,8 @@ parse_free (struct lexer *lexer, struct dictionary *dict,
             return NULL;
           if (!fmt_from_name (type, &input.type))
             {
-              msg (SE, _("Unknown format type `%s'."), type);
+              lex_next_error (lexer, -1, -1,
+                              _("Unknown format type `%s'."), type);
               return NULL;
             }
 
index 70ff2756dfaf8449d9a91f1c80f9cec9bbf66645..d34067e81910005d088c0410f711091e1b6e22dd 100644 (file)
@@ -742,8 +742,9 @@ cmd_begin_data (struct lexer *lexer, struct dataset *ds)
 
   if (!fh_is_locked (fh_inline_file (), FH_ACC_READ))
     {
-      msg (SE, _("This command is not valid here since the current "
-                 "input program does not access the inline file."));
+      lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
+                     _("This command is not valid here since the current "
+                       "input program does not access the inline file."));
       return CMD_CASCADING_FAILURE;
     }
   lex_match (lexer, T_ENDCMD);
index d0d4f0a8d371af818c884c2109209174cf9b44b0..a04fec5268ba1df5f564930c5d4b77fa06f7bee5 100644 (file)
@@ -61,7 +61,7 @@ parse_dataset_name (struct lexer *lexer, struct session *session)
   if (ds != NULL)
     lex_get (lexer);
   else
-    msg (SE, _("There is no dataset named %s."), lex_tokcstr (lexer));
+    lex_error (lexer, _("There is no dataset named %s."), lex_tokcstr (lexer));
   return ds;
 }
 
index 8dcc1028cf1bb654ac936154d17e198964befe91..01c56de1960d872cc7235ee785acfbf1253a3c8a 100644 (file)
@@ -58,9 +58,9 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
   handle_name = xstrdup (lex_tokcstr (lexer));
   if (fh_from_id (handle_name))
     {
-      msg (SE, _("File handle %s is already defined.  "
-                 "Use %s before redefining a file handle."),
-          handle_name, "CLOSE FILE HANDLE");
+      lex_error (lexer, _("File handle %s is already defined.  "
+                          "Use %s before redefining a file handle."),
+                 handle_name, "CLOSE FILE HANDLE");
       goto exit;
     }
 
@@ -74,7 +74,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (file_name)
             {
-              lex_sbc_only_once ("NAME");
+              lex_sbc_only_once (lexer, "NAME");
               goto exit;
             }
 
@@ -89,7 +89,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (lrecl)
             {
-              lex_sbc_only_once ("LRECL");
+              lex_sbc_only_once (lexer, "LRECL");
               goto exit;
             }
 
@@ -103,7 +103,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (tabwidth >= 0)
             {
-              lex_sbc_only_once ("TABWIDTH");
+              lex_sbc_only_once (lexer, "TABWIDTH");
               goto exit;
             }
           lex_match (lexer, T_EQUALS);
@@ -117,7 +117,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (mode != MODE_DEFAULT)
             {
-              lex_sbc_only_once ("MODE");
+              lex_sbc_only_once (lexer, "MODE");
               goto exit;
             }
           lex_match (lexer, T_EQUALS);
@@ -140,7 +140,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (ends >= 0)
             {
-              lex_sbc_only_once ("ENDS");
+              lex_sbc_only_once (lexer, "ENDS");
               goto exit;
             }
           lex_match (lexer, T_EQUALS);
@@ -159,7 +159,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (recform)
             {
-              lex_sbc_only_once ("RECFORM");
+              lex_sbc_only_once (lexer, "RECFORM");
               goto exit;
             }
           lex_match (lexer, T_EQUALS);
@@ -181,7 +181,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
         {
           if (encoding)
             {
-              lex_sbc_only_once ("ENCODING");
+              lex_sbc_only_once (lexer, "ENCODING");
               goto exit;
             }
 
@@ -323,8 +323,6 @@ struct file_handle *
 fh_parse (struct lexer *lexer, enum fh_referent referent_mask,
           struct session *session)
 {
-  struct file_handle *handle;
-
   if (session != NULL && lex_token (lexer) == T_ID)
     {
       struct dataset *ds;
@@ -337,13 +335,16 @@ fh_parse (struct lexer *lexer, enum fh_referent referent_mask,
         }
     }
 
+  int start_ofs = lex_ofs (lexer);
+  struct file_handle *handle;
   if (lex_match_id (lexer, "INLINE"))
     handle = fh_inline_file ();
   else
     {
       if (lex_token (lexer) != T_ID && !lex_is_string (lexer))
         {
-          lex_error (lexer, _("expecting a file name or handle name"));
+          lex_error (lexer,
+                     _("Syntax error expecting a file name or handle name."));
           return NULL;
         }
 
@@ -358,8 +359,9 @@ fh_parse (struct lexer *lexer, enum fh_referent referent_mask,
 
   if (!(fh_get_referent (handle) & referent_mask))
     {
-      msg (SE, _("Handle for %s not allowed here."),
-           referent_name (fh_get_referent (handle)));
+      lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
+                     _("Handle for %s not allowed here."),
+                     referent_name (fh_get_referent (handle)));
       fh_unref (handle);
       return NULL;
     }
index 0c60bd3d63bb2c84104a28442e331499a088c30d..e746e9a05c35d9c2e5203b3633e57e19d64b4e1f 100644 (file)
@@ -124,8 +124,7 @@ cmd_get_data (struct lexer *lexer, struct dataset *ds)
       spreadsheet_unref (spreadsheet);
     }
   else
-    msg (SE, _("Unsupported TYPE %s."), tok);
-
+    lex_error_expecting (lexer, "TXT", "PSQL", "GNM", "ODS");
 
  error:
   destroy_spreadsheet_read_info (&opts);
@@ -276,8 +275,7 @@ parse_spreadsheet (struct lexer *lexer, char **filename,
            }
          else
            {
-             msg (SE, _("%s must be followed by either \"%s\" or \"%s\"."),
-                  "/SHEET", "NAME", "INDEX");
+              lex_error_expecting (lexer, "NAME", "INDEX");
              goto error;
            }
        }
@@ -299,8 +297,7 @@ parse_spreadsheet (struct lexer *lexer, char **filename,
            }
          else
            {
-             msg (SE, _("%s must be followed by either \"%s\" or \"%s\"."),
-                  "/CELLRANGE", "FULL", "RANGE");
+              lex_error_expecting (lexer, "FULL", "RANGE");
              goto error;
            }
        }
@@ -318,8 +315,7 @@ parse_spreadsheet (struct lexer *lexer, char **filename,
            }
          else
            {
-             msg (SE, _("%s must be followed by either \"%s\" or \"%s\"."),
-                  "/READNAMES", "ON", "OFF");
+              lex_error_expecting (lexer, "ON", "OFF");
              goto error;
            }
        }
@@ -468,6 +464,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
         }
       else if (lex_match_id (lexer, "IMPORTCASES"))
         {
+          int start_ofs = lex_ofs (lexer) - 1;
           lex_match (lexer, T_EQUALS);
           if (lex_match (lexer, T_ALL))
             {
@@ -485,8 +482,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
                 goto error;
               lex_get (lexer);
             }
-          msg (SW, _("Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES "
-                     "or SAMPLE may be used to substitute.)"));
+          lex_ofs_msg (lexer, SW, start_ofs, lex_ofs (lexer) - 1,
+                       _("Ignoring obsolete IMPORTCASES subcommand.  (N OF "
+                         "CASES or SAMPLE may be used to substitute.)"));
         }
       else if (lex_match_id_n (lexer, "DELIMITERS", 4))
         {
@@ -532,8 +530,8 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
           if (settings_get_syntax () == COMPATIBLE
               && ss_length (lex_tokss (lexer)) != 1)
             {
-              msg (SE, _("In compatible syntax mode, the QUALIFIER string "
-                         "must contain exactly one character."));
+              lex_error (lexer, _("In compatible syntax mode, the QUALIFIER "
+                                  "string must contain exactly one character."));
               goto error;
             }
 
@@ -567,6 +565,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
           lex_get (lexer);
         }
 
+      int name_ofs = lex_ofs (lexer);
       const char * tstr = lex_tokcstr (lexer);
       if (tstr == NULL)
        {
@@ -605,7 +604,8 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
             goto error;
           if (!fmt_from_name (fmt_type_name, &fmt_type))
             {
-              msg (SE, _("Unknown format type `%s'."), fmt_type_name);
+              lex_next_error (lexer, -1, -1,
+                              _("Unknown format type `%s'."), fmt_type_name);
               goto error;
             }
           /* Compose input format. */
@@ -630,7 +630,8 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
       v = dict_create_var (dict, name, fmt_var_width (&input));
       if (v == NULL)
         {
-          msg (SE, _("%s is a duplicate variable name."), name);
+          lex_ofs_error (lexer, name_ofs, name_ofs,
+                         _("%s is a duplicate variable name."), name);
           goto error;
         }
       var_set_both_formats (v, &output);
index b9e2c326feb86374b6e17f7afc70568b38d61e63..46ee3567f027b5341d5e3c8b103516de16b9db70 100644 (file)
@@ -294,7 +294,7 @@ cmd_reread (struct lexer *lexer, struct dataset *ds)
 
          if (e)
            {
-              lex_sbc_only_once ("COLUMN");
+              lex_sbc_only_once (lexer, "COLUMN");
               goto error;
            }
 
index c98b6f9a5218dce96f157a8698af1a84857ec12e..8a55f58a1230aa1cfa1a2865d6b987d5e9b8c1d0 100644 (file)
@@ -256,4 +256,3 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
   free (cmd.v_variables);
   return CMD_FAILURE;
 }
-
index f741b4d54cc2808b59488d5caec39865c82aa813..4ae4ef70cd7b13ced5138a1d71a549e65e66b94b 100644 (file)
@@ -1033,7 +1033,9 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
       else if (lex_match_id (lexer, "CELLS"))
        {
           if (mf.input_rowtype)
-            msg (SW, _("CELLS is ignored when VARIABLES includes ROWTYPE_"));
+            lex_next_msg (lexer, SW,
+                          -1, -1, _("CELLS is ignored when VARIABLES "
+                                    "includes ROWTYPE_"));
 
          lex_match (lexer, T_EQUALS);
 
@@ -1058,7 +1060,14 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
                   if (open || in_parens || (lex_token (lexer) != T_ENDCMD
                                             && lex_token (lexer) != T_SLASH))
                     {
-                      lex_error (lexer, _("Row type keyword expected."));
+                      const char *rowtypes[] = {
+#define RT(NAME, DIMS) #NAME,
+                        ROWTYPES
+#undef RT
+                        "N_VECTOR", "SD",
+                      };
+                      lex_error_expecting_array (
+                        lexer, rowtypes, sizeof rowtypes / sizeof *rowtypes);
                       goto error;
                     }
                   break;
index dddbb05767390ed0f4f1585ab98d35deb06c6138..c56e089a9639ecc784715023ef65d37305be696d 100644 (file)
@@ -103,8 +103,8 @@ parse_var_placements (struct lexer *lexer, struct pool *pool, size_t n_vars,
     }
   else
     {
-      msg (SE, _("SPSS-like or Fortran-like format "
-                 "specification expected after variable names."));
+      lex_error (lexer, _("SPSS-like or Fortran-like format "
+                          "specification expected after variable names."));
       return false;
     }
 }
@@ -233,7 +233,8 @@ fixed_parse_fortran (struct lexer *lexer, struct pool *pool, enum fmt_use use,
                 {
                   if (!fmt_from_name (type, &f.type))
                     {
-                      msg (SE, _("Unknown format type `%s'."), type);
+                      lex_next_error (lexer, -1, -1,
+                                      _("Unknown format type `%s'."), type);
                       return false;
                     }
                   if (!fmt_check (&f, use))
@@ -300,16 +301,26 @@ execute_placement_format (const struct fmt_spec *format,
 }
 
 static bool
-parse_column__ (int value, int base, int *column)
+parse_column__ (struct lexer *lexer, bool negative, int base, int *column)
 {
   assert (base == 0 || base == 1);
+
+  if (!lex_force_int (lexer))
+    return false;
+  long int value = lex_integer (lexer);
+  if (negative)
+    value = -value;
+  lex_get (lexer);
+
   *column = value - base + 1;
   if (*column < 1)
     {
       if (base == 1)
-        msg (SE, _("Column positions for fields must be positive."));
+        lex_next_error (lexer, -1, -1,
+                        _("Column positions for fields must be positive."));
       else
-        msg (SE, _("Column positions for fields must not be negative."));
+        lex_next_error (lexer, -1, -1,
+                        _("Column positions for fields must not be negative."));
       return false;
     }
   return true;
@@ -326,14 +337,7 @@ parse_column__ (int value, int base, int *column)
 bool
 parse_column (struct lexer *lexer, int base, int *column)
 {
-  assert (base == 0 || base == 1);
-
-  if (!lex_force_int (lexer)
-      || !parse_column__ (lex_integer (lexer), base, column))
-    return false;
-
-  lex_get (lexer);
-  return true;
+  return parse_column__ (lexer, false, base, column);
 }
 
 /* Parse a column or a range of columns, specified as a single
@@ -354,23 +358,23 @@ parse_column_range (struct lexer *lexer, int base,
                     int *first_column, int *last_column,
                     bool *range_specified)
 {
+  int start_ofs = lex_ofs (lexer);
+
   /* First column. */
-  if (!lex_force_int (lexer)
-      || !parse_column__ (lex_integer (lexer), base, first_column))
+  if (!parse_column__ (lexer, false, base, first_column))
     return false;
-  lex_get (lexer);
 
   /* Last column. */
   if (lex_is_integer (lexer) && lex_integer (lexer) < 0)
     {
-      if (!parse_column__ (-lex_integer (lexer), base, last_column))
+      if (!parse_column__ (lexer, true, base, last_column))
         return false;
-      lex_get (lexer);
 
       if (*last_column < *first_column)
        {
-         msg (SE, _("The ending column for a field must be "
-                    "greater than the starting column."));
+         lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
+                         _("The ending column for a field must be "
+                           "greater than the starting column."));
          return false;
        }
 
index b908927bdf6415438542a708f92050761575b9e0..7a16d818cb4f205fd7669365fa10a10b57cba64a 100644 (file)
@@ -79,7 +79,7 @@ cmd_print_space (struct lexer *lexer, struct dataset *ds)
       expr = expr_parse (lexer, ds, VAL_NUMERIC);
       if (lex_token (lexer) != T_ENDCMD)
        {
-          lex_error (lexer, _("expecting end of command"));
+          lex_error (lexer, _("Syntax error expecting end of command."));
           goto error;
        }
     }
index 657d53c35074a23e2660717937001fae087646fd..3b5f901b5342bfb4ab16028adbd8769b2980ca22 100644 (file)
@@ -190,7 +190,8 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
        print_table = false;
       else
        {
-         lex_error (lexer, _("expecting a valid subcommand"));
+          lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
+                               "TABLE", "NOTABLE");
          goto error;
        }
     }
index a565f4471c3aeef45f34db40c44641b34a620ec9..3b6836799756bfc3f68677f83998752f4e637984 100644 (file)
@@ -93,7 +93,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
        {
           if (handle != NULL)
             {
-              lex_sbc_only_once ("OUTFILE");
+              lex_sbc_only_once (lexer, "OUTFILE");
               goto error;
             }
 
@@ -107,7 +107,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
         {
           if (type != 0)
             {
-              lex_sbc_only_once ("TYPE");
+              lex_sbc_only_once (lexer, "TYPE");
               goto error;
             }
 
@@ -165,8 +165,8 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
                   /* XXX should support multibyte UTF-8 delimiters */
                   if (ss_length (lex_tokss (lexer)) != 1)
                     {
-                      msg (SE, _("The %s string must contain exactly one "
-                                 "character."), "DELIMITER");
+                      lex_error (lexer, _("The %s string must contain exactly "
+                                          "one character."), "DELIMITER");
                       goto error;
                     }
                   delimiter = ss_first (lex_tokss (lexer));
@@ -180,8 +180,8 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
                   /* XXX should support multibyte UTF-8 qualifiers */
                   if (ss_length (lex_tokss (lexer)) != 1)
                     {
-                      msg (SE, _("The %s string must contain exactly one "
-                                 "character."), "QUALIFIER");
+                      lex_error (lexer, _("The %s string must contain exactly "
+                                          "one character."), "QUALIFIER");
                       goto error;
                     }
                   qualifier = ss_first (lex_tokss (lexer));
index 88f3c75ee59a23d66a628995843d3b6dbaf23f27..bf78276b84af29b234cf1f543b27b2768b668d1d 100644 (file)
@@ -198,7 +198,7 @@ parse_write_command (struct lexer *lexer, struct dataset *ds,
        {
           if (handle != NULL)
             {
-              lex_sbc_only_once ("OUTFILE");
+              lex_sbc_only_once (lexer, "OUTFILE");
               goto error;
             }
 
@@ -212,7 +212,7 @@ parse_write_command (struct lexer *lexer, struct dataset *ds,
        {
           if (metadata != NULL)
             {
-              lex_sbc_only_once ("METADATA");
+              lex_sbc_only_once (lexer, "METADATA");
               goto error;
             }
 
index 4616b431d22cb1303810e8679899514d0589f5bb..ad25f400aaeac75924981210cfd190db4161f907 100644 (file)
@@ -55,7 +55,7 @@ parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax)
     return parse_dict_rename (lexer, dict, relax);
   else
     {
-      lex_error (lexer, _("expecting a valid subcommand"));
+      lex_error_expecting (lexer, "MAP", "DROP", "KEEP", "RENAME");
       return false;
     }
 }
index 1c38074f63c13c43be475b9c1fa009dbad01a61a..1063b1652e6abe0b549162f5ad40f31b1f28d5e4 100644 (file)
@@ -22,6 +22,7 @@
 #include "data/dataset.h"
 #include "data/dictionary.h"
 #include "language/command.h"
+#include "language/lexer/lexer.h"
 #include "language/lexer/variable-parser.h"
 #include "libpspp/message.h"
 
@@ -37,9 +38,10 @@ cmd_delete_variables (struct lexer *lexer, struct dataset *ds)
   bool ok;
 
   if (proc_make_temporary_transformations_permanent (ds))
-    msg (SE, _("%s may not be used after %s.  "
-               "Temporary transformations will be made permanent."),
-        "DELETE VARIABLES", "TEMPORARY");
+    lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
+                   _("%s may not be used after %s.  "
+                     "Temporary transformations will be made permanent."),
+                   "DELETE VARIABLES", "TEMPORARY");
 
   if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars, PV_NONE))
     goto error;
index 8d82b480cb308edb3d45f617c2bcc82178ff9db2..425c50db3e579b6b98bfae1cbd10e16e2bd06591 100644 (file)
@@ -123,9 +123,9 @@ cmd_missing_values (struct lexer *lexer, struct dataset *ds)
                   utf8_trunc_len = utf8_encoding_trunc_len (utf8_s, encoding,
                                                             MV_MAX_STRING);
                   if (utf8_trunc_len < utf8_len)
-                    msg (SE, _("Truncating missing value to maximum "
-                               "acceptable length (%d bytes)."),
-                         MV_MAX_STRING);
+                    lex_error (lexer, _("Truncating missing value to maximum "
+                                        "acceptable length (%d bytes)."),
+                               MV_MAX_STRING);
 
                   /* Recode to dictionary encoding and add. */
                   raw_s = recode_string (encoding, "UTF-8",
index e022586dac71102ef4244505264f8c88eeba8a17..e14b46e04fdd689b7cf0ff9740c9cb651ddfd64c 100644 (file)
@@ -76,9 +76,10 @@ int
 cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
 {
   if (proc_make_temporary_transformations_permanent (ds))
-    msg (SE, _("%s may not be used after %s.  "
-               "Temporary transformations will be made permanent."),
-         "MODIFY VARS", "TEMPORARY");
+    lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
+                   _("%s may not be used after %s.  "
+                     "Temporary transformations will be made permanent."),
+                   "MODIFY VARS", "TEMPORARY");
 
   /* Bits indicated whether we've already encountered a subcommand of this
      type. */
@@ -107,7 +108,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
        {
          if (already_encountered & 1)
            {
-              lex_sbc_only_once ("REORDER");
+              lex_sbc_only_once (lexer, "REORDER");
              goto done;
            }
          already_encountered |= 1;
@@ -187,7 +188,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
        {
          if (already_encountered & 2)
            {
-              lex_sbc_only_once ("RENAME");
+              lex_sbc_only_once (lexer, "RENAME");
              goto done;
            }
          already_encountered |= 2;
@@ -240,10 +241,11 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
        {
          if (already_encountered & 4)
            {
-             msg (SE,
-                   _("%s subcommand may be given at most once.  It may "
-                     "not be given in conjunction with the %s subcommand."),
-                  "KEEP", "DROP");
+             lex_next_error (lexer, -1, -1,
+                              _("%s subcommand may be given at most once.  "
+                                "It may not be given in conjunction with the "
+                                "%s subcommand."),
+                              "KEEP", "DROP");
              goto done;
            }
          already_encountered |= 4;
@@ -291,9 +293,10 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
 
          if (already_encountered & 4)
            {
-             msg (SE, _("%s subcommand may be given at most once.  It may "
-                         "not be given in conjunction with the %s "
-                         "subcommand."),
+             lex_next_error (lexer, -1, -1,
+                              _("%s subcommand may be given at most once.  "
+                                "It may not be given in conjunction with the "
+                                "%s subcommand."),
                   "DROP", "KEEP"
                );
              goto done;
@@ -327,11 +330,8 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
        }
       else
        {
-         if (lex_token (lexer) == T_ID)
-           msg (SE, _("Unrecognized subcommand name `%s'."),
-                 lex_tokcstr (lexer));
-         else
-           msg (SE, _("Subcommand name expected."));
+          lex_error_expecting (lexer, "REORDER", "RENAME", "KEEP",
+                               "DROP", "MAP");
          goto done;
        }
 
index 3b96da3a6ceab28a7177416386bcef1d22ca87fa..0ea5cb8c82af87cb3096ad012c45fc8c2dab726d 100644 (file)
@@ -146,7 +146,7 @@ parse_group (struct lexer *lexer, struct dictionary *dict,
             {
               if (!lex_is_integer (lexer))
                 {
-                  msg (SE, _("Numeric VALUE must be an integer."));
+                  lex_error (lexer, _("Numeric VALUE must be an integer."));
                   goto error;
                 }
               value_destroy (&mrset->counted, mrset->width);
@@ -481,8 +481,8 @@ parse_mrset_names (struct lexer *lexer, struct dictionary *dict,
             return false;
           if (dict_lookup_mrset (dict, lex_tokcstr (lexer)) == NULL)
             {
-              msg (SE, _("No multiple response set named %s."),
-                   lex_tokcstr (lexer));
+              lex_error (lexer, _("No multiple response set named %s."),
+                         lex_tokcstr (lexer));
               stringi_set_destroy (mrset_names);
               return false;
             }
index c04e8eba7a0f9f4797dbaf95276c5bae16b24c6f..ae9f25325a48ce0cded05b7451aa691633372d63 100644 (file)
@@ -64,8 +64,9 @@ cmd_numeric (struct lexer *lexer, struct dataset *ds)
          if (fmt_is_string (f.type))
            {
               char str[FMT_STRING_LEN_MAX + 1];
-             msg (SE, _("Format type %s may not be used with a numeric "
-                         "variable."), fmt_to_string (&f, str));
+             lex_next_error (lexer, -1, -1,
+                              _("Format type %s may not be used with a numeric "
+                                "variable."), fmt_to_string (&f, str));
              goto fail;
            }
 
@@ -135,8 +136,9 @@ cmd_string (struct lexer *lexer, struct dataset *ds)
       if (!fmt_is_string (f.type))
        {
           char str[FMT_STRING_LEN_MAX + 1];
-         msg (SE, _("Format type %s may not be used with a string "
-                     "variable."), fmt_to_string (&f, str));
+         lex_next_error (lexer, -2, -2,
+                          _("Format type %s may not be used with a string "
+                            "variable."), fmt_to_string (&f, str));
          goto fail;
        }
       if (!fmt_check_output (&f))
index 33d8f6887f61d64267b4a2d5587c79a822a12d2b..94331c4afe86d383db7aa3f90d4f861e762861e0 100644 (file)
@@ -48,8 +48,10 @@ cmd_rename_variables (struct lexer *lexer, struct dataset *ds)
   int status = CMD_CASCADING_FAILURE;
 
   if (proc_make_temporary_transformations_permanent (ds))
-    msg (SE, _("%s may not be used after %s.  "
-               "Temporary transformations will be made permanent."), "RENAME VARS", "TEMPORARY");
+    lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
+                   _("%s may not be used after %s.  "
+                     "Temporary transformations will be made permanent."),
+                   "RENAME VARS", "TEMPORARY");
 
   do
     {
index 7f5216134c8e1d869700ce3437db2ea7e7209845..45c352579ca41817c0102cf5f53825809fc40d12 100644 (file)
@@ -718,7 +718,7 @@ display_vectors (const struct dictionary *dict, int sorted)
   size_t n_vectors = dict_get_n_vectors (dict);
   if (n_vectors == 0)
     {
-      msg (SW, _("No vectors defined."));
+      msg (SN, _("No vectors defined."));
       return;
     }
 
index 0fb847165255cfe06ce0c60d88fed6fc7c12b953..235c57273b32ced586bbb0f6ecb8a000d3c8cf88 100644 (file)
@@ -149,7 +149,9 @@ get_label (struct lexer *lexer, struct variable **vars, size_t n_vars,
                                            MAX_LABEL_LEN);
       if (ds_length (&label) > trunc_len)
        {
-         msg (SW, _("Truncating value label to %d bytes."), MAX_LABEL_LEN);
+         lex_next_msg (lexer, SW, 0, 0,
+                        _("Truncating value label to %d bytes."),
+                        MAX_LABEL_LEN);
          ds_truncate (&label, trunc_len);
        }
 
index 16617f87e0b9e74f0864a22ccc285ee945d40f93..02687e51a9f71d3095bf82b7040ca69cb1122dfe 100644 (file)
@@ -63,16 +63,18 @@ cmd_vector (struct lexer *lexer, struct dataset *ds)
 
          if (dict_lookup_vector (dict, lex_tokcstr (lexer)))
            {
-             msg (SE, _("A vector named %s already exists."),
-                   lex_tokcstr (lexer));
+             lex_next_error (lexer, 0, 0,
+                              _("A vector named %s already exists."),
+                              lex_tokcstr (lexer));
              goto fail;
            }
 
           for (i = 0; i < n_vectors; i++)
             if (!utf8_strcasecmp (vectors[i], lex_tokcstr (lexer)))
              {
-               msg (SE, _("Vector name %s is given twice."),
-                     lex_tokcstr (lexer));
+               lex_next_error (lexer, 0, 0,
+                                _("Vector name %s is given twice."),
+                                lex_tokcstr (lexer));
                goto fail;
              }
 
@@ -95,8 +97,8 @@ cmd_vector (struct lexer *lexer, struct dataset *ds)
 
          if (n_vectors > 1)
            {
-             msg (SE, _("A slash must separate each vector "
-                         "specification in VECTOR's long form."));
+             lex_error (lexer, _("A slash must separate each vector "
+                                  "specification in VECTOR's long form."));
              goto fail;
            }
 
@@ -145,7 +147,7 @@ cmd_vector (struct lexer *lexer, struct dataset *ds)
             }
           if (n_vars == 0)
             {
-              lex_error (lexer, _("expecting vector length"));
+              lex_error (lexer, _("Syntax error expecting vector length."));
               goto fail;
             }
 
index 185165dcbbfdb1cfe42e2bd0872e748432ff955a..d99c2acb0cb04703eefb57b5e74f43a414c2c0a6 100644 (file)
@@ -46,12 +46,14 @@ cmd_weight (struct lexer *lexer, struct dataset *ds)
        return CMD_CASCADING_FAILURE;
       if (var_is_alpha (v))
        {
-         msg (SE, _("The weighting variable must be numeric."));
+         lex_next_error (lexer, -1, -1,
+                          _("The weighting variable must be numeric."));
          return CMD_CASCADING_FAILURE;
        }
       if (dict_class_from_id (var_get_name (v)) == DC_SCRATCH)
        {
-         msg (SE, _("The weighting variable may not be scratch."));
+         lex_next_error (lexer, -1, -1,
+                          _("The weighting variable may not be scratch."));
          return CMD_CASCADING_FAILURE;
        }
 
index 8b47f445ee050df33f112c25a2c85a526d9f7a4d..e07deeaf81876eac96be8a84319a2a0f49527b6e 100644 (file)
@@ -148,6 +148,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
 
           if (!lex_force_id (lexer))
             goto done;
+          int name_ofs = lex_ofs (lexer);
           name = xstrdup (lex_tokcstr (lexer));
 
           lex_get (lexer);
@@ -175,7 +176,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
             }
           else
             {
-              lex_error (lexer, _("expecting number or string"));
+              lex_error (lexer, _("Syntax error expecting number or string."));
               goto done;
             }
 
@@ -188,7 +189,8 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
           v = dict_create_var (d, name, width);
           if (v == NULL)
             {
-              msg (SE, _("Duplicate variable name %s."), name);
+              lex_ofs_error (lexer, name_ofs, name_ofs,
+                             _("Duplicate variable name %s."), name);
               value_destroy (&value, width);
               goto done;
             }
index d06d633d5b02fa4eb40273a02e2d7bbb64e90714..f1800f3775afeca8078d9916c608d8f5100fe0c9 100644 (file)
@@ -912,7 +912,7 @@ parse_sysvar (struct lexer *lexer, struct expression *e)
     return expr_allocate_number (e, settings_get_viewwidth ());
   else
     {
-      msg (SE, _("Unknown system variable %s."), lex_tokcstr (lexer));
+      lex_error (lexer, _("Unknown system variable %s."), lex_tokcstr (lexer));
       return NULL;
     }
 }
@@ -960,7 +960,7 @@ parse_primary__ (struct lexer *lexer, struct expression *e)
             return expr_allocate_format (e, &fmt);
 
           /* All attempts failed. */
-          msg (SE, _("Unknown identifier %s."), lex_tokcstr (lexer));
+          lex_error (lexer, _("Unknown identifier %s."), lex_tokcstr (lexer));
           return NULL;
         }
       break;
@@ -1332,7 +1332,8 @@ parse_function (struct lexer *lexer, struct expression *e)
   const struct operation *first, *last;
   if (!lookup_function (lex_tokcstr (lexer), &first, &last))
     {
-      msg (SE, _("No function or vector named %s."), lex_tokcstr (lexer));
+      lex_error (lexer, _("No function or vector named %s."),
+                 lex_tokcstr (lexer));
       ds_destroy (&func_name);
       return NULL;
     }
index aa30852d11327b3ddcf2d3fc8488f04649fc73f7..c2187e5a2f1024c78062aa055ba392285f5aaecf 100644 (file)
@@ -84,7 +84,7 @@ parse_abstract_format_specifier__ (struct lexer *lexer,
   return true;
 
 error:
-  lex_error (lexer, _("expecting valid format specifier"));
+  lex_error (lexer, _("Syntax error expecting valid format specifier."));
   return false;
 }
 
@@ -115,14 +115,14 @@ parse_format_specifier (struct lexer *lexer, struct fmt_spec *format)
 
   if (!fmt_from_name (type, &format->type))
     {
-      msg (SE, _("Unknown format type `%s'."), type);
+      lex_error (lexer, _("Unknown format type `%s'."), type);
       return false;
     }
 
   if (format->w == 0 && !strchr (lex_tokcstr (lexer), '0'))
     {
-      msg (SE, _("Format specifier `%s' lacks required width."),
-           lex_tokcstr (lexer));
+      lex_error (lexer, _("Format specifier `%s' lacks required width."),
+                 lex_tokcstr (lexer));
       return false;
     }
 
@@ -137,12 +137,12 @@ parse_format_specifier_name (struct lexer *lexer, enum fmt_type *type)
 {
   if (lex_token (lexer) != T_ID)
     {
-      lex_error (lexer, _("expecting format type"));
+      lex_error (lexer, _("Syntax error expecting format type."));
       return false;
     }
   if (!fmt_from_name (lex_tokcstr (lexer), type))
     {
-      msg (SE, _("Unknown format type `%s'."), lex_tokcstr (lexer));
+      lex_error (lexer, _("Unknown format type `%s'."), lex_tokcstr (lexer));
       return false;
     }
   lex_get (lexer);
index 1074ea243ad85db4f087ec03449d5b215e328107..9a67f969224e66be80d0d4c36a47fdb1d31ffd65 100644 (file)
@@ -85,6 +85,8 @@ static struct msg_point lex_token_start_point (const struct lex_source *,
 static struct msg_point lex_token_end_point (const struct lex_source *,
                                              const struct lex_token *);
 
+static size_t lex_ofs_at_phrase__ (struct lexer *, int ofs, const char *s);
+
 /* Source offset of the last byte in TOKEN. */
 static size_t
 lex_token_end (const struct lex_token *token)
@@ -290,9 +292,10 @@ static void lex_source_push_parse (struct lex_source *, struct lex_token *);
 static void lex_source_clear_parse (struct lex_source *);
 
 static bool lex_source_get_parse (struct lex_source *);
-static void lex_source_error_valist (struct lex_source *, int ofs0, int ofs1,
-                                     const char *format, va_list)
-   PRINTF_FORMAT (4, 0);
+static void lex_source_msg_valist (struct lex_source *, enum msg_class,
+                                   int ofs0, int ofs1,
+                                   const char *format, va_list)
+   PRINTF_FORMAT (5, 0);
 static const struct lex_token *lex_source_next__ (const struct lex_source *,
                                                   int n);
 \f
@@ -425,42 +428,77 @@ lex_error (struct lexer *lexer, const char *format, ...)
   va_list args;
 
   va_start (args, format);
-  lex_ofs_error_valist (lexer, lex_ofs (lexer), lex_ofs (lexer), format, args);
+  lex_ofs_msg_valist (lexer, SE, lex_ofs (lexer), lex_ofs (lexer),
+                      format, args);
   va_end (args);
 }
 
-/* Prints a syntax error message containing the current token and
-   given message MESSAGE (if non-null). */
+/* Prints a syntax error message for the span of tokens N0 through N1,
+   inclusive, from the current token in LEXER, adding message MESSAGE (if
+   non-null). */
 void
-lex_error_valist (struct lexer *lexer, const char *format, va_list args)
+lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...)
 {
-  lex_ofs_error_valist (lexer, lex_ofs (lexer), lex_ofs (lexer), format, args);
+  va_list args;
+
+  va_start (args, format);
+  int ofs = lex_ofs (lexer);
+  lex_ofs_msg_valist (lexer, SE, n0 + ofs, n1 + ofs, format, args);
+  va_end (args);
+}
+
+/* Prints a syntax error message for the span of tokens with offsets OFS0
+   through OFS1, inclusive, within the current command in LEXER, adding message
+   MESSAGE (if non-null). */
+void
+lex_ofs_error (struct lexer *lexer, int ofs0, int ofs1, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  lex_ofs_msg_valist (lexer, SE, ofs0, ofs1, format, args);
+  va_end (args);
+}
+
+/* Prints a message of the given CLASS containing the current token and given
+   message MESSAGE (if non-null). */
+void
+lex_msg (struct lexer *lexer, enum msg_class class, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  lex_ofs_msg_valist (lexer, class, lex_ofs (lexer), lex_ofs (lexer),
+                      format, args);
+  va_end (args);
 }
 
 /* Prints a syntax error message for the span of tokens N0 through N1,
    inclusive, from the current token in LEXER, adding message MESSAGE (if
    non-null). */
 void
-lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...)
+lex_next_msg (struct lexer *lexer, enum msg_class class, int n0, int n1,
+              const char *format, ...)
 {
   va_list args;
 
   va_start (args, format);
   int ofs = lex_ofs (lexer);
-  lex_ofs_error_valist (lexer, n0 + ofs, n1 + ofs, format, args);
+  lex_ofs_msg_valist (lexer, class, n0 + ofs, n1 + ofs, format, args);
   va_end (args);
 }
 
-/* Prints a syntax error message for the span of tokens with offsets OFS0
+/* Prints a message of the given CLASS for the span of tokens with offsets OFS0
    through OFS1, inclusive, within the current command in LEXER, adding message
    MESSAGE (if non-null). */
 void
-lex_ofs_error (struct lexer *lexer, int ofs0, int ofs1, const char *format, ...)
+lex_ofs_msg (struct lexer *lexer, enum msg_class class, int ofs0, int ofs1,
+             const char *format, ...)
 {
   va_list args;
 
   va_start (args, format);
-  lex_ofs_error_valist (lexer, ofs0, ofs1, format, args);
+  lex_ofs_msg_valist (lexer, class, ofs0, ofs1, format, args);
   va_end (args);
 }
 
@@ -505,42 +543,45 @@ lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n)
       break;
 
     case 1:
-      lex_error (lexer, _("expecting %s"), options[0]);
+      lex_error (lexer, _("Syntax error expecting %s."), options[0]);
       break;
 
     case 2:
-      lex_error (lexer, _("expecting %s or %s"), options[0], options[1]);
+      lex_error (lexer, _("Syntax error expecting %s or %s."),
+                 options[0], options[1]);
       break;
 
     case 3:
-      lex_error (lexer, _("expecting %s, %s, or %s"), options[0], options[1],
-                 options[2]);
+      lex_error (lexer, _("Syntax error expecting %s, %s, or %s."),
+                 options[0], options[1], options[2]);
       break;
 
     case 4:
-      lex_error (lexer, _("expecting %s, %s, %s, or %s"),
+      lex_error (lexer, _("Syntax error expecting %s, %s, %s, or %s."),
                  options[0], options[1], options[2], options[3]);
       break;
 
     case 5:
-      lex_error (lexer, _("expecting %s, %s, %s, %s, or %s"),
+      lex_error (lexer, _("Syntax error expecting %s, %s, %s, %s, or %s."),
                  options[0], options[1], options[2], options[3], options[4]);
       break;
 
     case 6:
-      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, or %s"),
+      lex_error (lexer, _("Syntax error expecting %s, %s, %s, %s, %s, or %s."),
                  options[0], options[1], options[2], options[3], options[4],
                  options[5]);
       break;
 
     case 7:
-      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, %s, or %s"),
+      lex_error (lexer, _("Syntax error expecting %s, %s, %s, %s, %s, %s, "
+                          "or %s."),
                  options[0], options[1], options[2], options[3], options[4],
                  options[5], options[6]);
       break;
 
     case 8:
-      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, %s, %s, or %s"),
+      lex_error (lexer, _("Syntax error expecting %s, %s, %s, %s, %s, %s, %s, "
+                          "or %s."),
                  options[0], options[1], options[2], options[3], options[4],
                  options[5], options[6], options[7]);
       break;
@@ -554,7 +595,7 @@ lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n)
               ds_put_cstr (&s, ", ");
             ds_put_cstr (&s, options[i]);
           }
-        lex_error (lexer, _("expecting one of the following: %s"),
+        lex_error (lexer, _("Syntax error expecting one of the following: %s."),
                    ds_cstr (&s));
         ds_destroy (&s);
       }
@@ -563,16 +604,21 @@ lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n)
 }
 
 /* Reports an error to the effect that subcommand SBC may only be specified
-   once.
-
-   This function does not take a lexer as an argument or use lex_error(),
-   because the result would ordinarily just be redundant: "Syntax error at
-   SUBCOMMAND: Subcommand SUBCOMMAND may only be specified once.", which does
-   not help the user find the error. */
+   once. */
 void
-lex_sbc_only_once (const char *sbc)
+lex_sbc_only_once (struct lexer *lexer, const char *sbc)
 {
-  msg (SE, _("Subcommand %s may only be specified once."), sbc);
+  int ofs = lex_ofs (lexer) - 1;
+  if (lex_ofs_token (lexer, ofs)->type == T_EQUALS)
+    ofs--;
+
+  /* lex_ofs_at_phrase__() handles subcommand names that are keywords, such as
+     BY. */
+  if (lex_ofs_at_phrase__ (lexer, ofs, sbc))
+    lex_ofs_error (lexer, ofs, ofs,
+                   _("Subcommand %s may only be specified once."), sbc);
+  else
+    msg (SE, _("Subcommand %s may only be specified once."), sbc);
 }
 
 /* Reports an error to the effect that subcommand SBC is missing.
@@ -592,7 +638,7 @@ lex_sbc_missing (const char *sbc)
 void
 lex_spec_only_once (struct lexer *lexer, const char *sbc, const char *spec)
 {
-  lex_error (lexer, _("%s may only be specified once within subcommand %s"),
+  lex_error (lexer, _("%s may only be specified once within subcommand %s."),
              spec, sbc);
 }
 
@@ -601,37 +647,18 @@ lex_spec_only_once (struct lexer *lexer, const char *sbc, const char *spec)
 void
 lex_spec_missing (struct lexer *lexer, const char *sbc, const char *spec)
 {
-  lex_error (lexer, _("Required %s specification missing from %s subcommand"),
-             sbc, spec);
+  lex_error (lexer, _("Required %s specification missing from %s subcommand."),
+             spec, sbc);
 }
 
 /* Prints a syntax error message for the span of tokens with offsets OFS0
    through OFS1, inclusive, within the current command in LEXER, adding message
    MESSAGE (if non-null) with the given ARGS. */
 void
-lex_ofs_error_valist (struct lexer *lexer, int ofs0, int ofs1,
-                      const char *format, va_list args)
+lex_ofs_msg_valist (struct lexer *lexer, enum msg_class class,
+                    int ofs0, int ofs1, const char *format, va_list args)
 {
-  struct lex_source *src = lex_source__ (lexer);
-
-  if (src != NULL)
-    lex_source_error_valist (src, ofs0, ofs1, format, args);
-  else
-    {
-      struct string s;
-
-      ds_init_empty (&s);
-      ds_put_format (&s, _("Syntax error at end of input"));
-      if (format != NULL)
-        {
-          ds_put_cstr (&s, ": ");
-          ds_put_vformat (&s, format, args);
-        }
-      if (ds_last (&s) != '.')
-        ds_put_byte (&s, '.');
-      msg (SE, "%s", ds_cstr (&s));
-      ds_destroy (&s);
-    }
+  lex_source_msg_valist (lex_source__ (lexer), class, ofs0, ofs1, format, args);
 }
 
 /* Checks that we're at end of command.
@@ -643,7 +670,7 @@ lex_end_of_command (struct lexer *lexer)
 {
   if (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_STOP)
     {
-      lex_error (lexer, _("expecting end of command"));
+      lex_error (lexer, _("Syntax error expecting end of command."));
       return CMD_FAILURE;
     }
   else
@@ -845,7 +872,7 @@ lex_force_string (struct lexer *lexer)
     return true;
   else
     {
-      lex_error (lexer, _("expecting string"));
+      lex_error (lexer, _("Syntax error expecting string."));
       return false;
     }
 }
@@ -874,7 +901,7 @@ lex_force_int (struct lexer *lexer)
     return true;
   else
     {
-      lex_error (lexer, _("expecting integer"));
+      lex_error (lexer, _("Syntax error expecting integer."));
       return false;
     }
 }
@@ -901,23 +928,25 @@ lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
       /* Weird, maybe a bug in the caller.  Just report that we needed an
          integer. */
       if (name)
-        lex_error (lexer, _("Integer expected for %s."), name);
+        lex_error (lexer, _("Syntax error expecting integer for %s."), name);
       else
-        lex_error (lexer, _("Integer expected."));
+        lex_error (lexer, _("Syntax error expecting integer."));
     }
   else if (min == max)
     {
       if (name)
-        lex_error (lexer, _("Expected %ld for %s."), min, name);
+        lex_error (lexer, _("Syntax error expecting %ld for %s."), min, name);
       else
-        lex_error (lexer, _("Expected %ld."), min);
+        lex_error (lexer, _("Syntax error expecting %ld."), min);
     }
   else if (min + 1 == max)
     {
       if (name)
-        lex_error (lexer, _("Expected %ld or %ld for %s."), min, min + 1, name);
+        lex_error (lexer, _("Syntax error expecting %ld or %ld for %s."),
+                   min, min + 1, name);
       else
-        lex_error (lexer, _("Expected %ld or %ld."), min, min + 1);
+        lex_error (lexer, _("Syntax error expecting %ld or %ld."),
+                   min, min + 1);
     }
   else
     {
@@ -928,10 +957,12 @@ lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
         {
           if (name)
             lex_error (lexer,
-                       _("Expected integer between %ld and %ld for %s."),
+                       _("Syntax error expecting integer "
+                         "between %ld and %ld for %s."),
                        min, max, name);
           else
-            lex_error (lexer, _("Expected integer between %ld and %ld."),
+            lex_error (lexer, _("Syntax error expecting integer "
+                                "between %ld and %ld."),
                        min, max);
         }
       else if (report_lower_bound)
@@ -939,44 +970,53 @@ lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
           if (min == 0)
             {
               if (name)
-                lex_error (lexer, _("Expected non-negative integer for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative integer for %s."),
                            name);
               else
-                lex_error (lexer, _("Expected non-negative integer."));
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative integer."));
             }
           else if (min == 1)
             {
               if (name)
-                lex_error (lexer, _("Expected positive integer for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "positive integer for %s."),
                            name);
               else
-                lex_error (lexer, _("Expected positive integer."));
+                lex_error (lexer, _("Syntax error expecting "
+                                    "positive integer."));
             }
           else
             {
               if (name)
-                lex_error (lexer, _("Expected integer %ld or greater for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "integer %ld or greater for %s."),
                            min, name);
               else
-                lex_error (lexer, _("Expected integer %ld or greater."), min);
+                lex_error (lexer, _("Syntax error expecting "
+                                    "integer %ld or greater."), min);
             }
         }
       else if (report_upper_bound)
         {
           if (name)
             lex_error (lexer,
-                       _("Expected integer less than or equal to %ld for %s."),
+                       _("Syntax error expecting integer less than or equal "
+                         "to %ld for %s."),
                        max, name);
           else
-            lex_error (lexer, _("Expected integer less than or equal to %ld."),
+            lex_error (lexer, _("Syntax error expecting integer less than or "
+                                "equal to %ld."),
                        max);
         }
       else
         {
           if (name)
-            lex_error (lexer, _("Integer expected for %s."), name);
+            lex_error (lexer, _("Syntax error expecting integer for %s."),
+                       name);
           else
-            lex_error (lexer, _("Integer expected."));
+            lex_error (lexer, _("Syntax error expecting integer."));
         }
     }
   return false;
@@ -990,7 +1030,7 @@ lex_force_num (struct lexer *lexer)
   if (lex_is_number (lexer))
     return true;
 
-  lex_error (lexer, _("expecting number"));
+  lex_error (lexer, _("Syntax error expecting number."));
   return false;
 }
 
@@ -1012,16 +1052,17 @@ lex_force_num_range_closed (struct lexer *lexer, const char *name,
       /* Weird, maybe a bug in the caller.  Just report that we needed an
          number. */
       if (name)
-        lex_error (lexer, _("Number expected for %s."), name);
+        lex_error (lexer, _("Syntax error expecting number for %s."), name);
       else
-        lex_error (lexer, _("Number expected."));
+        lex_error (lexer, _("Syntax error expecting number."));
     }
   else if (min == max)
     {
       if (name)
-        lex_error (lexer, _("Expected %g for %s."), min, name);
+        lex_error (lexer, _("Syntax error expecting number %g for %s."),
+                   min, name);
       else
-        lex_error (lexer, _("Expected %g."), min);
+        lex_error (lexer, _("Syntax error expecting number %g."), min);
     }
   else
     {
@@ -1032,10 +1073,12 @@ lex_force_num_range_closed (struct lexer *lexer, const char *name,
         {
           if (name)
             lex_error (lexer,
-                       _("Expected number between %g and %g for %s."),
+                       _("Syntax error expecting number "
+                         "between %g and %g for %s."),
                        min, max, name);
           else
-            lex_error (lexer, _("Expected number between %g and %g."),
+            lex_error (lexer, _("Syntax error expecting number "
+                                "between %g and %g."),
                        min, max);
         }
       else if (report_lower_bound)
@@ -1043,36 +1086,42 @@ lex_force_num_range_closed (struct lexer *lexer, const char *name,
           if (min == 0)
             {
               if (name)
-                lex_error (lexer, _("Expected non-negative number for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative number for %s."),
                            name);
               else
-                lex_error (lexer, _("Expected non-negative number."));
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative number."));
             }
           else
             {
               if (name)
-                lex_error (lexer, _("Expected number %g or greater for %s."),
+                lex_error (lexer, _("Syntax error expecting number "
+                                    "%g or greater for %s."),
                            min, name);
               else
-                lex_error (lexer, _("Expected number %g or greater."), min);
+                lex_error (lexer, _("Syntax error expecting number "
+                                    "%g or greater."), min);
             }
         }
       else if (report_upper_bound)
         {
           if (name)
             lex_error (lexer,
-                       _("Expected number less than or equal to %g for %s."),
+                       _("Syntax error expecting number "
+                         "less than or equal to %g for %s."),
                        max, name);
           else
-            lex_error (lexer, _("Expected number less than or equal to %g."),
+            lex_error (lexer, _("Syntax error expecting number "
+                                "less than or equal to %g."),
                        max);
         }
       else
         {
           if (name)
-            lex_error (lexer, _("Number expected for %s."), name);
+            lex_error (lexer, _("Syntax error expecting number for %s."), name);
           else
-            lex_error (lexer, _("Number expected."));
+            lex_error (lexer, _("Syntax error expecting number."));
         }
     }
   return false;
@@ -1096,9 +1145,9 @@ lex_force_num_range_halfopen (struct lexer *lexer, const char *name,
       /* Weird, maybe a bug in the caller.  Just report that we needed an
          number. */
       if (name)
-        lex_error (lexer, _("Number expected for %s."), name);
+        lex_error (lexer, _("Syntax error expecting number for %s."), name);
       else
-        lex_error (lexer, _("Number expected."));
+        lex_error (lexer, _("Syntax error expecting number."));
     }
   else
     {
@@ -1108,10 +1157,11 @@ lex_force_num_range_halfopen (struct lexer *lexer, const char *name,
       if (report_lower_bound && report_upper_bound)
         {
           if (name)
-            lex_error (lexer, _("Expected number in [%g,%g) for %s."),
+            lex_error (lexer, _("Syntax error expecting number "
+                                "in [%g,%g) for %s."),
                        min, max, name);
           else
-            lex_error (lexer, _("Expected number in [%g,%g)."),
+            lex_error (lexer, _("Syntax error expecting number in [%g,%g)."),
                        min, max);
         }
       else if (report_lower_bound)
@@ -1119,40 +1169,46 @@ lex_force_num_range_halfopen (struct lexer *lexer, const char *name,
           if (min == 0)
             {
               if (name)
-                lex_error (lexer, _("Expected non-negative number for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative number for %s."),
                            name);
               else
-                lex_error (lexer, _("Expected non-negative number."));
+                lex_error (lexer, _("Syntax error expecting "
+                                    "non-negative number."));
             }
           else
             {
               if (name)
-                lex_error (lexer, _("Expected number %g or greater for %s."),
+                lex_error (lexer, _("Syntax error expecting "
+                                    "number %g or greater for %s."),
                            min, name);
               else
-                lex_error (lexer, _("Expected number %g or greater."), min);
+                lex_error (lexer, _("Syntax error expecting "
+                                    "number %g or greater."), min);
             }
         }
       else if (report_upper_bound)
         {
           if (name)
             lex_error (lexer,
-                       _("Expected number less than %g for %s."), max, name);
+                       _("Syntax error expecting "
+                         "number less than %g for %s."), max, name);
           else
-            lex_error (lexer, _("Expected number less than %g."), max);
+            lex_error (lexer, _("Syntax error expecting "
+                                "number less than %g."), max);
         }
       else
         {
           if (name)
-            lex_error (lexer, _("Number expected for %s."), name);
+            lex_error (lexer, _("Syntax error expecting number for %s."), name);
           else
-            lex_error (lexer, _("Number expected."));
+            lex_error (lexer, _("Syntax error expecting number."));
         }
     }
   return false;
 }
 
-/* If the current token is an number in the open range (MIN,MAX], does
+/* If the current token is an number in the open range (MIN,MAX), does
    nothing and returns true.  Otherwise, reports an error and returns false.
    If NAME is nonnull, then it is used in the error message. */
 bool
@@ -1170,9 +1226,9 @@ lex_force_num_range_open (struct lexer *lexer, const char *name,
       /* Weird, maybe a bug in the caller.  Just report that we needed an
          number. */
       if (name)
-        lex_error (lexer, _("Number expected for %s."), name);
+        lex_error (lexer, _("Syntax error expecting number for %s."), name);
       else
-        lex_error (lexer, _("Number expected."));
+        lex_error (lexer, _("Syntax error expecting number."));
     }
   else
     {
@@ -1182,43 +1238,52 @@ lex_force_num_range_open (struct lexer *lexer, const char *name,
       if (report_lower_bound && report_upper_bound)
         {
           if (name)
-            lex_error (lexer, _("Expected number in (%g,%g) for %s."),
+            lex_error (lexer, _("Syntax error expecting number "
+                                "in (%g,%g) for %s."),
                        min, max, name);
           else
-            lex_error (lexer, _("Expected number in (%g,%g)."), min, max);
+            lex_error (lexer, _("Syntax error expecting number "
+                                "in (%g,%g)."), min, max);
         }
       else if (report_lower_bound)
         {
           if (min == 0)
             {
               if (name)
-                lex_error (lexer, _("Expected positive number for %s."), name);
+                lex_error (lexer, _("Syntax error expecting "
+                                    "positive number for %s."), name);
               else
-                lex_error (lexer, _("Expected positive number."));
+                lex_error (lexer, _("Syntax error expecting "
+                                    "positive number."));
             }
           else
             {
               if (name)
-                lex_error (lexer, _("Expected number greater than %g for %s."),
+                lex_error (lexer, _("Syntax error expecting number "
+                                    "greater than %g for %s."),
                            min, name);
               else
-                lex_error (lexer, _("Expected number greater than %g."), min);
+                lex_error (lexer, _("Syntax error expecting number "
+                                    "greater than %g."), min);
             }
         }
       else if (report_upper_bound)
         {
           if (name)
-            lex_error (lexer, _("Expected number less than %g for %s."),
+            lex_error (lexer, _("Syntax error expecting number "
+                                "less than %g for %s."),
                        max, name);
           else
-            lex_error (lexer, _("Expected number less than %g."), max);
+            lex_error (lexer, _("Syntax error expecting number "
+                                "less than %g."), max);
         }
       else
         {
           if (name)
-            lex_error (lexer, _("Number expected for %s."), name);
+            lex_error (lexer, _("Syntax error expecting number "
+                                "for %s."), name);
           else
-            lex_error (lexer, _("Number expected."));
+            lex_error (lexer, _("Syntax error expecting number."));
         }
     }
   return false;
@@ -1232,7 +1297,7 @@ lex_force_id (struct lexer *lexer)
   if (lex_token (lexer) == T_ID)
     return true;
 
-  lex_error (lexer, _("expecting identifier"));
+  lex_error (lexer, _("Syntax error expecting identifier."));
   return false;
 }
 \f
@@ -1546,7 +1611,7 @@ lex_tokens_match (const struct token *actual, const struct token *expected)
 }
 
 static size_t
-lex_at_phrase__ (struct lexer *lexer, const char *s)
+lex_ofs_at_phrase__ (struct lexer *lexer, int ofs, const char *s)
 {
   struct string_lexer slex;
   struct token token;
@@ -1555,7 +1620,7 @@ lex_at_phrase__ (struct lexer *lexer, const char *s)
   string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE, true);
   while (string_lexer_next (&slex, &token))
     {
-      bool match = lex_tokens_match (lex_next (lexer, i++), &token);
+      bool match = lex_tokens_match (lex_ofs_token (lexer, ofs + i++), &token);
       token_uninit (&token);
       if (!match)
         return 0;
@@ -1572,7 +1637,7 @@ lex_at_phrase__ (struct lexer *lexer, const char *s)
 bool
 lex_at_phrase (struct lexer *lexer, const char *s)
 {
-  return lex_at_phrase__ (lexer, s) > 0;
+  return lex_ofs_at_phrase__ (lexer, lex_ofs (lexer), s) > 0;
 }
 
 /* If LEXER is positioned at the sequence of tokens that may be parsed from S,
@@ -1584,7 +1649,7 @@ lex_at_phrase (struct lexer *lexer, const char *s)
 bool
 lex_match_phrase (struct lexer *lexer, const char *s)
 {
-  size_t n = lex_at_phrase__ (lexer, s);
+  size_t n = lex_ofs_at_phrase__ (lexer, lex_ofs (lexer), s);
   if (n > 0)
     lex_get_n (lexer, n);
   return n > 0;
@@ -1653,6 +1718,7 @@ lex_token_location (const struct lex_source *src,
     .file_name = intern_new_if_nonnull (src->reader->file_name),
     .start = lex_token_start_point (src, t0),
     .end = lex_token_end_point (src, t1),
+    .src = CONST_CAST (struct lex_source *, src),
   };
 }
 
@@ -1920,63 +1986,39 @@ lex_source_get_macro_call (struct lex_source *src, int ofs0, int ofs1)
 }
 
 static void
-lex_source_error_valist (struct lex_source *src, int ofs0, int ofs1,
-                         const char *format, va_list args)
+lex_source_msg_valist (struct lex_source *src, enum msg_class class,
+                       int ofs0, int ofs1, const char *format, va_list args)
 {
-  const struct lex_token *token;
-  struct string s;
-
-  ds_init_empty (&s);
+  struct string s = DS_EMPTY_INITIALIZER;
 
-  token = lex_source_ofs__ (src, ofs0);
-  if (token->token.type == T_ENDCMD)
-    ds_put_cstr (&s, _("Syntax error at end of command"));
-  else
+  if (src)
     {
-      /* Get the syntax that caused the error. */
-      char *raw_syntax = lex_source_syntax__ (src, ofs0, ofs1);
-      char syntax[64];
-      str_ellipsize (ss_cstr (raw_syntax), syntax, sizeof syntax);
-      free (raw_syntax);
-
       /* Get the macro call(s) that expanded to the syntax that caused the
          error. */
       char call[64];
       str_ellipsize (lex_source_get_macro_call (src, ofs0, ofs1),
                      call, sizeof call);
-
-      if (syntax[0])
-        {
-          if (call[0])
-            ds_put_format (&s,
-                           _("Syntax error at `%s' (in expansion of `%s')"),
-                           syntax, call);
-          else
-            ds_put_format (&s, _("Syntax error at `%s'"), syntax);
-        }
-      else
-        {
-          if (call[0])
-            ds_put_format (&s, _("Syntax error in syntax expanded from `%s'"),
-                           call);
-          else
-            ds_put_cstr (&s, _("Syntax error"));
-        }
+      if (call[0])
+        ds_put_format (&s, _("In syntax expanded from `%s'"), call);
     }
+  else
+    ds_put_cstr (&s, _("At end of input"));
 
+  if (!ds_is_empty (&s))
+    ds_put_cstr (&s, ": ");
   if (format)
-    {
-      ds_put_cstr (&s, ": ");
-      ds_put_vformat (&s, format, args);
-    }
+    ds_put_vformat (&s, format, args);
+  else
+    ds_put_cstr (&s, _("Syntax error."));
+
   if (ds_last (&s) != '.')
     ds_put_byte (&s, '.');
 
   struct msg *m = xmalloc (sizeof *m);
   *m = (struct msg) {
-    .category = MSG_C_SYNTAX,
-    .severity = MSG_S_ERROR,
-    .location = lex_source_get_location (src, ofs0, ofs1),
+    .category = msg_class_to_category (class),
+    .severity = msg_class_to_severity (class),
+    .location = src ? lex_source_get_location (src, ofs0, ofs1) : NULL,
     .text = ds_steal_cstr (&s),
   };
   msg_emit (m);
@@ -1990,8 +2032,7 @@ lex_get_error (struct lex_source *src, const struct lex_token *token)
                  syntax, sizeof syntax);
 
   struct string s = DS_EMPTY_INITIALIZER;
-  ds_put_format (&s, _("Syntax error at `%s'"), syntax);
-  ds_put_format (&s, ": %s", token->token.string.string);
+  ds_put_cstr (&s, token->token.string.string);
 
   struct msg *m = xmalloc (sizeof *m);
   *m = (struct msg) {
@@ -2374,7 +2415,7 @@ lex_set_message_handler (struct lexer *lexer,
   msg_set_handler (&msg_handler);
 }
 
-void
+struct lex_source *
 lex_source_ref (const struct lex_source *src_)
 {
   struct lex_source *src = CONST_CAST (struct lex_source *, src_);
@@ -2383,6 +2424,7 @@ lex_source_ref (const struct lex_source *src_)
       assert (src->n_refs > 0);
       src->n_refs++;
     }
+  return src;
 }
 
 void
@@ -2590,6 +2632,13 @@ lex_source_get_line (const struct lex_source *src, int line)
     return ss_empty ();
 
   size_t ofs = src->lines[line - 1];
-  size_t end = line >= src->n_lines ? src->length : src->lines[line];
+  size_t end;
+  if (line < src->n_lines)
+    end = src->lines[line];
+  else
+    {
+      const char *newline = memchr (src->buffer + ofs, '\n', src->length - ofs);
+      end = newline ? newline - src->buffer : src->length;
+    }
   return ss_buffer (&src->buffer[ofs], end - ofs);
 }
index ed4711213a7f64ab2bb3a119d870a356abb9e852..c00f70646d56a7552f3cc8979de540532d2f3570 100644 (file)
@@ -174,12 +174,25 @@ const char *lex_get_file_name (const struct lexer *);
 struct msg_location *lex_get_location (const struct lexer *, int n0, int n1);
 const char *lex_get_encoding (const struct lexer *);
 
-/* Issuing errors. */
+/* Issuing errors and warnings. */
 void lex_error (struct lexer *, const char *, ...) PRINTF_FORMAT (2, 3);
 void lex_next_error (struct lexer *, int n0, int n1, const char *, ...)
   PRINTF_FORMAT (4, 5);
 void lex_ofs_error (struct lexer *, int ofs0, int ofs1, const char *, ...)
   PRINTF_FORMAT (4, 5);
+
+void lex_msg (struct lexer *, enum msg_class, const char *, ...)
+  PRINTF_FORMAT (3, 4);
+void lex_next_msg (struct lexer *, enum msg_class, int n0, int n1,
+                   const char *, ...)
+  PRINTF_FORMAT (5, 6);
+void lex_ofs_msg (struct lexer *, enum msg_class, int ofs0, int ofs1,
+                  const char *, ...)
+  PRINTF_FORMAT (5, 6);
+void lex_ofs_msg_valist (struct lexer *lexer, enum msg_class,
+                         int ofs0, int ofs1, const char *format, va_list)
+  PRINTF_FORMAT (5, 0);
+
 int lex_end_of_command (struct lexer *);
 
 void lex_error_expecting (struct lexer *, ...) SENTINEL(0);
@@ -188,7 +201,7 @@ void lex_error_expecting (struct lexer *, ...) SENTINEL(0);
 void lex_error_expecting_valist (struct lexer *, va_list);
 void lex_error_expecting_array (struct lexer *, const char **, size_t n);
 
-void lex_sbc_only_once (const char *);
+void lex_sbc_only_once (struct lexer *, const char *);
 void lex_sbc_missing (const char *);
 
 void lex_spec_only_once (struct lexer *, const char *subcommand,
@@ -196,12 +209,6 @@ void lex_spec_only_once (struct lexer *, const char *subcommand,
 void lex_spec_missing (struct lexer *, const char *subcommand,
                        const char *specification);
 
-void lex_error_valist (struct lexer *, const char *, va_list)
-  PRINTF_FORMAT (2, 0);
-void lex_ofs_error_valist (struct lexer *lexer, int ofs0, int ofs1,
-                           const char *format, va_list)
-  PRINTF_FORMAT (4, 0);
-
 /* Error handling. */
 enum segmenter_mode lex_get_syntax_mode (const struct lexer *);
 enum lex_error_mode lex_get_error_mode (const struct lexer *);
@@ -213,7 +220,7 @@ void lex_discard_noninteractive (struct lexer *);
 void lex_set_message_handler (struct lexer *,
                               void (*output_msg) (const struct msg *,
                                                   struct lexer *));
-void lex_source_ref (const struct lex_source *);
+struct lex_source *lex_source_ref (const struct lex_source *);
 void lex_source_unref (struct lex_source *);
 struct substring lex_source_get_line (const struct lex_source *, int line);
 
index e5805f035670b33e7b6f62aea55b58a623818ad4..15ed9f12d22e9f11daebee0e6918e8dca778190f 100644 (file)
@@ -821,7 +821,7 @@ macro_call_create__ (const struct macro_set *macros,
 /* If TOKEN is the first token of a call to a macro in MACROS, create a new
    macro expander, initializes *MCP to it.  Returns 0 if more tokens are needed
    and should be added via macro_call_add() or 1 if the caller should next call
-   macro_call_get_expansion().
+   macro_call_expand().
 
    If TOKEN is not the first token of a macro call, returns -1 and sets *MCP to
    NULL. */
@@ -866,8 +866,8 @@ macro_call_destroy (struct macro_call *mc)
    Returns a positive number to indicate that the returned number of tokens
    invoke a macro.  The number returned might be less than the number of tokens
    added because it can take a few tokens of lookahead to determine whether the
-   macro invocation is finished.  The caller should call
-   macro_call_get_expansion() to obtain the expansion. */
+   macro invocation is finished.  The caller should call macro_call_expand() to
+   obtain the expansion. */
 int
 macro_call_add (struct macro_call *mc, const struct macro_token *mt,
                 const struct msg_location *loc)
index 23b4def274cc71191dd3d1018625766500b753eb..c91fa89da12474a7c8d115c9a60ab844691e468d 100644 (file)
@@ -48,6 +48,8 @@ bool
 parse_num_range (struct lexer *lexer,
                  double *x, double *y, const enum fmt_type *format)
 {
+  int start_ofs = lex_ofs (lexer);
+
   if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
     *x = LOWEST;
   else if (!parse_number (lexer, x, format))
@@ -63,15 +65,17 @@ parse_num_range (struct lexer *lexer,
       if (*y < *x)
         {
           double t;
-          msg (SW, _("The high end of the range (%.*g) is below the low end "
-                     "(%.*g).  The range will be treated as if reversed."),
-               DBL_DIG + 1, *y, DBL_DIG + 1, *x);
+          lex_ofs_msg (lexer, SW, start_ofs, lex_ofs (lexer) - 1,
+                       ("The high end of the range (%.*g) is below the low end "
+                        "(%.*g).  The range will be treated as if reversed."),
+                       DBL_DIG + 1, *y, DBL_DIG + 1, *x);
           t = *x;
           *x = *y;
           *y = t;
         }
       else if (*x == *y)
-        msg (SW, _("Ends of range are equal (%.*g)."), DBL_DIG + 1, *x);
+        lex_ofs_msg (lexer, SW, start_ofs, lex_ofs (lexer) - 1,
+                     _("Ends of range are equal (%.*g)."), DBL_DIG + 1, *x);
 
       return true;
     }
@@ -79,7 +83,9 @@ parse_num_range (struct lexer *lexer,
     {
       if (*x == LOWEST)
         {
-          msg (SE, _("%s or %s must be part of a range."), "LO", "LOWEST");
+          lex_next_msg (lexer, SW, -1, -1,
+                        _("%s or %s must be part of a range."),
+                         "LO", "LOWEST");
           return false;
         }
       *y = *x;
@@ -110,7 +116,8 @@ parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
       *x = v.f;
       if (*x == SYSMIS)
         {
-          msg (SE, _("System-missing value is not valid here."));
+          lex_next_error (lexer, -1, -1,
+                          _("System-missing value is not valid here."));
           return false;
         }
       return true;
index 14635a209eeb67692d9b6e2587877c3659d297d6..7d30a2ad9253f0f1cc81326f139199d9ab85fde8 100644 (file)
@@ -83,7 +83,7 @@ parse_vs_variable_idx (struct lexer *lexer, const struct var_set *vs,
 
   if (!is_vs_name_token (lexer, vs))
     {
-      lex_error (lexer, _("expecting variable name"));
+      lex_error (lexer, _("Syntax error expecting variable name."));
       return false;
     }
   else if (var_set_lookup_var_idx (vs, lex_tokcstr (lexer), idx))
@@ -93,7 +93,7 @@ parse_vs_variable_idx (struct lexer *lexer, const struct var_set *vs,
     }
   else
     {
-      msg (SE, _("%s is not a variable name."), lex_tokcstr (lexer));
+      lex_error (lexer, _("%s is not a variable name."), lex_tokcstr (lexer));
       return false;
     }
 }
@@ -195,37 +195,45 @@ parse_var_idx_class (struct lexer *lexer, const struct var_set *vs,
    PV_OPTS, which also affects what variables are allowed in
    appropriate ways. */
 static void
-add_variable (struct variable ***v, size_t *nv, size_t *mv,
+add_variable (struct lexer *lexer,
+              struct variable ***v, size_t *nv, size_t *mv,
               char *included, int pv_opts,
-              const struct var_set *vs, size_t idx)
+              const struct var_set *vs, size_t idx,
+              int start_ofs, int end_ofs)
 {
   struct variable *add = var_set_get_var (vs, idx);
   const char *add_name = var_get_name (add);
 
   if ((pv_opts & PV_NUMERIC) && !var_is_numeric (add))
-    msg (SW, _("%s is not a numeric variable.  It will not be "
-               "included in the variable list."), add_name);
+    lex_ofs_msg (lexer, SW, start_ofs, end_ofs,
+                 _("%s is not a numeric variable.  It will not be "
+                   "included in the variable list."), add_name);
   else if ((pv_opts & PV_STRING) && !var_is_alpha (add))
-    msg (SE, _("%s is not a string variable.  It will not be "
-               "included in the variable list."), add_name);
+    lex_ofs_error (lexer, start_ofs, end_ofs,
+                   _("%s is not a string variable.  It will not be "
+                     "included in the variable list."), add_name);
   else if ((pv_opts & PV_NO_SCRATCH)
            && dict_class_from_id (add_name) == DC_SCRATCH)
-    msg (SE, _("Scratch variables (such as %s) are not allowed "
-               "here."), add_name);
+    lex_ofs_error (lexer, start_ofs, end_ofs,
+                   _("Scratch variables (such as %s) are not allowed "
+                     "here."), add_name);
   else if ((pv_opts & (PV_SAME_TYPE | PV_SAME_WIDTH)) && *nv
            && var_get_type (add) != var_get_type ((*v)[0]))
-    msg (SE, _("%s and %s are not the same type.  All variables in "
-               "this variable list must be of the same type.  %s "
-               "will be omitted from the list."),
-         var_get_name ((*v)[0]), add_name, add_name);
+    lex_ofs_error (lexer, start_ofs, end_ofs,
+                 _("%s and %s are not the same type.  All variables in "
+                   "this variable list must be of the same type.  %s "
+                   "will be omitted from the list."),
+                 var_get_name ((*v)[0]), add_name, add_name);
   else if ((pv_opts & PV_SAME_WIDTH) && *nv
            && var_get_width (add) != var_get_width ((*v)[0]))
-    msg (SE, _("%s and %s are string variables with different widths.  "
-               "All variables in this variable list must have the "
-               "same width.  %s will be omitted from the list."),
-         var_get_name ((*v)[0]), add_name, add_name);
+    lex_ofs_error (lexer, start_ofs, end_ofs,
+                 _("%s and %s are string variables with different widths.  "
+                   "All variables in this variable list must have the "
+                   "same width.  %s will be omitted from the list."),
+                 var_get_name ((*v)[0]), add_name, add_name);
   else if ((pv_opts & PV_NO_DUPLICATE) && included && included[idx])
-    msg (SE, _("Variable %s appears twice in variable list."), add_name);
+    lex_ofs_error (lexer, start_ofs, end_ofs,
+                   _("Variable %s appears twice in variable list."), add_name);
   else if ((pv_opts & PV_DUPLICATE) || !included || !included[idx])
     {
       if (*nv >= *mv)
@@ -245,16 +253,19 @@ add_variable (struct variable ***v, size_t *nv, size_t *mv,
    duplicates if indicated by PV_OPTS, which also affects what
    variables are allowed in appropriate ways. */
 static void
-add_variables (struct variable ***v, size_t *nv, size_t *mv, char *included,
+add_variables (struct lexer *lexer,
+               struct variable ***v, size_t *nv, size_t *mv, char *included,
                int pv_opts,
                const struct var_set *vs, int first_idx, int last_idx,
-               enum dict_class class)
+               enum dict_class class,
+               int start_ofs, int end_ofs)
 {
   size_t i;
 
   for (i = first_idx; i <= last_idx; i++)
     if (dict_class_from_id (var_get_name (var_set_get_var (vs, i))) == class)
-      add_variable (v, nv, mv, included, pv_opts, vs, i);
+      add_variable (lexer, v, nv, mv, included, pv_opts, vs, i,
+                    start_ofs, end_ofs);
 }
 
 /* Note that if parse_variables() returns false, *v is free()'d.
@@ -309,9 +320,11 @@ parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
 
   do
     {
+      int start_ofs = lex_ofs (lexer);
       if (lex_match (lexer, T_ALL))
-        add_variables (v, nv, &mv, included, pv_opts,
-                       vs, 0, var_set_get_n (vs) - 1, DC_ORDINARY);
+        add_variables (lexer, v, nv, &mv, included, pv_opts,
+                       vs, 0, var_set_get_n (vs) - 1, DC_ORDINARY,
+                       start_ofs, start_ofs);
       else
         {
           enum dict_class class;
@@ -321,7 +334,8 @@ parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
             goto fail;
 
           if (!lex_match (lexer, T_TO))
-            add_variable (v, nv, &mv, included, pv_opts, vs, first_idx);
+            add_variable (lexer, v, nv, &mv, included, pv_opts, vs, first_idx,
+                          start_ofs, start_ofs);
           else
             {
               size_t last_idx;
@@ -331,6 +345,8 @@ parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
               if (!parse_var_idx_class (lexer, vs, &last_idx, &last_class))
                 goto fail;
 
+              int end_ofs = lex_ofs (lexer) - 1;
+
               first_var = var_set_get_var (vs, first_idx);
               last_var = var_set_get_var (vs, last_idx);
 
@@ -338,27 +354,56 @@ parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
                 {
                   const char *first_name = var_get_name (first_var);
                   const char *last_name = var_get_name (last_var);
-                  msg (SE, _("%s TO %s is not valid syntax since %s "
-                             "precedes %s in the dictionary."),
-                       first_name, last_name, first_name, last_name);
+                  lex_ofs_error (lexer, start_ofs, end_ofs,
+                                 _("%s TO %s is not valid syntax since %s "
+                                   "precedes %s in the dictionary."),
+                                 first_name, last_name, first_name, last_name);
                   goto fail;
                 }
 
               if (class != last_class)
                 {
-                  msg (SE, _("When using the TO keyword to specify several "
-                             "variables, both variables must be from "
-                             "the same variable dictionaries, of either "
-                             "ordinary, scratch, or system variables.  "
-                             "%s is a %s variable, whereas %s is %s."),
-                       var_get_name (first_var), dict_class_to_name (class),
-                       var_get_name (last_var),
-                       dict_class_to_name (last_class));
+                  lex_ofs_error (lexer, start_ofs, end_ofs,
+                                 _("With the syntax <a> TO <b>, variables <a> "
+                                   "and <b> must be both regular variables "
+                                   "or both scratch variables."));
+                  struct pair
+                    {
+                      const char *name;
+                      enum dict_class class;
+                      int ofs;
+                    }
+                  pairs[2] = {
+                    { var_get_name (first_var), class, start_ofs },
+                    { var_get_name (last_var), last_class, end_ofs },
+                  };
+                  for (size_t i = 0; i < 2; i++)
+                    switch (pairs[i].class)
+                      {
+                      case DC_ORDINARY:
+                        lex_ofs_msg (lexer, SN, pairs[i].ofs, pairs[i].ofs,
+                                     _("%s is a regular variable."),
+                                     pairs[i].name);
+                        break;
+
+                      case DC_SCRATCH:
+                        lex_ofs_msg (lexer, SN, pairs[i].ofs, pairs[i].ofs,
+                                     _("%s is a scratch variable."),
+                                     pairs[i].name);
+                        break;
+
+                      case DC_SYSTEM:
+                        lex_ofs_msg (lexer, SN, pairs[i].ofs, pairs[i].ofs,
+                                     _("%s is a system variable."),
+                                     pairs[i].name);
+                        break;
+                      }
                   goto fail;
                 }
 
-              add_variables (v, nv, &mv, included, pv_opts,
-                             vs, first_idx, last_idx, class);
+              add_variables (lexer, v, nv, &mv, included, pv_opts,
+                             vs, first_idx, last_idx, class,
+                             start_ofs, lex_ofs (lexer) - 1);
             }
         }
 
@@ -389,7 +434,7 @@ parse_DATA_LIST_var (struct lexer *lexer, const struct dictionary *d)
 {
   if (!is_dict_name_token (lexer, d))
     {
-      lex_error (lexer, "expecting variable name");
+      lex_error (lexer, ("Syntax error expecting variable name."));
       return NULL;
     }
   if (!dict_id_is_valid (d, lex_tokcstr (lexer), true))
@@ -406,7 +451,7 @@ parse_DATA_LIST_var (struct lexer *lexer, const struct dictionary *d)
    the number of digits in the suffix into *N_DIGITSP, and returns the number
    of bytes in the root.  On failure, returns 0. */
 static int
-extract_numeric_suffix (const char *name,
+extract_numeric_suffix (struct lexer *lexer, int ofs, const char *name,
                         unsigned long int *numberp, int *n_digitsp)
 {
   size_t root_len, n_digits;
@@ -421,16 +466,18 @@ extract_numeric_suffix (const char *name,
 
   if (n_digits == 0)
     {
-      msg (SE, _("`%s' cannot be used with TO because it does not end in "
-                 "a digit."), name);
+      lex_ofs_error (lexer, ofs, ofs,
+                     _("`%s' cannot be used with TO because it does not end in "
+                       "a digit."), name);
       return 0;
     }
 
   *numberp = strtoull (name + root_len, NULL, 10);
   if (*numberp == ULONG_MAX)
     {
-      msg (SE, _("Numeric suffix on `%s' is larger than supported with TO."),
-           name);
+      lex_ofs_error (lexer, ofs, ofs,
+                     _("Numeric suffix on `%s' is larger than supported with TO."),
+                     name);
       return 0;
     }
   *n_digitsp = n_digits;
@@ -438,14 +485,15 @@ extract_numeric_suffix (const char *name,
 }
 
 static bool
-add_var_name (char *name,
+add_var_name (struct lexer *lexer, int start_ofs, int end_ofs, char *name,
               char ***names, size_t *n_vars, size_t *allocated_vars,
               struct stringi_set *set, int pv_opts)
 {
   if (pv_opts & PV_NO_DUPLICATE && !stringi_set_insert (set, name))
     {
-      msg (SE, _("Variable %s appears twice in variable list."),
-           name);
+      lex_ofs_error (lexer, start_ofs, end_ofs,
+                     _("Variable %s appears twice in variable list."),
+                     name);
       return false;
     }
 
@@ -497,12 +545,14 @@ parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
 
   do
     {
+      int start_ofs = lex_ofs (lexer);
       name1 = parse_DATA_LIST_var (lexer, dict);
       if (!name1)
         goto exit;
       if (dict_class_from_id (name1) == DC_SCRATCH && pv_opts & PV_NO_SCRATCH)
        {
-         msg (SE, _("Scratch variables not allowed here."));
+         lex_ofs_error (lexer, start_ofs, start_ofs,
+                         _("Scratch variables not allowed here."));
          goto exit;
        }
       if (lex_match (lexer, T_TO))
@@ -515,23 +565,28 @@ parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
           name2 = parse_DATA_LIST_var (lexer, dict);
           if (!name2)
             goto exit;
+          int end_ofs = lex_ofs (lexer) - 1;
 
-          root_len1 = extract_numeric_suffix (name1, &num1, &n_digits1);
+          root_len1 = extract_numeric_suffix (lexer, start_ofs,
+                                              name1, &num1, &n_digits1);
           if (root_len1 == 0)
             goto exit;
 
-          root_len2 = extract_numeric_suffix (name2, &num2, &n_digits2);
+          root_len2 = extract_numeric_suffix (lexer, end_ofs,
+                                              name2, &num2, &n_digits2);
           if (root_len2 == 0)
            goto exit;
 
          if (root_len1 != root_len2 || memcasecmp (name1, name2, root_len1))
            {
-             msg (SE, _("Prefixes don't match in use of TO convention."));
+             lex_ofs_error (lexer, start_ofs, end_ofs,
+                             _("Prefixes don't match in use of TO convention."));
              goto exit;
            }
          if (num1 > num2)
            {
-             msg (SE, _("Bad bounds in use of TO convention."));
+             lex_ofs_error (lexer, start_ofs, end_ofs,
+                             _("Bad bounds in use of TO convention."));
              goto exit;
            }
 
@@ -540,7 +595,8 @@ parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
               char *name = xasprintf ("%.*s%0*lu",
                                       root_len1, name1,
                                       n_digits1, number);
-              if (!add_var_name (name, &names, &n_vars, &allocated_vars,
+              if (!add_var_name (lexer, start_ofs, end_ofs,
+                                 name, &names, &n_vars, &allocated_vars,
                                  &set, pv_opts))
                 {
                   free (name);
@@ -555,7 +611,8 @@ parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
        }
       else
        {
-          if (!add_var_name (name1, &names, &n_vars, &allocated_vars,
+          if (!add_var_name (lexer, start_ofs, start_ofs,
+                             name1, &names, &n_vars, &allocated_vars,
                              &set, pv_opts))
             goto exit;
           name1 = NULL;
@@ -721,7 +778,7 @@ var_syntax_parse (struct lexer *lexer, struct var_syntax **vs, size_t *n_vs)
 
   if (lex_token (lexer) != T_ID)
     {
-      lex_error (lexer, _("expecting variable name"));
+      lex_error (lexer, _("Syntax error expecting variable name."));
       goto error;
     }
 
@@ -731,20 +788,24 @@ var_syntax_parse (struct lexer *lexer, struct var_syntax **vs, size_t *n_vs)
       if (allocated_vs >= *n_vs)
         *vs = x2nrealloc (*vs, &allocated_vs, sizeof **vs);
       struct var_syntax *new = &(*vs)[(*n_vs)++];
-      *new = (struct var_syntax) { .first = ss_xstrdup (lex_tokss (lexer)) };
+      *new = (struct var_syntax) {
+        .first = ss_xstrdup (lex_tokss (lexer)),
+        .first_ofs = lex_ofs (lexer)
+      };
       lex_get (lexer);
 
       if (lex_match (lexer, T_TO))
         {
           if (lex_token (lexer) != T_ID)
             {
-              lex_error (lexer, _("expecting variable name"));
+              lex_error (lexer, _("Syntax error expecting variable name."));
               goto error;
             }
 
           new->last = ss_xstrdup (lex_tokss (lexer));
           lex_get (lexer);
         }
+      new->last_ofs = lex_ofs (lexer) - 1;
     }
   while (lex_token (lexer) == T_ID);
   return true;
@@ -761,9 +822,12 @@ error:
    array of pointers to variables and *N_VARS to the length of the array and
    returns true.  On error, sets *VARS to NULL and *N_VARS to 0.
 
+   The LEXER is just used for error messages.
+
    For the moment, only honors PV_NUMERIC in OPTS. */
 bool
-var_syntax_evaluate (const struct var_syntax *vs, size_t n_vs,
+var_syntax_evaluate (struct lexer *lexer,
+                     const struct var_syntax *vs, size_t n_vs,
                      const struct dictionary *dict,
                      struct variable ***vars, size_t *n_vars, int opts)
 {
@@ -775,19 +839,23 @@ var_syntax_evaluate (const struct var_syntax *vs, size_t n_vs,
   size_t allocated_vars = 0;
   for (size_t i = 0; i < n_vs; i++)
     {
+      int first_ofs = vs[i].first_ofs;
       struct variable *first = dict_lookup_var (dict, vs[i].first);
       if (!first)
         {
-          msg (SE, _("%s is not a variable name."), vs[i].first);
+          lex_ofs_error (lexer, first_ofs, first_ofs,
+                         _("%s is not a variable name."), vs[i].first);
           goto error;
         }
 
+      int last_ofs = vs[i].last_ofs;
       struct variable *last = (vs[i].last
                                ? dict_lookup_var (dict, vs[i].last)
                                : first);
       if (!last)
         {
-          msg (SE, _("%s is not a variable name."), vs[i].last);
+          lex_ofs_error (lexer, last_ofs, last_ofs,
+                         _("%s is not a variable name."), vs[i].last);
           goto error;
         }
 
@@ -795,10 +863,11 @@ var_syntax_evaluate (const struct var_syntax *vs, size_t n_vs,
       size_t last_idx = var_get_dict_index (last);
       if (last_idx < first_idx)
         {
-          msg (SE, _("%s TO %s is not valid syntax since %s "
-                     "precedes %s in the dictionary."),
-               vs[i].first, vs[i].last,
-               vs[i].first, vs[i].last);
+          lex_ofs_error (lexer, first_ofs, last_ofs,
+                         _("%s TO %s is not valid syntax since %s "
+                           "precedes %s in the dictionary."),
+                         vs[i].first, vs[i].last,
+                         vs[i].first, vs[i].last);
           goto error;
         }
 
@@ -807,7 +876,9 @@ var_syntax_evaluate (const struct var_syntax *vs, size_t n_vs,
           struct variable *v = dict_get_var (dict, j);
           if (opts & PV_NUMERIC && !var_is_numeric (v))
             {
-              msg (SW, _("%s is not a numeric variable."), var_get_name (v));
+              lex_ofs_error (lexer, first_ofs, last_ofs,
+                             _("%s is not a numeric variable."),
+                             var_get_name (v));
               goto error;
             }
 
index 66e8efab6c58cd5b17f5e2c0ed0778296f0959de..5815dc458b943fc1235fe52ef8fd0d345e9376a4 100644 (file)
@@ -82,11 +82,20 @@ struct var_syntax
   {
     char *first;                /* Always nonnull. */
     char *last;                 /* Nonnull for var ranges (e.g. "a TO b"). */
+
+    /* For error reporting.
+
+      This only works if var_syntax_parse() and var_syntax_evaluate() are
+      called while we're parsing the same source file.  That matches the
+      current use case in MATRIX; if that changes, then this will need to
+      switch to use struct msg_location instead. */
+    int first_ofs;
+    int last_ofs;
   };
 void var_syntax_destroy (struct var_syntax *, size_t n);
 
 bool var_syntax_parse (struct lexer *, struct var_syntax **, size_t *);
-bool var_syntax_evaluate (const struct var_syntax *, size_t,
+bool var_syntax_evaluate (struct lexer *, const struct var_syntax *, size_t,
                           const struct dictionary *,
                           struct variable ***, size_t *, int opts);
 
index 4151860e109bc4e1c8cf49af8f65789140e25d48..944ba7b92278173633df300f0e22f3f96401d85c 100644 (file)
@@ -445,7 +445,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       /* Get the name of the aggregation function. */
       if (lex_token (lexer) != T_ID)
        {
-         lex_error (lexer, _("expecting aggregation function"));
+         lex_error (lexer, _("Syntax error expecting aggregation function."));
          goto error;
        }
 
@@ -457,8 +457,8 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          break;
       if (NULL == function->name)
        {
-         msg (SE, _("Unknown aggregation function %s."),
-              ds_cstr (&function_name));
+         lex_error (lexer, _("Unknown aggregation function %s."),
+                     ds_cstr (&function_name));
          goto error;
        }
       ds_destroy (&function_name);
@@ -476,26 +476,28 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       else
         {
          /* Parse list of source variables. */
-         {
-           int pv_opts = PV_NO_SCRATCH;
-
-           if (func_index == SUM || func_index == MEAN || func_index == SD)
-             pv_opts |= PV_NUMERIC;
-           else if (function->n_args)
-             pv_opts |= PV_SAME_TYPE;
-
-           if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts))
-             goto error;
-         }
+          int pv_opts = PV_NO_SCRATCH;
+          if (func_index == SUM || func_index == MEAN || func_index == SD)
+            pv_opts |= PV_NUMERIC;
+          else if (function->n_args)
+            pv_opts |= PV_SAME_TYPE;
+
+          int vars_start_ofs = lex_ofs (lexer);
+          if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts))
+            goto error;
+          int vars_end_ofs = lex_ofs (lexer) - 1;
 
          /* Parse function arguments, for those functions that
             require arguments. */
+          int args_start_ofs = 0;
          if (function->n_args != 0)
            for (i = 0; i < function->n_args; i++)
              {
                int type;
 
                lex_match (lexer, T_COMMA);
+                if (i == 0)
+                  args_start_ofs = lex_ofs (lexer);
                if (lex_is_string (lexer))
                  {
                    arg[i].c = recode_string (dict_get_encoding (agr->dict),
@@ -510,21 +512,35 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
                  }
                 else
                   {
-                   msg (SE, _("Missing argument %zu to %s."),
-                         i + 1, function->name);
+                   lex_error (lexer, _("Missing argument %zu to %s."),
+                               i + 1, function->name);
                    goto error;
                  }
-
-               lex_get (lexer);
-
                if (type != var_get_type (src[0]))
                  {
                    msg (SE, _("Arguments to %s must be of same type as "
                               "source variables."),
                         function->name);
+                    if (type == VAL_NUMERIC)
+                      {
+                        lex_next_msg (lexer, SN, 0, 0,
+                                      _("The argument is numeric."));
+                        lex_ofs_msg (lexer, SN, vars_start_ofs, vars_end_ofs,
+                                     _("The variables have string type."));
+                      }
+                    else
+                      {
+                        lex_next_msg (lexer, SN, 0, 0,
+                                      _("The argument is a string."));
+                        lex_ofs_msg (lexer, SN, vars_start_ofs, vars_end_ofs,
+                                     _("The variables are numeric."));
+                      }
                    goto error;
                  }
+
+               lex_get (lexer);
              }
+          int args_end_ofs = lex_ofs (lexer) - 1;
 
          /* Trailing rparen. */
          if (!lex_force_match (lexer, T_RPAREN))
@@ -554,10 +570,11 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
               arg[0] = arg[1];
               arg[1] = t;
 
-              msg (SW, _("The value arguments passed to the %s function "
-                         "are out-of-order.  They will be treated as if "
-                         "they had been specified in the correct order."),
-                   function->name);
+              lex_ofs_msg (lexer, SW, args_start_ofs, args_end_ofs,
+                           _("The value arguments passed to the %s function "
+                             "are out of order.  They will be treated as if "
+                             "they had been specified in the correct order."),
+                           function->name);
             }
        }
 
@@ -674,7 +691,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          if (lex_token (lexer) == T_ENDCMD)
            return true;
 
-         lex_error (lexer, "expecting end of command");
+         lex_error (lexer, "Syntax error expecting end of command.");
          return false;
        }
       continue;
index 499f149d22a14c4003f2d2ae1a9a42d368202798..0cd81ac920826c651976e74d508591ac2dab2030 100644 (file)
@@ -203,7 +203,7 @@ cmd_autorecode (struct lexer *lexer, struct dataset *ds)
 
   if (lex_token (lexer) != T_ENDCMD)
     {
-      lex_error (lexer, _("expecting end of command"));
+      lex_error (lexer, _("Syntax error expecting end of command."));
       goto error;
     }
 
index bb63e982538b30a912e52ae94cfcce96c83a76b0..1b203083e0768b6f738c7c7e67e8d2b5c1a277c3 100644 (file)
@@ -305,6 +305,7 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
     .descending = false,
   };
   bool show_tables = true;
+  int exclude_ofs = 0;
   lex_match (lexer, T_SLASH);
   for (;;)
     {
@@ -316,6 +317,7 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
       else if (lex_match_id (lexer, "MISSING"))
         {
           lex_match (lexer, T_EQUALS);
+          exclude_ofs = lex_ofs (lexer);
           if (lex_match_id (lexer, "TABLE"))
             proc.exclude = MV_ANY;
           else if (lex_match_id (lexer, "INCLUDE"))
@@ -472,8 +474,9 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
   /* Missing values. */
   if (proc.mode == GENERAL && !proc.exclude)
     {
-      msg (SE, _("Missing mode %s not allowed in general mode.  "
-                "Assuming %s."), "REPORT", "MISSING=TABLE");
+      lex_ofs_error (lexer, exclude_ofs, exclude_ofs,
+                     _("Missing mode %s not allowed in general mode.  "
+                       "Assuming %s."), "REPORT", "MISSING=TABLE");
       proc.exclude = MV_ANY;
     }
 
@@ -656,7 +659,8 @@ parse_crosstabs_variables (struct lexer *lexer, struct dataset *ds,
 {
   if (proc->n_pivots)
     {
-      msg (SE, _("%s must be specified before %s."), "VARIABLES", "TABLES");
+      lex_next_error (lexer, -1, -1, _("%s must be specified before %s."),
+                      "VARIABLES", "TABLES");
       return false;
     }
 
index d0c0a8482b6da23897dba1ae8925d152353d7a57..514765962a99532ee5183035692304740bea744e 100644 (file)
@@ -295,7 +295,7 @@ parse_ctables_summary_function (struct lexer *lexer,
         }
     }
 
-  lex_error (lexer, _("Expecting summary function name."));
+  lex_error (lexer, _("Syntax error expecting summary function name."));
   return false;
 }
 
@@ -1602,8 +1602,7 @@ struct ctables_category
           };
       };
 
-    /* Source location.  This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
-       CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
+    /* Source location (sometimes NULL). */
     struct msg_location *location;
   };
 
@@ -2975,6 +2974,7 @@ struct ctables_table
     enum pivot_axis_type label_axis[PIVOT_N_AXES];
     enum pivot_axis_type clabels_from_axis;
     enum pivot_axis_type clabels_to_axis;
+    int clabels_start_ofs, clabels_end_ofs;
     const struct variable *clabels_example;
     struct hmap clabels_values_map;
     struct ctables_value **clabels_values;
@@ -4092,6 +4092,8 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
   bool show_totals = false;
   char *total_label = NULL;
   bool totals_before = false;
+  int key_start_ofs = 0;
+  int key_end_ofs = 0;
   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
     {
       if (!c->n_cats && lex_match_id (lexer, "ORDER"))
@@ -4109,7 +4111,7 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
         }
       else if (!c->n_cats && lex_match_id (lexer, "KEY"))
         {
-          int start_ofs = lex_ofs (lexer) - 1;
+          key_start_ofs = lex_ofs (lexer) - 1;
           lex_match (lexer, T_EQUALS);
           if (lex_match_id (lexer, "VALUE"))
             cat.type = CCT_VALUE;
@@ -4146,9 +4148,13 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
                   bool UNUSED b = lex_force_match (lexer, T_LPAREN);
                   goto error;
                 }
+            }
+          key_end_ofs = lex_ofs (lexer) - 1;
 
-              lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
-                              _("Data-dependent sorting is not implemented."));
+          if (cat.type == CCT_FUNCTION)
+            {
+              lex_ofs_error (lexer, key_start_ofs, key_end_ofs,
+                             _("Data-dependent sorting is not implemented."));
               goto error;
             }
         }
@@ -4219,6 +4225,9 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
 
   if (!c->n_cats)
     {
+      if (key_start_ofs)
+        cat.location = lex_ofs_location (lexer, key_start_ofs, key_end_ofs);
+
       if (c->n_cats >= allocated_cats)
         c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
       c->cats[c->n_cats++] = cat;
@@ -4893,15 +4902,13 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
 }
 
 static bool
-ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
+ctables_check_label_position (struct ctables_table *t, struct lexer *lexer,
+                              enum pivot_axis_type a)
 {
   enum pivot_axis_type label_pos = t->label_axis[a];
   if (label_pos == a)
     return true;
 
-  const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
-  const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
-
   const struct ctables_stack *stack = &t->stacks[a];
   if (!stack->n)
     return true;
@@ -4920,17 +4927,29 @@ ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
   for (size_t i = 0; i < c0->n_cats; i++)
     if (c0->cats[i].type == CCT_FUNCTION)
       {
-        msg (SE, _("%s=%s is not allowed with sorting based "
-                   "on a summary function."),
-             subcommand_name, pos_name);
+        msg (SE, _("Category labels may not be moved to another axis when "
+                   "sorting by a summary function."));
+        lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+                     _("This syntax moves category labels to another axis."));
+        msg_at (SN, c0->cats[i].location,
+                _("This syntax requests sorting by a summary function."));
         return false;
       }
-  if (n0->n - 1 == n0->scale_idx)
+
+  for (size_t i = 0; i < stack->n; i++)
     {
-      msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
-                 "but %s is a scale variable."),
-           subcommand_name, pos_name, var_get_name (v0));
-      return false;
+      const struct ctables_nest *ni = &stack->nests[i];
+      assert (ni->n > 0);
+      const struct variable *vi = ni->vars[ni->n - 1];
+      if (n0->n - 1 == ni->scale_idx)
+        {
+          msg (SE, _("To move category labels from one axis to another, "
+                     "the variables whose labels are to be moved must be "
+                     "categorical, but %s is scale."), var_get_name (vi));
+          lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+                       _("This syntax moves category labels to another axis."));
+          return false;
+        }
     }
 
   for (size_t i = 1; i < stack->n; i++)
@@ -4940,41 +4959,39 @@ ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
       const struct variable *vi = ni->vars[ni->n - 1];
       struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
 
-      if (ni->n - 1 == ni->scale_idx)
-        {
-          msg (SE, _("%s=%s requires the variables to be moved to be "
-                     "categorical, but %s is a scale variable."),
-               subcommand_name, pos_name, var_get_name (vi));
-          return false;
-        }
       if (var_get_width (v0) != var_get_width (vi))
         {
-          msg (SE, _("%s=%s requires the variables to be "
-                     "moved to have the same width, but %s has "
-                     "width %d and %s has width %d."),
-               subcommand_name, pos_name,
+          msg (SE, _("To move category labels from one axis to another, "
+                     "the variables whose labels are to be moved must all "
+                     "have the same width, but %s has width %d and %s has "
+                     "width %d."),
                var_get_name (v0), var_get_width (v0),
                var_get_name (vi), var_get_width (vi));
+          lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+                       _("This syntax moves category labels to another axis."));
           return false;
         }
       if (!val_labs_equal (var_get_value_labels (v0),
                            var_get_value_labels (vi)))
         {
-          msg (SE, _("%s=%s requires the variables to be "
-                     "moved to have the same value labels, but %s "
-                     "and %s have different value labels."),
-               subcommand_name, pos_name,
+          msg (SE, _("To move category labels from one axis to another, "
+                     "the variables whose labels are to be moved must all "
+                     "have the same value labels, but %s and %s have "
+                     "different value labels."),
                var_get_name (v0), var_get_name (vi));
+          lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+                       _("This syntax moves category labels to another axis."));
           return false;
         }
       if (!ctables_categories_equal (c0, ci))
         {
-          msg (SE, _("%s=%s requires the variables to be "
-                     "moved to have the same category "
-                     "specifications, but %s and %s have different "
-                     "category specifications."),
-               subcommand_name, pos_name,
+          msg (SE, _("To move category labels from one axis to another, "
+                     "the variables whose labels are to be moved must all "
+                     "have the same category specifications, but %s and %s "
+                     "have different category specifications."),
                var_get_name (v0), var_get_name (vi));
+          lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+                       _("This syntax moves category labels to another axis."));
           return false;
         }
     }
@@ -5051,7 +5068,7 @@ enumerate_sum_vars (const struct ctables_axis *a,
 }
 
 static bool
-ctables_prepare_table (struct ctables_table *t)
+ctables_prepare_table (struct ctables_table *t, struct lexer *lexer)
 {
   for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
     if (t->axes[a])
@@ -5288,8 +5305,8 @@ ctables_prepare_table (struct ctables_table *t)
   enumerate_sum_vars (t->axes[t->summary_axis],
                       &t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
 
-  return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
-          && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
+  return (ctables_check_label_position (t, lexer, PIVOT_AXIS_ROW)
+          && ctables_check_label_position (t, lexer, PIVOT_AXIS_COLUMN));
 }
 
 static void
@@ -5800,7 +5817,8 @@ ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
         = ctables_find_postcompute (ct, lex_tokcstr (lexer));
       if (!pc)
         {
-          msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
+          lex_error (lexer, _("Unknown computed category &%s."),
+                     lex_tokcstr (lexer));
           goto error;
         }
       lex_get (lexer);
@@ -6017,6 +6035,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           double widths[2] = { SYSMIS, SYSMIS };
           double units_per_inch = 72.0;
 
+          int start_ofs = lex_ofs (lexer);
           while (lex_token (lexer) != T_SLASH)
             {
               if (lex_match_id (lexer, "MINCOLWIDTH"))
@@ -6087,7 +6106,9 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           if (widths[0] != SYSMIS && widths[1] != SYSMIS
               && widths[0] > widths[1])
             {
-              msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
+              lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
+                             _("MINCOLWIDTH must not be greater than "
+                               "MAXCOLWIDTH."));
               goto error;
             }
 
@@ -6200,6 +6221,15 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
                                "SMISSING", "PCOMPUTE", "PPROPERTIES",
                                "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
+          if (lex_match_id (lexer, "SLABELS")
+              || lex_match_id (lexer, "CLABELS")
+              || lex_match_id (lexer, "CRITERIA")
+              || lex_match_id (lexer, "CATEGORIES")
+              || lex_match_id (lexer, "TITLES")
+              || lex_match_id (lexer, "SIGTEST")
+              || lex_match_id (lexer, "COMPARETEST"))
+            lex_next_msg (lexer, SN, -1, -1,
+                          _("TABLE must appear before this subcommand."));
           goto error;
         }
 
@@ -6343,7 +6373,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
 
       if (lex_token (lexer) == T_ENDCMD)
         {
-          if (!ctables_prepare_table (t))
+          if (!ctables_prepare_table (t, lexer))
             goto error;
           break;
         }
@@ -6386,6 +6416,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
             }
           else if (lex_match_id (lexer, "CLABELS"))
             {
+              int start_ofs = lex_ofs (lexer) - 1;
               if (lex_match_id (lexer, "AUTO"))
                 {
                   t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
@@ -6423,6 +6454,24 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
                                        "COLLABELS");
                   goto error;
                 }
+              int end_ofs = lex_ofs (lexer) - 1;
+
+              if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
+                  && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
+                {
+                  msg (SE, _("ROWLABELS and COLLABELS may not both be "
+                             "specified."));
+
+                  lex_ofs_msg (lexer, SN, t->clabels_start_ofs,
+                               t->clabels_end_ofs,
+                               _("This is the first specification."));
+                  lex_ofs_msg (lexer, SN, start_ofs, end_ofs,
+                               _("This is the second specification."));
+                  goto error;
+                }
+
+              t->clabels_start_ofs = start_ofs;
+              t->clabels_end_ofs = end_ofs;
             }
           else if (lex_match_id (lexer, "CRITERIA"))
             {
@@ -6540,7 +6589,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
             }
           else if (lex_match_id (lexer, "COMPARETEST"))
             {
-              int start_ofs = lex_ofs (lexer);
+              int start_ofs = lex_ofs (lexer) - 1;
               if (!t->pairwise)
                 {
                   t->pairwise = xmalloc (sizeof *t->pairwise);
@@ -6690,6 +6739,16 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
               lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
                                    "CRITERIA", "CATEGORIES", "TITLES",
                                    "SIGTEST", "COMPARETEST");
+              if (lex_match_id (lexer, "FORMAT")
+                  || lex_match_id (lexer, "VLABELS")
+                  || lex_match_id (lexer, "MRSETS")
+                  || lex_match_id (lexer, "SMISSING")
+                  || lex_match_id (lexer, "PCOMPUTE")
+                  || lex_match_id (lexer, "PPROPERTIES")
+                  || lex_match_id (lexer, "WEIGHT")
+                  || lex_match_id (lexer, "HIDESMALLCOUNTS"))
+                lex_next_msg (lexer, SN, -1, -1,
+                              _("This subcommand must appear before TABLE."));
               goto error;
             }
 
@@ -6698,19 +6757,12 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
         }
 
       if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW)
-        {
-          t->clabels_from_axis = PIVOT_AXIS_ROW;
-          if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
-            {
-              msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
-              goto error;
-            }
-        }
+        t->clabels_from_axis = PIVOT_AXIS_ROW;
       else if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
         t->clabels_from_axis = PIVOT_AXIS_COLUMN;
       t->clabels_to_axis = t->label_axis[t->clabels_from_axis];
 
-      if (!ctables_prepare_table (t))
+      if (!ctables_prepare_table (t, lexer))
         goto error;
     }
   while (lex_token (lexer) != T_ENDCMD);
index 160e45610f4057bd28a269d9c1b9a94eda47c073..5f8c98085288c8e8eb9b478c34ebde64f81a0d1c 100644 (file)
@@ -335,11 +335,8 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
 
               if (lex_match (lexer, T_LPAREN))
                 {
-                  if (lex_token (lexer) != T_ID)
-                    {
-                      lex_error (lexer, NULL);
-                      goto error;
-                    }
+                  if (!lex_force_id (lexer))
+                    goto error;
                   if (try_name (dict, dsc, lex_tokcstr (lexer)))
                     {
                       struct dsc_var *dsc_var = &dsc->vars[dsc->n_vars - 1];
@@ -347,8 +344,9 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
                       n_zs++;
                     }
                   else
-                    msg (SE, _("Z-score variable name %s would be"
-                               " a duplicate variable name."), lex_tokcstr (lexer));
+                    lex_error (lexer, _("Z-score variable name %s would be "
+                                        "a duplicate variable name."),
+                               lex_tokcstr (lexer));
                   lex_get (lexer);
                   if (!lex_force_match (lexer, T_RPAREN))
                    goto error;
@@ -468,13 +466,15 @@ match_statistic (struct lexer *lexer)
 {
   if (lex_token (lexer) == T_ID)
     {
-      enum dsc_statistic stat;
-
-      for (stat = 0; stat < DSC_N_STATS; stat++)
+      for (enum dsc_statistic stat = 0; stat < DSC_N_STATS; stat++)
         if (lex_match_id (lexer, dsc_info[stat].identifier))
          return stat;
 
-      lex_error (lexer, _("expecting statistic name: reverting to default"));
+      const char *stat_names[DSC_N_STATS];
+      for (enum dsc_statistic stat = 0; stat < DSC_N_STATS; stat++)
+        stat_names[stat] = dsc_info[stat].identifier;
+      lex_error_expecting_array (lexer, stat_names,
+                                 sizeof stat_names / sizeof *stat_names);
       lex_get (lexer);
     }
 
index 19c42fcad5aff5bb63d3b61fcd89458f76c7d218..446f9058e9e1c285289f57db7c3a7fef9940b8c2 100644 (file)
@@ -1758,7 +1758,7 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 
   if (totals_seen && nototals_seen)
     {
-      msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
+      msg (SE, _("%s and %s are mutually exclusive."), "TOTAL", "NOTOTAL");
       goto error;
     }
 
index 993f8975a629d6beae47ba95b7fed7f0c0cf2747..113d565367c894baea9ad3fae275043f497c4e45 100644 (file)
@@ -97,8 +97,10 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   bool ok;
 
   if (proc_make_temporary_transformations_permanent (ds))
-    msg (SW, _("%s ignores %s.  "
-               "Temporary transformations will be made permanent."), "FLIP", "TEMPORARY");
+    lex_ofs_msg (lexer, SW, 0, lex_ofs (lexer) - 1,
+                 _("%s ignores %s.  "
+                   "Temporary transformations will be made permanent."),
+                 "FLIP", "TEMPORARY");
 
   flip = pool_create_container (struct flip_pgm, pool);
   flip->n_vars = 0;
index 8157bb295887c762f2362005b6e8f4bfcb474dcf..5c5619105222a866a72f83ad5ac0e42eeedcab28 100644 (file)
@@ -840,7 +840,7 @@ cmd_graph (struct lexer *lexer, struct dataset *ds)
              const struct variable *v = NULL;
              if (!lex_match_variable (lexer,graph.dict,&v))
                {
-                 lex_error (lexer, _("Variable expected"));
+                 lex_error (lexer, _("Syntax error expecting variable name."));
                  goto error;
                }
              graph.by_var[0] = v;
@@ -880,7 +880,6 @@ cmd_graph (struct lexer *lexer, struct dataset *ds)
       else if (lex_match_id (lexer, "FOOTNOTE"))
        {
          lex_error (lexer, _("%s is not yet implemented."),"FOOTNOTE");
-         lex_error (lexer, _("FOOTNOTE is not implemented yet for GRAPH"));
          goto error;
        }
       else if (lex_match_id (lexer, "MISSING"))
index 37b565e05eba85cce43d341ff2b315a9212ac319..ba6a1402074926f8aefc84e4aadd86b0c6729c8c 100644 (file)
@@ -89,9 +89,13 @@ struct msave_common
     /* Common configuration for all MSAVEs. */
     struct msg_location *location; /* Range of lines for first MSAVE. */
     struct file_handle *outfile;   /* Output file for all the MSAVEs. */
+    struct msg_location *outfile_location;
     struct string_array variables; /* VARIABLES subcommand. */
+    struct msg_location *variables_location;
     struct string_array fnames;    /* FNAMES subcommand. */
+    struct msg_location *fnames_location;
     struct string_array snames;    /* SNAMES subcommand. */
+    struct msg_location *snames_location;
 
     /* Collects and owns factors and splits.  The individual msave_command
        structs point to these but do not own them.  (This is because factors
@@ -4742,7 +4746,8 @@ matrix_lvalue_parse (struct matrix_state *s)
     {
       if (!lvalue->var)
         {
-          msg (SE, _("Undefined variable %s."), lex_tokcstr (s->lexer));
+          lex_error (s->lexer, _("Undefined variable %s."),
+                     lex_tokcstr (s->lexer));
           goto error;
         }
 
@@ -5173,6 +5178,7 @@ struct matrix_command
 
         struct matrix_get
           {
+            struct lexer *lexer;
             struct matrix_lvalue *dst;
             struct dataset *dataset;
             struct file_handle *file;
@@ -5924,7 +5930,7 @@ matrix_break_parse (struct matrix_state *s)
 {
   if (!s->in_loop)
     {
-      msg (SE, _("BREAK not inside LOOP."));
+      lex_next_error (s->lexer, -1, -1, _("BREAK not inside LOOP."));
       return NULL;
     }
 
@@ -6022,7 +6028,7 @@ matrix_release_parse (struct matrix_state *s)
           cmd->release.vars[cmd->release.n_vars++] = var;
         }
       else
-        lex_error (s->lexer, _("Variable name expected."));
+        lex_error (s->lexer, _("Syntax error expecting variable name."));
       lex_get (s->lexer);
 
       if (!lex_match (s->lexer, T_COMMA))
@@ -6441,6 +6447,10 @@ matrix_read_parse (struct matrix_state *s)
   if (!read->dst)
     goto error;
 
+  int by_ofs = 0;
+  int format_ofs = 0;
+  int record_width_start = 0, record_width_end = 0;
+
   int by = 0;
   int repetitions = 0;
   int record_width = 0;
@@ -6471,6 +6481,7 @@ matrix_read_parse (struct matrix_state *s)
         {
           lex_match (s->lexer, T_EQUALS);
 
+          record_width_start = lex_ofs (s->lexer);
           if (!lex_force_int_range (s->lexer, "FIELD", 1, INT_MAX))
             goto error;
           read->c1 = lex_integer (s->lexer);
@@ -6479,6 +6490,7 @@ matrix_read_parse (struct matrix_state *s)
               || !lex_force_int_range (s->lexer, "TO", read->c1, INT_MAX))
             goto error;
           read->c2 = lex_integer (s->lexer) + 1;
+          record_width_end = lex_ofs (s->lexer);
           lex_get (s->lexer);
 
           record_width = read->c2 - read->c1;
@@ -6488,12 +6500,20 @@ matrix_read_parse (struct matrix_state *s)
                                         read->c2 - read->c1))
                 goto error;
               by = lex_integer (s->lexer);
+              by_ofs = lex_ofs (s->lexer);
+              int field_end = lex_ofs (s->lexer);
               lex_get (s->lexer);
 
               if (record_width % by)
                 {
-                  msg (SE, _("BY %d does not evenly divide record width %d."),
-                       by, record_width);
+                  lex_ofs_error (
+                    s->lexer, record_width_start, field_end,
+                    _("Field width %d does not evenly divide record width %d."),
+                    by, record_width);
+                  lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                               _("This syntax designates the record width."));
+                  lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+                               _("This syntax specifies the field width."));
                   goto error;
                 }
             }
@@ -6527,7 +6547,7 @@ matrix_read_parse (struct matrix_state *s)
         {
           if (seen_format)
             {
-              lex_sbc_only_once ("FORMAT");
+              lex_sbc_only_once (s->lexer, "FORMAT");
               goto error;
             }
           seen_format = true;
@@ -6537,6 +6557,7 @@ matrix_read_parse (struct matrix_state *s)
           if (lex_token (s->lexer) != T_STRING && !lex_force_id (s->lexer))
             goto error;
 
+          format_ofs = lex_ofs (s->lexer);
           const char *p = lex_tokcstr (s->lexer);
           if (c_isdigit (p[0]))
             {
@@ -6578,6 +6599,8 @@ matrix_read_parse (struct matrix_state *s)
     {
       msg (SE, _("SIZE is required for reading data into a full matrix "
                  "(as opposed to a submatrix)."));
+      msg_at (SN, read->dst->var_location,
+              _("This expression designates a full matrix."));
       goto error;
     }
 
@@ -6618,6 +6641,10 @@ matrix_read_parse (struct matrix_state *s)
     {
       msg (SE, _("%d repetitions cannot fit in record width %d."),
            repetitions, record_width);
+      lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                   _("This syntax designates the number of repetitions."));
+      lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                   _("This syntax designates the record width."));
       goto error;
     }
   int w = (repetitions ? record_width / repetitions
@@ -6625,14 +6652,26 @@ matrix_read_parse (struct matrix_state *s)
            : by);
   if (by && w != by)
     {
+      msg (SE, _("This command specifies two different field widths."));
       if (repetitions)
-        msg (SE, _("FORMAT specifies %d repetitions with record width %d, "
-                   "which implies field width %d, "
-                   "but BY specifies field width %d."),
-             repetitions, record_width, w, by);
+        {
+          lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                       ngettext ("This syntax specifies %d repetition.",
+                                 "This syntax specifies %d repetitions.",
+                                 repetitions),
+                       repetitions);
+          lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                       _("This syntax designates record width %d, "
+                         "which divided by %d repetitions implies "
+                         "field width %d."),
+                       record_width, repetitions, w);
+        }
       else
-        msg (SE, _("FORMAT specifies field width %d but BY specifies %d."),
-             w, by);
+        lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                     _("This syntax specifies field width %d."), w);
+
+      lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+                   _("This syntax specifies field width %d."), by);
       goto error;
     }
   read->w = w;
@@ -6988,6 +7027,10 @@ matrix_write_parse (struct matrix_state *s)
   if (!write->expression)
     goto error;
 
+  int by_ofs = 0;
+  int format_ofs = 0;
+  int record_width_start = 0, record_width_end = 0;
+
   int by = 0;
   int repetitions = 0;
   int record_width = 0;
@@ -7019,6 +7062,8 @@ matrix_write_parse (struct matrix_state *s)
         {
           lex_match (s->lexer, T_EQUALS);
 
+          record_width_start = lex_ofs (s->lexer);
+
           if (!lex_force_int_range (s->lexer, "FIELD", 1, INT_MAX))
             goto error;
           write->c1 = lex_integer (s->lexer);
@@ -7027,6 +7072,7 @@ matrix_write_parse (struct matrix_state *s)
               || !lex_force_int_range (s->lexer, "TO", write->c1, INT_MAX))
             goto error;
           write->c2 = lex_integer (s->lexer) + 1;
+          record_width_end = lex_ofs (s->lexer);
           lex_get (s->lexer);
 
           record_width = write->c2 - write->c1;
@@ -7035,13 +7081,21 @@ matrix_write_parse (struct matrix_state *s)
               if (!lex_force_int_range (s->lexer, "BY", 1,
                                         write->c2 - write->c1))
                 goto error;
+              by_ofs = lex_ofs (s->lexer);
+              int field_end = lex_ofs (s->lexer);
               by = lex_integer (s->lexer);
               lex_get (s->lexer);
 
               if (record_width % by)
                 {
-                  msg (SE, _("BY %d does not evenly divide record width %d."),
-                       by, record_width);
+                  lex_ofs_error (
+                    s->lexer, record_width_start, field_end,
+                    _("Field width %d does not evenly divide record width %d."),
+                    by, record_width);
+                  lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                               _("This syntax designates the record width."));
+                  lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+                               _("This syntax specifies the field width."));
                   goto error;
                 }
             }
@@ -7067,7 +7121,7 @@ matrix_write_parse (struct matrix_state *s)
         {
           if (has_format || write->format)
             {
-              lex_sbc_only_once ("FORMAT");
+              lex_sbc_only_once (s->lexer, "FORMAT");
               goto error;
             }
 
@@ -7076,6 +7130,7 @@ matrix_write_parse (struct matrix_state *s)
           if (lex_token (s->lexer) != T_STRING && !lex_force_id (s->lexer))
             goto error;
 
+          format_ofs = lex_ofs (s->lexer);
           const char *p = lex_tokcstr (s->lexer);
           if (c_isdigit (p[0]))
             {
@@ -7151,8 +7206,10 @@ matrix_write_parse (struct matrix_state *s)
    */
   if (repetitions > record_width)
     {
-      msg (SE, _("%d repetitions cannot fit in record width %d."),
-           repetitions, record_width);
+      lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                   _("This syntax designates the number of repetitions."));
+      lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                   _("This syntax designates the record width."));
       goto error;
     }
   int w = (repetitions ? record_width / repetitions
@@ -7160,14 +7217,26 @@ matrix_write_parse (struct matrix_state *s)
            : by);
   if (by && w != by)
     {
+      msg (SE, _("This command specifies two different field widths."));
       if (repetitions)
-        msg (SE, _("FORMAT specifies %d repetitions with record width %d, "
-                   "which implies field width %d, "
-                   "but BY specifies field width %d."),
-             repetitions, record_width, w, by);
+        {
+          lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                       ngettext ("This syntax specifies %d repetition.",
+                                 "This syntax specifies %d repetitions.",
+                                 repetitions),
+                       repetitions);
+          lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+                       _("This syntax designates record width %d, "
+                         "which divided by %d repetitions implies "
+                         "field width %d."),
+                       record_width, repetitions, w);
+        }
       else
-        msg (SE, _("FORMAT specifies field width %d but BY specifies %d."),
-             w, by);
+        lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+                     _("This syntax specifies field width %d."), w);
+
+      lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+                   _("This syntax specifies field width %d."), by);
       goto error;
     }
   if (w && !write->format)
@@ -7181,10 +7250,11 @@ matrix_write_parse (struct matrix_state *s)
 
   if (write->format && fmt_var_width (write->format) > sizeof (double))
     {
-      char s[FMT_STRING_LEN_MAX + 1];
-      fmt_to_string (write->format, s);
-      msg (SE, _("Format %s is too wide for %zu-byte matrix elements."),
-           s, sizeof (double));
+      char fs[FMT_STRING_LEN_MAX + 1];
+      fmt_to_string (write->format, fs);
+      lex_ofs_error (s->lexer, format_ofs, format_ofs,
+                     _("Format %s is too wide for %zu-byte matrix elements."),
+                     fs, sizeof (double));
       goto error;
     }
 
@@ -7291,6 +7361,7 @@ matrix_get_parse (struct matrix_state *s)
   *cmd = (struct matrix_command) {
     .type = MCMD_GET,
     .get = {
+      .lexer = s->lexer,
       .dataset = s->dataset,
       .user = { .treatment = MGET_ERROR },
       .system = { .treatment = MGET_ERROR },
@@ -7335,7 +7406,7 @@ matrix_get_parse (struct matrix_state *s)
 
           if (get->n_vars)
             {
-              lex_sbc_only_once ("VARIABLES");
+              lex_sbc_only_once (s->lexer, "VARIABLES");
               goto error;
             }
 
@@ -7418,7 +7489,7 @@ matrix_get_execute__ (struct matrix_command *cmd, struct casereader *reader,
 
   if (get->n_vars)
     {
-      if (!var_syntax_evaluate (get->vars, get->n_vars, dict,
+      if (!var_syntax_evaluate (get->lexer, get->vars, get->n_vars, dict,
                                 &vars, &n_vars, PV_NUMERIC))
         return;
     }
@@ -7540,7 +7611,7 @@ matrix_open_casereader (const struct matrix_command *cmd,
     {
       if (dict_get_n_vars (dataset_dict (dataset)) == 0)
         {
-          msg_at (ME, cmd->location,
+          msg_at (SE, cmd->location,
                   _("The %s command cannot read an empty active file."),
                   command_name);
           return false;
@@ -7579,21 +7650,39 @@ matrix_get_execute (struct matrix_command *cmd)
 
 static bool
 variables_changed (const char *keyword,
-                   const struct string_array *new,
-                   const struct string_array *old)
-{
-  if (new->n)
-    {
-      if (!old->n)
-        {
-          msg (SE, _("%s may only be specified on MSAVE if it was specified "
-                     "on the first MSAVE within MATRIX."), keyword);
+                   const struct string_array *new_vars,
+                   const struct msg_location *new_vars_location,
+                   const struct msg_location *new_location,
+                   const struct string_array *old_vars,
+                   const struct msg_location *old_vars_location,
+                   const struct msg_location *old_location)
+{
+  if (new_vars->n)
+    {
+      if (!old_vars->n)
+        {
+          msg_at (SE, new_location,
+                  _("%s may only be specified on MSAVE if it was specified "
+                    "on the first MSAVE within MATRIX."), keyword);
+          msg_at (SN, old_location,
+                  _("The first MSAVE in MATRIX did not specify %s."),
+                  keyword);
+          msg_at (SN, new_vars_location,
+                  _("This is the specification of %s on a later MSAVE."),
+                  keyword);
           return true;
         }
-      else if (!string_array_equal_case (old, new))
-        {
-          msg (SE, _("%s must specify the same variables each time within "
-                     "a given MATRIX."), keyword);
+      if (!string_array_equal_case (old_vars, new_vars))
+        {
+          msg_at (SE, new_location,
+                  _("%s must specify the same variables on each MSAVE "
+                    "within a given MATRIX."), keyword);
+          msg_at (SE, old_vars_location,
+                  _("This is the specification of %s on the first MSAVE."),
+                  keyword);
+          msg_at (SE, new_vars_location,
+                  _("This is a different specification of %s on a later MSAVE."),
+                  keyword);
           return true;
         }
     }
@@ -7605,14 +7694,25 @@ msave_common_changed (const struct msave_common *old,
                       const struct msave_common *new)
 {
   if (new->outfile && !fh_equal (old->outfile, new->outfile))
-    msg (SE, _("OUTFILE must name the same file on each MSAVE "
-               "within a single MATRIX command."));
-  else if (variables_changed ("VARIABLES", &new->variables, &old->variables)
-           || variables_changed ("FNAMES", &new->fnames, &old->fnames)
-           || variables_changed ("SNAMES", &new->snames, &old->snames))
-    msg_at (SN, old->location,
-            _("This is the location of the first MSAVE command."));
-  else
+    {
+      msg (SE, _("OUTFILE must name the same file on each MSAVE "
+                 "within a single MATRIX command."));
+      msg_at (SN, old->outfile_location,
+              _("This is the OUTFILE on the first MSAVE command."));
+      msg_at (SN, new->outfile_location,
+              _("This is the OUTFILE on a later MSAVE command."));
+      return false;
+    }
+
+  if (!variables_changed ("VARIABLES",
+                          &new->variables, new->variables_location, new->location,
+                          &old->variables, old->variables_location, old->location)
+      && !variables_changed ("FNAMES",
+                             &new->fnames, new->fnames_location, new->location,
+                             &old->fnames, old->fnames_location, old->location)
+      && !variables_changed ("SNAMES",
+                             &new->snames, new->snames_location, new->location,
+                             &old->snames, old->snames_location, old->location))
     return false;
 
   return true;
@@ -7625,9 +7725,13 @@ msave_common_destroy (struct msave_common *common)
     {
       msg_location_destroy (common->location);
       fh_unref (common->outfile);
+      msg_location_destroy (common->outfile_location);
       string_array_destroy (&common->variables);
+      msg_location_destroy (common->variables_location);
       string_array_destroy (&common->fnames);
+      msg_location_destroy (common->fnames_location);
       string_array_destroy (&common->snames);
+      msg_location_destroy (common->snames_location);
 
       for (size_t i = 0; i < common->n_factors; i++)
         matrix_expr_destroy (common->factors[i]);
@@ -7661,17 +7765,22 @@ match_rowtype (struct lexer *lexer)
 }
 
 static bool
-parse_var_names (struct lexer *lexer, struct string_array *sa)
+parse_var_names (struct lexer *lexer, struct string_array *sa,
+                 struct msg_location **locationp)
 {
   lex_match (lexer, T_EQUALS);
 
   string_array_clear (sa);
+  msg_location_destroy (*locationp);
+  *locationp = NULL;
 
   struct dictionary *dict = dict_create (get_default_encoding ());
   char **names;
   size_t n_names;
+  int start_ofs = lex_ofs (lexer);
   bool ok = parse_DATA_LIST_vars (lexer, dict, &names, &n_names,
                                   PV_NO_DUPLICATE | PV_NO_SCRATCH);
+  int end_ofs = lex_ofs (lexer) - 1;
   dict_unref (dict);
 
   if (ok)
@@ -7680,16 +7789,17 @@ parse_var_names (struct lexer *lexer, struct string_array *sa)
         if (ss_equals_case (ss_cstr (names[i]), ss_cstr ("ROWTYPE_"))
             || ss_equals_case (ss_cstr (names[i]), ss_cstr ("VARNAME_")))
           {
-            msg (SE, _("Variable name %s is reserved."), names[i]);
+            lex_ofs_error (lexer, start_ofs, end_ofs,
+                           _("Variable name %s is reserved."), names[i]);
             for (size_t j = 0; j < n_names; j++)
               free (names[i]);
             free (names);
             return false;
           }
 
-      string_array_clear (sa);
       sa->strings = names;
       sa->n = sa->allocated = n_names;
+      *locationp = lex_ofs_location (lexer, start_ofs, end_ofs);
     }
   return ok;
 }
@@ -7728,23 +7838,30 @@ matrix_msave_parse (struct matrix_state *s)
           lex_match (s->lexer, T_EQUALS);
 
           fh_unref (common->outfile);
+          int start_ofs = lex_ofs (s->lexer);
           common->outfile = fh_parse (s->lexer, FH_REF_FILE, NULL);
           if (!common->outfile)
             goto error;
+          msg_location_destroy (common->outfile_location);
+          common->outfile_location = lex_ofs_location (s->lexer, start_ofs,
+                                                       lex_ofs (s->lexer) - 1);
         }
       else if (lex_match_id (s->lexer, "VARIABLES"))
         {
-          if (!parse_var_names (s->lexer, &common->variables))
+          if (!parse_var_names (s->lexer, &common->variables,
+                                &common->variables_location))
             goto error;
         }
       else if (lex_match_id (s->lexer, "FNAMES"))
         {
-          if (!parse_var_names (s->lexer, &common->fnames))
+          if (!parse_var_names (s->lexer, &common->fnames,
+                                &common->fnames_location))
             goto error;
         }
       else if (lex_match_id (s->lexer, "SNAMES"))
         {
-          if (!parse_var_names (s->lexer, &common->snames))
+          if (!parse_var_names (s->lexer, &common->snames,
+                                &common->snames_location))
             goto error;
         }
       else if (lex_match_id (s->lexer, "SPLIT"))
@@ -7782,12 +7899,12 @@ matrix_msave_parse (struct matrix_state *s)
     {
       if (common->fnames.n && !factors)
         {
-          msg (SE, _("FNAMES requires FACTOR."));
+          msg_at (SE, common->fnames_location, _("FNAMES requires FACTOR."));
           goto error;
         }
       if (common->snames.n && !splits)
         {
-          msg (SE, _("SNAMES requires SPLIT."));
+          msg_at (SE, common->snames_location, _("SNAMES requires SPLIT."));
           goto error;
         }
       if (!common->outfile)
@@ -7869,8 +7986,7 @@ msave_add_vars (struct dictionary *d, const struct string_array *vars)
 }
 
 static struct dictionary *
-msave_create_dict (const struct msave_common *common,
-                   const struct msg_location *location)
+msave_create_dict (const struct msave_common *common)
 {
   struct dictionary *dict = dict_create (get_default_encoding ());
 
@@ -7887,7 +8003,8 @@ msave_create_dict (const struct msave_common *common,
   const char *dup_factor = msave_add_vars (dict, &common->fnames);
   if (dup_factor)
     {
-      msg_at (SE, location, _("Duplicate or invalid FACTOR variable name %s."),
+      msg_at (SE, common->fnames_location,
+              _("Duplicate or invalid FACTOR variable name %s."),
               dup_factor);
       goto error;
     }
@@ -7897,7 +8014,8 @@ msave_create_dict (const struct msave_common *common,
   const char *dup_var = msave_add_vars (dict, &common->variables);
   if (dup_var)
     {
-      msg_at (SE, location, _("Duplicate or invalid variable name %s."),
+      msg_at (SE, common->variables_location,
+              _("Duplicate or invalid variable name %s."),
               dup_var);
       goto error;
     }
@@ -7976,7 +8094,7 @@ matrix_msave_execute (struct matrix_command *cmd)
 
   if (!common->writer)
     {
-      struct dictionary *dict = msave_create_dict (common, cmd->location);
+      struct dictionary *dict = msave_create_dict (common);
       if (!dict)
         goto error;
 
@@ -8851,7 +8969,8 @@ matrix_commands_parse (struct matrix_state *s, struct matrix_commands *c,
 
       if (lex_at_phrase (s->lexer, "END MATRIX"))
         {
-          msg (SE, _("Premature END MATRIX within %s."), command_name);
+          lex_next_error (s->lexer, 0, 1,
+                          _("Premature END MATRIX within %s."), command_name);
           return false;
         }
 
index eada5d16496db6195b78eb6463db9f03a5c4ec30..d7b2cff85311015ae739644163a126a577d76c8c 100644 (file)
@@ -424,7 +424,7 @@ parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests
           npt->missing++;
           if (npt->missing > 1)
             {
-              lex_sbc_only_once ("MISSING");
+              lex_sbc_only_once (lexer, "MISSING");
               goto lossage;
             }
           while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
@@ -451,7 +451,7 @@ parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests
           npt->method++;
           if (npt->method > 1)
             {
-              lex_sbc_only_once ("METHOD");
+              lex_sbc_only_once (lexer, "METHOD");
               goto lossage;
             }
           switch (npar_method (lexer, nps))
@@ -500,7 +500,7 @@ parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests
       }
     if (lex_token (lexer) != T_ENDCMD)
       {
-        lex_error (lexer, _("expecting end of command"));
+        lex_error (lexer, _("Syntax error expecting end of command."));
         goto lossage;
       }
 
@@ -688,7 +688,8 @@ npar_runs (struct lexer *lexer, struct dataset *ds,
        }
       else
        {
-         lex_error (lexer, _("Expecting %s, %s, %s or a number."), "MEAN", "MEDIAN", "MODE");
+         lex_error (lexer, _("Syntax error expecting %s, %s, %s or a number."),
+                     "MEAN", "MEDIAN", "MODE");
          return 0;
        }
 
index 6ad71f14c829fc75a91b63e89854f18d9fc3b179..c65683e246ab4b1949fcaebda03b353a7eb3073a 100644 (file)
@@ -517,8 +517,9 @@ cmd_oneway (struct lexer *lexer, struct dataset *ds)
                    }
                  else
                    {
-                     msg (SE, _("The post hoc analysis method %s is not supported."), lex_tokcstr (lexer));
-                     lex_error (lexer, NULL);
+                     lex_error (lexer,
+                                 _("The post hoc analysis method %s is not supported."),
+                                 lex_tokcstr (lexer));
                      goto error;
                    }
                }
index 0cd08d02a9268224436bac93339ffc876c9669a7..161b9768b1fc431d8cd71ad9169062e13431e543 100644 (file)
@@ -905,8 +905,7 @@ quick_cluster_parse (struct lexer *lexer, struct qc *qc)
                }
              else
                {
-                 lex_error (lexer, _("Expecting %s or %s."),
-                            "CLUSTER", "DISTANCE");
+                 lex_error_expecting (lexer, "CLUSTER", "DISTANCE");
                  return false;
                }
            }
index ad113676cb86d39996271def3f4936a0062f33a1..5baa4ca6ce0f6ef3b629c8b7a16faa743d6c6109 100644 (file)
@@ -328,11 +328,11 @@ parse_into (struct lexer *lexer, struct rank *cmd,
          const char *name = lex_tokcstr (lexer);
 
          if (var_count >= subcase_get_n_fields (&cmd->sc))
-            msg (SE, _("Too many variables in %s clause."), "INTO");
+            lex_error (lexer, _("Too many variables in %s clause."), "INTO");
          else if (dict_lookup_var (cmd->dict, name) != NULL)
-            msg (SE, _("Variable %s already exists."), name);
+            lex_error (lexer, _("Variable %s already exists."), name);
           else if (string_set_contains (new_names, name))
-            msg (SE, _("Duplicate variable name %s."), name);
+            lex_error (lexer, _("Duplicate variable name %s."), name);
           else
             {
               string_set_insert (new_names, name);
index b4448dd9e58ead03cb61cf3d5e5b3c97201b1edf..8605638de954bfaa4fa3e85048fa7258efa86ccf 100644 (file)
@@ -212,6 +212,8 @@ cmd_regression (struct lexer *lexer, struct dataset *ds)
   bool variables_seen = false;
   bool method_seen = false;
   bool dependent_seen = false;
+  int save_start = 0;
+  int save_end = 0;
   while (lex_token (lexer) != T_ENDCMD)
     {
       lex_match (lexer, T_SLASH);
@@ -220,12 +222,14 @@ cmd_regression (struct lexer *lexer, struct dataset *ds)
         {
          if (method_seen)
            {
-             msg (SE, _("VARIABLES may not appear after %s"), "METHOD");
+             lex_next_error (lexer, -1, -1,
+                              _("VARIABLES may not appear after %s"), "METHOD");
              goto error;
            }
          if (dependent_seen)
            {
-             msg (SE, _("VARIABLES may not appear after %s"), "DEPENDENT");
+             lex_next_error (lexer, -1, -1,
+                              _("VARIABLES may not appear after %s"), "DEPENDENT");
              goto error;
            }
          variables_seen = true;
@@ -338,6 +342,7 @@ cmd_regression (struct lexer *lexer, struct dataset *ds)
         }
       else if (lex_match_id (lexer, "SAVE"))
         {
+          save_start = lex_ofs (lexer) - 1;
           lex_match (lexer, T_EQUALS);
 
           while (lex_token (lexer) != T_ENDCMD
@@ -357,6 +362,7 @@ cmd_regression (struct lexer *lexer, struct dataset *ds)
                   goto error;
                 }
             }
+          save_end = lex_ofs (lexer) - 1;
         }
       else
         {
@@ -408,12 +414,14 @@ cmd_regression (struct lexer *lexer, struct dataset *ds)
         }
 
       if (proc_make_temporary_transformations_permanent (ds))
-        msg (SW, _("REGRESSION with SAVE ignores TEMPORARY.  "
-                   "Temporary transformations will be made permanent."));
+        lex_ofs_msg (lexer, SW, save_start, save_end,
+                     _("REGRESSION with SAVE ignores TEMPORARY.  "
+                       "Temporary transformations will be made permanent."));
 
       if (dict_get_filter (dict))
-        msg (SW, _("REGRESSION with SAVE ignores FILTER.  "
-                   "All cases will be processed."));
+        lex_ofs_msg (lexer, SW, save_start, save_end,
+                     _("REGRESSION with SAVE ignores FILTER.  "
+                       "All cases will be processed."));
 
       workspace.writer = autopaging_writer_create (proto);
       caseproto_unref (proto);
index 5816a2304ceba846425dab7bf1ab5af3d09ed785..9c794217923b708fc11d7d4552d4a2deb9e1380e 100644 (file)
@@ -277,11 +277,15 @@ cmd_reliability (struct lexer *lexer, struct dataset *ds)
        }
       else if (lex_match_id (lexer, "STATISTICS"))
         {
+          int statistics_start = lex_ofs (lexer) - 1;
           lex_match (lexer, T_EQUALS);
-          msg (SW, _("The STATISTICS subcommand is not yet implemented.  "
-                     "No statistics will be produced."));
           while (lex_match (lexer, T_ID))
             continue;
+          int statistics_end = lex_ofs (lexer) - 1;
+
+          lex_ofs_msg (lexer, SW, statistics_start, statistics_end,
+                       _("The STATISTICS subcommand is not yet implemented.  "
+                         "No statistics will be produced."));
         }
       else
        {
index 8810301c67f7d990e79444dbf5781790ee84c60f..84759d8d10564ce723171df5998c1674092bb966 100644 (file)
@@ -93,6 +93,7 @@ cmd_t_test (struct lexer *lexer, struct dataset *ds)
           tt.mode = MODE_INDEP;
           lex_match (lexer, T_EQUALS);
 
+          int groups_start = lex_ofs (lexer);
           if (NULL == (gvar = parse_variable (lexer, dict)))
             goto exit;
 
@@ -127,11 +128,13 @@ cmd_t_test (struct lexer *lexer, struct dataset *ds)
               cut = false;
               n = 0;
             }
+          int groups_end = lex_ofs (lexer) - 1;
 
           if (n != 2 && var_is_alpha (gvar))
             {
-              msg (SE, _("When applying %s to a string variable, two "
-                         "values must be specified."), "GROUPS");
+              lex_ofs_error (lexer, groups_start, groups_end,
+                             _("When applying %s to a string variable, two "
+                               "values must be specified."), "GROUPS");
               goto exit;
             }
         }
@@ -142,7 +145,9 @@ cmd_t_test (struct lexer *lexer, struct dataset *ds)
 
           if (tt.n_vars > 0)
             {
-              msg (SE, _("%s subcommand may not be used with %s."), "VARIABLES", "PAIRS");
+              lex_next_error (lexer, -1, -1,
+                              _("%s subcommand may not be used with %s."),
+                              "VARIABLES", "PAIRS");
               goto exit;
             }
 
@@ -238,7 +243,9 @@ cmd_t_test (struct lexer *lexer, struct dataset *ds)
         {
           if (tt.mode == MODE_PAIRED)
             {
-              msg (SE, _("%s subcommand may not be used with %s."), "VARIABLES", "PAIRS");
+              lex_next_error (lexer, -1, -1,
+                              _("%s subcommand may not be used with %s."),
+                              "VARIABLES", "PAIRS");
               goto exit;
             }
 
index 48a49116f2a529caa951433478d9abf5dd321609..16ac95c5e396cdeac8926845cbb4b13d8eee3209 100644 (file)
@@ -76,7 +76,7 @@ parse_float_format (struct lexer *lexer, enum float_format *format)
         *format = fp_formats[i].format;
         return true;
       }
-  lex_error (lexer, "expecting floating-point format identifier");
+  lex_error (lexer, "Syntax error expecting floating-point format identifier.");
   return false;
 }
 
index 34408739b4ce0856d2e643737c50b71a5d9e36ab..bd030d7ed7afa92cca818b5a0b3db073cc09c6a7 100644 (file)
@@ -47,7 +47,7 @@ read_values (struct lexer *lexer, double **values, double **weights, size_t *n)
         {
           if (!lex_is_number (lexer))
             {
-              lex_error (lexer, _("expecting weight value"));
+              lex_error (lexer, _("Syntax error expecting weight value."));
               return false;
             }
           weight = lex_tokval (lexer);
index ec5e7646f4ee1bba3fb610d9aa7f45fc7ba1d43c..d82ce406a82704bcef689039e04391c5f5f340de 100644 (file)
@@ -42,8 +42,8 @@ cmd_cd (struct lexer *lexer, struct dataset *ds UNUSED)
   if (-1 == chdir (path))
     {
       int err = errno;
-      msg (SE, _("Cannot change directory to %s: %s"), path,
-          strerror (err));
+      lex_error (lexer, _("Cannot change directory to %s: %s"), path,
+                 strerror (err));
       goto error;
     }
 
index 8afbe6562d9dc1c8b2fb9ef0345b9d1f91891c41..36a04b238554b736a9ac1c1958eb83ca8895abd5 100644 (file)
@@ -30,6 +30,6 @@ cmd_use (struct lexer *lexer, struct dataset *ds UNUSED)
   if (lex_match (lexer, T_ALL))
     return CMD_SUCCESS;
 
-  msg (SW, _("Only %s is currently implemented."), "USE ALL");
+  lex_msg (lexer, SW, _("Only %s is currently implemented."), "USE ALL");
   return CMD_FAILURE;
 }
index d730e02634860e4286a7ca245e45bc810394ed9a..45fca6175b86b17934c9417d746a19d4fa0ca10d 100644 (file)
 #define N_(msgid) msgid
 \f
 #if !HAVE_FORK
+#define TIME_LIMIT_SUPPORTED 0
 static bool
 run_commands (const struct string_array *commands, double time_limit)
 {
-  if (time_limit != DBL_MAX)
-    {
-      msg (SE, _("Time limit not supported on this platform."));
-      return false;
-    }
+  assert (time_limit == DBL_MAX);
 
   for (size_t i = 0; i < commands->n; i++)
     {
@@ -80,6 +77,7 @@ run_commands (const struct string_array *commands, double time_limit)
   return true;
 }
 #else
+#define TIME_LIMIT_SUPPORTED 1
 static bool
 run_command (const char *command, struct timespec timeout)
 {
@@ -289,7 +287,9 @@ cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (settings_get_safer_mode ())
     {
-      msg (SE, _("This command not allowed when the %s option is set."), "SAFER");
+      lex_next_error (lexer, -1, -1,
+                      _("This command not allowed when the %s option is set."),
+                      "SAFER");
       return CMD_FAILURE;
     }
 
@@ -314,6 +314,7 @@ cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
   double time_limit = DBL_MAX;
   if (lex_match_id (lexer, "TIMELIMIT"))
     {
+      int time_limit_start = lex_ofs (lexer) - 1;
       if (!lex_force_match (lexer, T_EQUALS)
           || !lex_force_num (lexer))
         {
@@ -324,6 +325,15 @@ cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
       double num = lex_number (lexer);
       lex_get (lexer);
       time_limit = num < 0.0 ? 0.0 : num;
+
+      int time_limit_end = lex_ofs (lexer) - 1;
+      if (!TIME_LIMIT_SUPPORTED)
+        {
+          lex_ofs_error (lexer, time_limit_start, time_limit_end,
+                         _("Time limit not supported on this platform."));
+          string_array_destroy (&commands);
+          return false;
+        }
     }
 
   enum cmd_result result = lex_end_of_command (lexer);
index 771eb72832dd1de27304976879eb931cb29350e5..1854cff9f49fb5cf4d4d7cb9b0b938d5cff5987a 100644 (file)
@@ -43,6 +43,14 @@ int change_permissions(const char *file_name, enum PER per);
 int
 cmd_permissions (struct lexer *lexer, struct dataset *ds UNUSED)
 {
+  if (settings_get_safer_mode ())
+    {
+      lex_next_error (lexer, -1, -1,
+                      _("This command not allowed when the %s option is set."),
+                      "SAFER");
+      return 0;
+    }
+
   char  *fn = NULL;
   const char *str = NULL;
   lex_match (lexer, T_SLASH);
@@ -100,12 +108,6 @@ change_permissions (const char *file_name, enum PER per)
   struct stat buf;
   mode_t mode;
 
-  if (settings_get_safer_mode ())
-    {
-      msg (SE, _("This command not allowed when the %s option is set."), "SAFER");
-      return 0;
-    }
-
   locale_file_name = utf8_to_filename (file_name);
   if (-1 == stat(locale_file_name, &buf))
     {
index e7c17765d61c2419c90ceb73a4a409b2d14a8bf0..a547e5104afbe67d26cc80bcf75279038fc6a215 100644 (file)
@@ -35,6 +35,7 @@
 #include "language/command.h"
 #include "language/lexer/format-parser.h"
 #include "language/lexer/lexer.h"
+#include "language/lexer/token.h"
 #include "libpspp/assertion.h"
 #include "libpspp/compiler.h"
 #include "libpspp/copyleft.h"
@@ -78,6 +79,13 @@ match_subcommand (struct lexer *lexer, const char *name)
     return false;
 }
 
+static int
+subcommand_start_ofs (struct lexer *lexer)
+{
+  int ofs = lex_ofs (lexer) - 1;
+  return lex_ofs_token (lexer, ofs)->type == T_EQUALS ? ofs - 1 : ofs;
+}
+
 static int
 parse_enum_valist (struct lexer *lexer, va_list args)
 {
@@ -156,16 +164,6 @@ force_parse_bool (struct lexer *lexer)
                            "OFF", false, "NO", false);
 }
 
-static bool
-force_parse_int (struct lexer *lexer, int *integerp)
-{
-  if (!lex_force_int (lexer))
-    return false;
-  *integerp = lex_integer (lexer);
-  lex_get (lexer);
-  return true;
-}
-
 static bool
 parse_output_routing (struct lexer *lexer, enum settings_output_type type)
 {
@@ -302,9 +300,12 @@ show_real_format (enum float_format float_format)
 static bool
 parse_unimplemented (struct lexer *lexer, const char *name)
 {
-  msg (SW, _("%s is not yet implemented."), name);
+  int start = subcommand_start_ofs (lexer);
   if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
     lex_get (lexer);
+  int end = lex_ofs (lexer) - 1;
+
+  lex_ofs_msg (lexer, SW, start, end, _("%s is not yet implemented."), name);
   return true;
 }
 
@@ -488,7 +489,7 @@ parse_EPOCH (struct lexer *lexer)
     }
   else
     {
-      lex_error (lexer, _("expecting %s or year"), "AUTOMATIC");
+      lex_error (lexer, _("Syntax error expecting %s or year."), "AUTOMATIC");
       return false;
     }
 
@@ -516,22 +517,23 @@ show_ERRORS (const struct dataset *ds UNUSED)
 static bool
 parse_FORMAT (struct lexer *lexer)
 {
+  int start = subcommand_start_ofs (lexer);
   struct fmt_spec fmt;
 
-  lex_match (lexer, T_EQUALS);
   if (!parse_format_specifier (lexer, &fmt))
     return false;
 
   if (!fmt_check_output (&fmt))
     return false;
 
+  int end = lex_ofs (lexer) - 1;
   if (fmt_is_string (fmt.type))
     {
       char str[FMT_STRING_LEN_MAX + 1];
-      msg (SE, _("%s requires numeric output format as an argument.  "
-                "Specified format %s is of type string."),
-          "FORMAT",
-          fmt_to_string (&fmt, str));
+      lex_ofs_error (lexer, start, end,
+                     _("%s requires numeric output format as an argument.  "
+                       "Specified format %s is of type string."),
+                     "FORMAT", fmt_to_string (&fmt, str));
       return false;
     }
 
@@ -672,7 +674,7 @@ parse_LOCALE (struct lexer *lexer)
     set_default_encoding (s);
   else if (!set_encoding_from_locale (s))
     {
-      msg (ME, _("%s is not a recognized encoding or locale name"), s);
+      lex_error (lexer, _("%s is not a recognized encoding or locale name"), s);
       return false;
     }
 
@@ -781,14 +783,10 @@ show_MPRINT (const struct dataset *ds UNUSED)
 static bool
 parse_MXERRS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXERRS", 1, INT_MAX))
     return false;
-
-  if (n >= 1)
-    settings_set_max_messages (MSG_S_ERROR, n);
-  else
-    msg (SE, _("%s must be at least 1."), "MXERRS");
+  settings_set_max_messages (MSG_S_ERROR, lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -801,14 +799,10 @@ show_MXERRS (const struct dataset *ds UNUSED)
 static bool
 parse_MXLOOPS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXLOOPS", 1, INT_MAX))
     return false;
-
-  if (n >= 1)
-    settings_set_mxloops (n);
-  else
-    msg (SE, _("%s must be at least 1."), "MXLOOPS");
+  settings_set_mxloops (lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -821,14 +815,10 @@ show_MXLOOPS (const struct dataset *ds UNUSED)
 static bool
 parse_MXWARNS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXWARNS", 0, INT_MAX))
     return false;
-
-  if (n >= 0)
-    settings_set_max_messages (MSG_S_WARNING, n);
-  else
-    msg (SE, _("%s must not be negative."), "MXWARNS");
+  settings_set_max_messages (MSG_S_WARNING, lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -1383,7 +1373,7 @@ static struct settings *saved_settings[MAX_SAVED_SETTINGS];
 static int n_saved_settings;
 
 int
-cmd_preserve (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
+cmd_preserve (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (n_saved_settings < MAX_SAVED_SETTINGS)
     {
@@ -1392,16 +1382,17 @@ cmd_preserve (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
     }
   else
     {
-      msg (SE, _("Too many %s commands without a %s: at most "
-                 "%d levels of saved settings are allowed."),
-          "PRESERVE", "RESTORE",
-           MAX_SAVED_SETTINGS);
+      lex_next_error (lexer, -1, -1,
+                      _("Too many %s commands without a %s: at most "
+                        "%d levels of saved settings are allowed."),
+                      "PRESERVE", "RESTORE",
+                      MAX_SAVED_SETTINGS);
       return CMD_CASCADING_FAILURE;
     }
 }
 
 int
-cmd_restore (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
+cmd_restore (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (n_saved_settings > 0)
     {
@@ -1412,7 +1403,8 @@ cmd_restore (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
     }
   else
     {
-      msg (SE, _("%s without matching %s."), "RESTORE", "PRESERVE");
+      lex_next_error (lexer, -1, -1,
+                      _("%s without matching %s."), "RESTORE", "PRESERVE");
       return CMD_FAILURE;
     }
 }
index 5c330f65a823e746a8dfefa854f79ff142529e56..61e67f33fa448d902a1d4f562404745401dfade7 100644 (file)
@@ -374,7 +374,8 @@ lvalue_parse (struct lexer *lexer, struct dataset *ds)
       lvalue->vector = dict_lookup_vector (dict, lex_tokcstr (lexer));
       if (lvalue->vector == NULL)
        {
-         msg (SE, _("There is no vector named %s."), lex_tokcstr (lexer));
+         lex_error (lexer, _("There is no vector named %s."),
+                     lex_tokcstr (lexer));
           goto lossage;
        }
 
index 9db76f6c652b11e5a12586208eb83102faa1fd51..a4e14316610ba1da31a7187eca9d17ade7b1589f 100644 (file)
@@ -122,7 +122,7 @@ cmd_count (struct lexer *lexer, struct dataset *ds)
         {
           if (var_is_alpha (dv->var))
             {
-              msg (SE, _("Destination cannot be a string variable."));
+              lex_error (lexer, _("Destination cannot be a string variable."));
               goto fail;
             }
         }
index 467a18652db46384e5b045a174f29d310f3fdee8..42fa9ff2157a531671b5aa2f173b56a1c203a10f 100644 (file)
@@ -279,8 +279,9 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns,
           if (trns->src_type != VAL_STRING
               || (have_dst_type && trns->dst_type != VAL_NUMERIC))
             {
-              msg (SE, _("CONVERT requires string input values and "
-                         "numeric output values."));
+              lex_next_error (lexer, -1, -1,
+                              _("CONVERT requires string input values and "
+                                "numeric output values."));
               return false;
             }
         }
@@ -336,7 +337,8 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
          if (lex_token (lexer) == T_ID
              && lex_id_match (ss_cstr ("THRU"), lex_tokss (lexer)))
            {
-             msg (SE, _("%s is not allowed with string variables."), "THRU");
+             lex_error (lexer, _("%s is not allowed with string variables."),
+                         "THRU");
              return false;
            }
        }
@@ -419,7 +421,7 @@ parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out)
     }
   else
     {
-      lex_error (lexer, _("expecting output value"));
+      lex_error (lexer, _("Syntax error expecting output value."));
       return false;
     }
   return true;
@@ -520,11 +522,12 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
       const struct variable *v = trns->dst_vars[i];
       if (v != NULL && var_get_type (v) != trns->dst_type)
         {
-          msg (SE, _("Type mismatch.  Cannot store %s data in "
-                     "%s variable %s."),
-               trns->dst_type == VAL_STRING ? _("string") : _("numeric"),
-               var_is_alpha (v) ? _("string") : _("numeric"),
-               var_get_name (v));
+          if (trns->dst_type == VAL_STRING)
+            msg (SE, _("Type mismatch.  Cannot store string data in "
+                       "numeric variable %s."), var_get_name (v));
+          else
+            msg (SE, _("Type mismatch.  Cannot store numeric data in "
+                       "string variable %s."), var_get_name (v));
           return false;
         }
     }
index 665cd4468edc370e80390441cb27d1ddb9fa3f27..037cff4a38854ea7afb17d2092e388b884b5c13e 100644 (file)
@@ -70,12 +70,8 @@ cmd_sample (struct lexer *lexer, struct dataset *ds)
       unsigned long max = gsl_rng_max (get_rng ());
 
       type = TYPE_FRACTION;
-      if (lex_tokval (lexer) <= 0 || lex_tokval (lexer) >= 1)
-       {
-         msg (SE, _("The sampling factor must be between 0 and 1 "
-                    "exclusive."));
-         return CMD_FAILURE;
-       }
+      if (!lex_force_num_range_open (lexer, "SAMPLE", 0, 1))
+        return CMD_FAILURE;
 
       frac = lex_tokval (lexer) * (max - min) + min;
       a = b = 0;
index deb13047e9fd7fcc49a18629e3ccb32d4417ebea..2648f9f0c8954c6045c67813d3a9c2c043498996 100644 (file)
@@ -56,7 +56,7 @@ cmd_select_if (struct lexer *lexer, struct dataset *ds)
   if (lex_token (lexer) != T_ENDCMD)
     {
       expr_free (e);
-      lex_error (lexer, _("expecting end of command"));
+      lex_error (lexer, _("Syntax error expecting end of command."));
       return CMD_CASCADING_FAILURE;
     }
 
@@ -100,35 +100,33 @@ cmd_filter (struct lexer *lexer, struct dataset *ds)
   struct dictionary *dict = dataset_dict (ds);
   if (lex_match_id (lexer, "OFF"))
     dict_set_filter (dict, NULL);
-  else if (lex_token (lexer) == T_ENDCMD)
+  else if (lex_match (lexer, T_BY))
     {
-      msg (SW, _("Syntax error expecting OFF or BY.  "
-                 "Turning off case filtering."));
-      dict_set_filter (dict, NULL);
-    }
-  else
-    {
-      struct variable *v;
-
-      lex_match (lexer, T_BY);
-      v = parse_variable (lexer, dict);
+      struct variable *v = parse_variable (lexer, dict);
       if (!v)
        return CMD_FAILURE;
 
       if (var_is_alpha (v))
        {
-         msg (SE, _("The filter variable must be numeric."));
+         lex_next_error (lexer, -1, -1,
+                          _("The filter variable must be numeric."));
          return CMD_FAILURE;
        }
 
       if (dict_class_from_id (var_get_name (v)) == DC_SCRATCH)
        {
-         msg (SE, _("The filter variable may not be scratch."));
+         lex_next_error (lexer, -1, -1,
+                          _("The filter variable may not be scratch."));
          return CMD_FAILURE;
        }
 
       dict_set_filter (dict, v);
     }
+  else
+    {
+      lex_error_expecting (lexer, "OFF", "BY");
+      return CMD_FAILURE;
+    }
 
   return CMD_SUCCESS;
 }
index 11e5b9d98eeda0694180836d8149c71ee2d24055..d994a369f8ef4411fe555de16268133831470a0b 100644 (file)
@@ -151,7 +151,7 @@ struct msg_handler
     void (*output_msg) (const struct msg *, void *aux);
     void *aux;
 
-    void (*lex_source_ref) (const struct lex_source *);
+    struct lex_source *(*lex_source_ref) (const struct lex_source *);
     void (*lex_source_unref) (struct lex_source *);
     struct substring (*lex_source_get_line) (const struct lex_source *,
                                              int line);
index e9cbb919a0c661a9ce20d86d92a46b617408489d..71e50819e0307f1e5cdc97a50c654063d9634b2a 100644 (file)
@@ -16,6 +16,32 @@ dnl along with this program.  If not, see <http://www.gnu.org/licenses/>.
 dnl
 AT_BANNER([command parser])
 
+AT_SETUP([command parser negative tests])
+AT_DATA([command.sps], [dnl
+DATA X.
+XYZZY.
+CLOSE FILE XYZZY.
+foo.
+])
+AT_CHECK([pspp command.sps], [1], [dnl
+command.sps:1.1-1.6: error: Unknown command `DATA X'.
+    1 | DATA X.
+      | ^~~~~~
+
+command.sps:2.1-2.5: error: Unknown command `XYZZY'.
+    2 | XYZZY.
+      | ^~~~~
+
+command.sps:3.1-3.16: error: Unknown command `CLOSE FILE XYZZY'.
+    3 | CLOSE FILE XYZZY.
+      | ^~~~~~~~~~~~~~~~
+
+command.sps:4.1-4.3: error: Unknown command `foo'.
+    4 | foo.
+      | ^~~
+])
+AT_CLEANUP
+
 dnl Tests for a bug which crashed pspp when given certain invalid input.
 AT_SETUP([command parser crash bug])
 AT_DATA([command.sps], [dnl
@@ -23,16 +49,20 @@ DATA rubbish.
 EXECUTE.
 ])
 AT_CHECK([pspp -O format=csv command.sps], [1], [dnl
-command.sps:1: error: Unknown command `DATA rubbish'.
+"command.sps:1.1-1.12: error: Unknown command `DATA rubbish'.
+    1 | DATA rubbish.
+      | ^~~~~~~~~~~~"
 
-command.sps:2: error: EXECUTE: EXECUTE is allowed only after the active dataset has been defined.
+"command.sps:2.1-2.7: error: EXECUTE: EXECUTE is allowed only after the active dataset has been defined.
+    2 | EXECUTE.
+      | ^~~~~~~"
 ])
 AT_CLEANUP
 
 dnl Tests for a bug where FINISH or EXIT wasn't executed until the lexer
 dnl read the first token of the next command.
 dnl
-dnl (If this reecurs, the test will run forever.)
+dnl (If this recurs, the test will run forever.)
 AT_SETUP([FINISH executes immediately])
 AT_CHECK([(echo "FINISH."; while echo ; do true; done) | pspp], [0], [ignore], [ignore])
 AT_CLEANUP
index 8ca40cbaba396e853b727753a8589c43fcf568e6..e528509b1e8bfe344aa0dc9a4ff6f933faa39ec4 100644 (file)
@@ -240,6 +240,8 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 
 define.sps:8.18: error: DEBUG EXPAND: Reached end of command expecting 1 more
 token in argument !3 to macro !p.
+    8 | !p a1 a2 b1 b2 c1.
+      |                  ^
 
 (a1 a2, b1 b2, c1)
 
@@ -247,6 +249,8 @@ token in argument !3 to macro !p.
 
 define.sps:10.12: error: DEBUG EXPAND: Reached end of command expecting 1 more
 token in argument !2 to macro !p.
+   10 | !p a1 a2 b1.
+      |            ^
 
 (a1 a2, b1, z)
 
@@ -254,6 +258,8 @@ token in argument !2 to macro !p.
 
 define.sps:12.6: error: DEBUG EXPAND: Reached end of command expecting 1 more
 token in argument !1 to macro !p.
+   12 | !p a1.
+      |      ^
 
 (a1, y, z)
 
@@ -358,6 +364,8 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 
 define.sps:8.9: error: DEBUG EXPAND: Reached end of command expecting ":" in
 argument !3 to macro !p.
+    8 | !p a,b;c.
+      |         ^
 
 (a, b, c)
 
@@ -365,6 +373,8 @@ argument !3 to macro !p.
 
 define.sps:10.7: error: DEBUG EXPAND: Reached end of command expecting ";" in
 argument !2 to macro !p.
+   10 | !p a,b.
+      |       ^
 
 (a, b, z)
 
@@ -372,6 +382,8 @@ argument !2 to macro !p.
 
 define.sps:12.5: error: DEBUG EXPAND: Reached end of command expecting "," in
 argument !1 to macro !p.
+   12 | !p a.
+      |     ^
 
 (a, y, z)
 
@@ -429,11 +441,15 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 
 define.sps:8.12: error: DEBUG EXPAND: Reached end of command expecting "}" in
 argument !3 to macro !p.
+    8 | !p (a)<b>{c.
+      |            ^
 
 (a, b, c)
 
 define.sps:9.11: error: DEBUG EXPAND: Reached end of command expecting "}" in
 argument !3 to macro !p.
+    9 | !p (a)<b>{.
+      |           ^
 
 (a, b, )
 
@@ -441,11 +457,15 @@ argument !3 to macro !p.
 
 define.sps:11.9: error: DEBUG EXPAND: Reached end of command expecting ">" in
 argument !2 to macro !p.
+   11 | !p (a)<b.
+      |         ^
 
 (a, b, z)
 
 define.sps:12.8: error: DEBUG EXPAND: Reached end of command expecting ">" in
 argument !2 to macro !p.
+   12 | !p (a)<.
+      |        ^
 
 (a, , z)
 
@@ -453,11 +473,15 @@ argument !2 to macro !p.
 
 define.sps:14.6: error: DEBUG EXPAND: Reached end of command expecting ")" in
 argument !1 to macro !p.
+   14 | !p (a.
+      |      ^
 
 (a, y, z)
 
 define.sps:15.5: error: DEBUG EXPAND: Reached end of command expecting ")" in
 argument !1 to macro !p.
+   15 | !p (.
+      |     ^
 
 (, y, z)
 
@@ -470,7 +494,9 @@ AT_DATA([define.sps], [dnl
 DEFINE !macro(!x !TOKENS(1).
 ])
 AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
-"define.sps:1.15-1.16: error: DEFINE: Syntax error at `!x': Keyword macro parameter must be named in definition without ""!"" prefix."
+"define.sps:1.15-1.16: error: DEFINE: Keyword macro parameter must be named in definition without ""!"" prefix.
+    1 | DEFINE !macro@{:@!x !TOKENS(1).
+      |               ^~"
 ])
 AT_CLEANUP
 
@@ -479,7 +505,9 @@ AT_DATA([define.sps], [dnl
 DEFINE !macro(if=!TOKENS(1).
 ])
 AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
-"define.sps:1.15-1.16: error: DEFINE: Syntax error at `if': Cannot use macro keyword ""if"" as an argument name."
+"define.sps:1.15-1.16: error: DEFINE: Cannot use macro keyword ""if"" as an argument name.
+    1 | DEFINE !macro@{:@if=!TOKENS(1).
+      |               ^~"
 ])
 AT_CLEANUP
 
@@ -516,11 +544,15 @@ AT_CAPTURE_FILE([define.sps])
 AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:3.8: error: DEBUG EXPAND: Found `.' while expecting `=' reading
 argument !arg1 to macro !k.
+    3 | !k arg1.
+      |        ^
 
 k( )
 
 define.sps:4.9: error: DEBUG EXPAND: Reached end of command expecting 1 more
 token in argument !arg1 to macro !k.
+    4 | !k arg1=.
+      |         ^
 
 k( )
 ])
@@ -568,21 +600,29 @@ AT_CAPTURE_FILE([define.sps])
 AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:6.8: error: DEBUG EXPAND: Found `.' while expecting `=' reading
 argument !arg1 to macro !k.
+    6 | !k arg1.
+      |        ^
 
 k(, )
 
 define.sps:7.9: error: DEBUG EXPAND: Reached end of command expecting "/" in
 argument !arg1 to macro !k.
+    7 | !k arg1=.
+      |         ^
 
 k(, )
 
 define.sps:8.10: error: DEBUG EXPAND: Reached end of command expecting "/" in
 argument !arg1 to macro !k.
+    8 | !k arg1=x.
+      |          ^
 
 k(x, )
 
 define.sps:9.18: error: DEBUG EXPAND: Reached end of command expecting "/" in
 argument !arg2 to macro !k.
+    9 | !k arg1=x/ arg2=y.
+      |                  ^
 
 k(x, y)
 ])
@@ -634,16 +674,22 @@ AT_CAPTURE_FILE([define.sps])
 AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:6.8: error: DEBUG EXPAND: Found `.' while expecting `=' reading
 argument !arg1 to macro !k.
+    6 | !k arg1.
+      |        ^
 
 k(, )
 
 define.sps:7.9: error: DEBUG EXPAND: Found `.' while expecting `@{:@' reading
 argument !arg1 to macro !k.
+    7 | !k arg1=.
+      |         ^
 
 k(, )
 
 define.sps:8.9: error: DEBUG EXPAND: Found `x' while expecting `@{:@' reading
 argument !arg1 to macro !k.
+    8 | !k arg1=x.
+      |         ^
 
 k(, )
 
@@ -651,21 +697,29 @@ note: unexpanded token "x"
 
 define.sps:9.11: error: DEBUG EXPAND: Reached end of command expecting "@:}@" in
 argument !arg1 to macro !k.
+    9 | !k arg1=@{:@x.
+      |           ^
 
 k(x, )
 
 define.sps:10.17: error: DEBUG EXPAND: Found `.' while expecting `=' reading
 argument !arg2 to macro !k.
+   10 | !k arg1=(x) arg2.
+      |                 ^
 
 k(x, )
 
 define.sps:11.18: error: DEBUG EXPAND: Found `.' while expecting `{' reading
 argument !arg2 to macro !k.
+   11 | !k arg1=(x) arg2=.
+      |                  ^
 
 k(x, )
 
 define.sps:12.18: error: DEBUG EXPAND: Found `y' while expecting `{' reading
 argument !arg2 to macro !k.
+   12 | !k arg1=(x) arg2=y.
+      |                  ^
 
 k(x, )
 
@@ -673,6 +727,8 @@ note: unexpanded token "y"
 
 define.sps:13.18: error: DEBUG EXPAND: Found `@{:@' while expecting `{' reading
 argument !arg2 to macro !k.
+   13 | !k arg1=(x) arg2=@{:@y.
+      |                  ^
 
 k(x, )
 
@@ -980,6 +1036,8 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1-10: At `"ba' in the expansion of `!s',dnl "
 
 define.sps:12.1-12.2: error: DEBUG EXPAND: Unterminated string constant.
+   12 | !s.
+      | ^~
 
 nana.
 nan.
@@ -1100,9 +1158,13 @@ define.sps:1-3: inside the expansion of `!macro',
 define.sps:1-3: inside the expansion of `!macro',
 define.sps:1-3: inside the expansion of `!macro',
 define.sps:1-3: inside the expansion of `!macro',
-define.sps:4.1-4.6: error: DEFINE: Maximum nesting level 50 exceeded.  (Use SET MNEST to change the limit.)"
+define.sps:4.1-4.6: error: DEFINE: Maximum nesting level 50 exceeded.  (Use SET MNEST to change the limit.)
+    4 | !macro.
+      | ^~~~~~"
 
-define.sps:4.1-4.6: error: Syntax error at `!macro' (in expansion of `!macro'): expecting command name.
+"define.sps:4.1-4.6: error: In syntax expanded from `!macro': Syntax error expecting command name.
+    4 | !macro.
+      | ^~~~~~"
 ])
 AT_CLEANUP
 
@@ -1371,12 +1433,16 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1-3: At `!x' in the expansion of `!for',
 define.sps:10.1-10.12: error: DEBUG EXPAND: Cannot use argument name or macro
 keyword as !DO variable.
+   10 | !for x=1 y=5.
+      | ^~~~~~~~~~~~
 
 !DO 1 = 1 !TO 5 !var !DOEND.
 
 define.sps:5-7: At `!noexpand' in the expansion of `!for2',
 define.sps:11.1-11.13: error: DEBUG EXPAND: Cannot use argument name or macro
 keyword as !DO variable.
+   11 | !for2 x=1 y=5.
+      | ^~~~~~~~~~~~~
 
 !DO !noexpand = 1 !TO 5 !var !DOEND.
 ])
@@ -1421,6 +1487,8 @@ In the expansion of `!DO',
 define.sps:3-5: inside the expansion of `!for',
 define.sps:14.1-14.8: error: DEBUG EXPAND: Numerical !DO loop exceeded maximum
 number of iterations 3.  (Use SET MITERATE to change the limit.)
+   14 | !for 1 5.
+      | ^~~~~~~~
 
 1 2 3 4.
 
@@ -1428,6 +1496,8 @@ In the expansion of `!DO',
 define.sps:7-9: inside the expansion of `!forby',
 define.sps:15.1-15.12: error: DEBUG EXPAND: Numerical !DO loop exceeded maximum
 number of iterations 3.  (Use SET MITERATE to change the limit.)
+   15 | !forby 1 5 1.
+      | ^~~~~~~~~~~~
 
 1 2 3 4.
 
@@ -1447,6 +1517,8 @@ In the expansion of `!DO',
 define.sps:7-9: inside the expansion of `!forby',
 define.sps:23.1-23.13: error: DEBUG EXPAND: Numerical !DO loop exceeded maximum
 number of iterations 3.  (Use SET MITERATE to change the limit.)
+   23 | !forby 5 1 -1.
+      | ^~~~~~~~~~~~~
 
 5 4 3 2.
 
@@ -1526,6 +1598,8 @@ In the expansion of `!DO',
 define.sps:1-3: inside the expansion of `!for',
 define.sps:7.1-7.10: error: DEBUG EXPAND: !DO loop over list exceeded maximum
 number of iterations 2.  (Use SET MITERATE to change the limit.)
+    7 | !for a b c.
+      | ^~~~~~~~~~
 
 ( (a) (b) ).
 
@@ -1533,6 +1607,8 @@ In the expansion of `!DO',
 define.sps:1-3: inside the expansion of `!for',
 define.sps:8.1-8.23: error: DEBUG EXPAND: !DO loop over list exceeded maximum
 number of iterations 2.  (Use SET MITERATE to change the limit.)
+    8 | !for 'foo bar baz quux'.
+      | ^~~~~~~~~~~~~~~~~~~~~~~
 
 ( (foo) (bar) ).
 
@@ -1607,15 +1683,21 @@ DEBUG EXPAND.
 ])
 AT_CHECK([pspp --testing-mode define.sps -O format=csv], [1], [dnl
 "define.sps:1-3: At `!x' in the expansion of `!macro',
-define.sps:10.1-10.10: error: DEBUG EXPAND: Cannot use argument name or macro keyword ""!x"" as !LET variable."
+define.sps:10.1-10.10: error: DEBUG EXPAND: Cannot use argument name or macro keyword ""!x"" as !LET variable.
+   10 | !macro x=1.
+      | ^~~~~~~~~~"
 
 !LET 1 = 1
 
 "define.sps:5-7: At `!do' in the expansion of `!macro2',
-define.sps:11.1-11.7: error: DEBUG EXPAND: Cannot use argument name or macro keyword ""!do"" as !LET variable."
+define.sps:11.1-11.7: error: DEBUG EXPAND: Cannot use argument name or macro keyword ""!do"" as !LET variable.
+   11 | !macro2.
+      | ^~~~~~~"
 
 "define.sps:5-7: At `=' in the expansion of `!macro2',
-define.sps:11.1-11.7: error: DEBUG EXPAND: Expected macro variable name following !DO."
+define.sps:11.1-11.7: error: DEBUG EXPAND: Expected macro variable name following !DO.
+   11 | !macro2.
+      | ^~~~~~~"
 
 !LET !do = x
 ])
@@ -1668,7 +1750,9 @@ DATA LIST NOTABLE /a b 1-2.
 COMPUTE x = !vars x.
 ])
 AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
-define.sps:3.13-3.19: error: COMPUTE: Syntax error at `b' (in expansion of `!vars x'): expecting end of command.
+"define.sps:3.13-3.19: error: COMPUTE: In syntax expanded from `!vars x': Syntax error expecting end of command.
+    3 | COMPUTE x = !vars x.
+      |             ^~~~~~~"
 ])
 AT_CLEANUP
 
@@ -1768,48 +1852,64 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:3: At `x' in the expansion of `!c',
 define.sps:20.1-20.2: error: DEBUG EXPAND: `,' or `@:}@' expected in call to macro
 function !SUBSTR.
+   20 | !c.
+      | ^~
 
 !SUBSTR(1 x)
 
 define.sps:4: In the expansion of `!d',
 define.sps:21.1-21.2: error: DEBUG EXPAND: Missing `@:}@' in call to macro
 function !SUBSTR.
+   21 | !d.
+      | ^~
 
 !SUBSTR@{:@1
 
 define.sps:5: In the expansion of `!narg_blanks',
 define.sps:22.1-22.12: error: DEBUG EXPAND: Macro function !BLANKS takes one
 argument (not 0).
+   22 | !narg_blanks.
+      | ^~~~~~~~~~~~
 
 !BLANKS( )
 
 define.sps:6: In the expansion of `!narg_concat',
 define.sps:23.1-23.12: error: DEBUG EXPAND: Macro function !CONCAT needs at
 least one argument.
+   23 | !narg_concat.
+      | ^~~~~~~~~~~~
 
 !CONCAT( )
 
 define.sps:7: In the expansion of `!narg_eval',
 define.sps:24.1-24.10: error: DEBUG EXPAND: Macro function !EVAL takes one
 argument (not 0).
+   24 | !narg_eval.
+      | ^~~~~~~~~~
 
 !EVAL( )
 
 define.sps:8: In the expansion of `!narg_head',
 define.sps:25.1-25.10: error: DEBUG EXPAND: Macro function !HEAD takes one
 argument (not 0).
+   25 | !narg_head.
+      | ^~~~~~~~~~
 
 !HEAD( )
 
 define.sps:9: In the expansion of `!narg_index',
 define.sps:26.1-26.11: error: DEBUG EXPAND: Macro function !INDEX takes two
 arguments (not 0).
+   26 | !narg_index.
+      | ^~~~~~~~~~~
 
 !INDEX( )
 
 define.sps:10: In the expansion of `!narg_length',
 define.sps:27.1-27.12: error: DEBUG EXPAND: Macro function !LENGTH takes one
 argument (not 0).
+   27 | !narg_length.
+      | ^~~~~~~~~~~~
 
 !LENGTH( )
 
@@ -1818,30 +1918,40 @@ argument (not 0).
 define.sps:12: In the expansion of `!narg_quote',
 define.sps:29.1-29.11: error: DEBUG EXPAND: Macro function !QUOTE takes one
 argument (not 0).
+   29 | !narg_quote.
+      | ^~~~~~~~~~~
 
 !QUOTE( )
 
 define.sps:13: In the expansion of `!narg_substr',
 define.sps:30.1-30.12: error: DEBUG EXPAND: Macro function !SUBSTR takes two or
 three arguments (not 0).
-
+   30 | !narg_substr.
+      | ^~~~~~~~~~~~
 !SUBSTR( )
 
 define.sps:14: In the expansion of `!narg_tail',
 define.sps:31.1-31.10: error: DEBUG EXPAND: Macro function !TAIL takes one
 argument (not 0).
+   31 | !narg_tail.
+      | ^~~~~~~~~~
 
 !TAIL( )
 
 define.sps:15: In the expansion of `!narg_unquote',
 define.sps:32.1-32.13: error: DEBUG EXPAND: Macro function !UNQUOTE takes one
 argument (not 0).
+   32 | !narg_unquote.
+      | ^~~~~~~~~~~~~
 
 !UNQUOTE( )
 
 define.sps:16: In the expansion of `!narg_upcase',
 define.sps:33.1-33.12: error: DEBUG EXPAND: Macro function !UPCASE takes one
 argument (not 0).
+   33 | !narg_upcase.
+      | ^~~~~~~~~~~~
 
 !UPCASE( )
 ])
@@ -1861,18 +1971,24 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1: In the expansion of `!a',
 define.sps:5.1-5.2: error: DEBUG EXPAND: Argument to !BLANKS must be non-
 negative integer (not "x").
+    5 | !a.
+      | ^~
 
 !BLANKS(x).
 
 define.sps:2: In the expansion of `!b',
 define.sps:6.1-6.2: error: DEBUG EXPAND: Second argument of !SUBSTR must be
 positive integer (not "y").
+    6 | !b.
+      | ^~
 
 !SUBSTR(x, y).
 
 define.sps:3: In the expansion of `!c',
 define.sps:7.1-7.2: error: DEBUG EXPAND: Third argument of !SUBSTR must be non-
 negative integer (not "z").
+    7 | !c.
+      | ^~
 
 !SUBSTR(x, 1, z).
 ])
@@ -1892,6 +2008,8 @@ DEBUG EXPAND.
 AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1-2: At `.' in the expansion of `!a',
 define.sps:5.1-5.2: error: DEBUG EXPAND: Expecting ')' in macro expression.
+    5 | !a.
+      | ^~
 
 !LET !x = (1.
 
@@ -1899,12 +2017,16 @@ At `x' in the expansion of `!DO',
 define.sps:2: inside the expansion of `!b',
 define.sps:6.1-6.2: error: DEBUG EXPAND: Macro expression must evaluate to a
 number (not "x").
+    6 | !b.
+      | ^~
 
 !DO !x = x.
 
 define.sps:3: At `)' in the expansion of `!c',
 define.sps:7.1-7.2: error: DEBUG EXPAND: Expecting literal or function
 invocation in macro expression.
+    7 | !c.
+      | ^~
 
 !LET !x = ( ).
 ])
@@ -1924,18 +2046,24 @@ DEBUG EXPAND.
 AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1: In the expansion of `!a',
 define.sps:5.1-5.2: error: DEBUG EXPAND: !THEN expected in macro !IF construct.
+    5 | !a.
+      | ^~
 
 !IF 1
 
 define.sps:2: In the expansion of `!b',
 define.sps:6.1-6.2: error: DEBUG EXPAND: !ELSE or !IFEND expected in macro !IF
 construct.
+    6 | !b.
+      | ^~
 
 !IF 1 !THEN
 
 define.sps:3: In the expansion of `!c',
 define.sps:7.1-7.2: error: DEBUG EXPAND: !IFEND expected in macro !IF
 construct.
+    7 | !c.
+      | ^~
 
 !IF 1 !THEN !ELSE
 ])
@@ -1958,22 +2086,30 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1: In the expansion of `!a',
 define.sps:6.1-6.2: error: DEBUG EXPAND: Expected macro variable name following
 !LET.
+    6 | !a.
+      | ^~
 
 !LET
 
 define.sps:2: At `0' in the expansion of `!b',
 define.sps:7.1-7.2: error: DEBUG EXPAND: Expected macro variable name following
 !LET.
+    7 | !b.
+      | ^~
 
 !LET 0
 
 define.sps:3: In the expansion of `!c',
 define.sps:8.1-8.2: error: DEBUG EXPAND: Expected `=' following !LET.
+    8 | !c.
+      | ^~
 
 !LET !x
 
 define.sps:4: At `y' in the expansion of `!d',
 define.sps:9.1-9.2: error: DEBUG EXPAND: Expected `=' following !LET.
+    9 | !d.
+      | ^~
 
 !LET !x y
 ])
@@ -2008,23 +2144,31 @@ AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
 define.sps:1: In the expansion of `!a',
 define.sps:12.1-12.2: error: DEBUG EXPAND: Expected macro variable name
 following !DO.
+   12 | !a.
+      | ^~
 
 !DO
 
 define.sps:2: At `0' in the expansion of `!b',
 define.sps:13.1-13.2: error: DEBUG EXPAND: Expected macro variable name
 following !DO.
+   13 | !b.
+      | ^~
 
 !DO 0
 
 define.sps:3: In the expansion of `!c',
 define.sps:14.1-14.2: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+   14 | !c.
+      | ^~
 
 !DO !x
 
 In the expansion of `!DO',
 define.sps:4: inside the expansion of `!d',
 define.sps:15.1-15.2: error: DEBUG EXPAND: Missing !DOEND.
+   15 | !d.
+      | ^~
 
 !DO !x !in(x)
 
@@ -2032,33 +2176,45 @@ At `x' in the expansion of `!DO',
 define.sps:5: inside the expansion of `!e',
 define.sps:16.1-16.2: error: DEBUG EXPAND: Macro expression must evaluate to a
 number (not "x").
+   16 | !e.
+      | ^~
 
 !DO !x = x.
 
 At `x' in the expansion of `!DO',
 define.sps:6: inside the expansion of `!f',
 define.sps:17.1-17.2: error: DEBUG EXPAND: Expected !TO in numerical !DO loop.
+   17 | !f.
+      | ^~
 
 !DO !x = 5 x
 
 In the expansion of `!DO',
 define.sps:7: inside the expansion of `!g',
 define.sps:18.1-18.2: error: DEBUG EXPAND: !BY value cannot be zero.
+   18 | !g.
+      | ^~
 
 !DO !x = 5 !TO 6 !BY 0
 
 define.sps:8: In the expansion of `!h',
 define.sps:19.1-19.2: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+   19 | !h.
+      | ^~
 
 !DO !x
 
 define.sps:9: At `0' in the expansion of `!i',
 define.sps:20.1-20.2: error: DEBUG EXPAND: Expected `=' or !IN in !DO loop.
+   20 | !i.
+      | ^~
 
 !DO !x 0
 
 define.sps:10: At `!BREAK' in the expansion of `!j',
 define.sps:21.1-21.2: error: DEBUG EXPAND: !BREAK outside !DO.
+   21 | !j.
+      | ^~
 
 ])
 AT_CLEANUP
@@ -2106,63 +2262,114 @@ DEFINE !macro(x=!TOKENS(1) !CMDEND) !ENDDEFINE.
 DEFINE !macro()
 ])
 AT_CHECK([pspp define.sps], [1], [dnl
-define.sps:1.36-1.40: error: DEFINE: Syntax error at `'x y'': String must
-contain exactly one token.
-
-define.sps:2.40-2.46: error: DEFINE: Syntax error at `!TOKENS': Positional
-parameters must precede keyword parameters.
-
-define.sps:3.15-3.16: error: DEFINE: Syntax error at `!a': Keyword macro
-parameter must be named in definition without "!" prefix.
-
-define.sps:4.15-4.16: error: DEFINE: Syntax error at `do': Cannot use macro
-keyword "do" as an argument name.
-
-define.sps:5.8: error: DEFINE: Syntax error at `0': expecting identifier.
-
-define.sps:6.10: error: DEFINE: Syntax error at `y': expecting `@{:@'.
-
-define.sps:7.15: error: DEFINE: Syntax error at `1': expecting identifier.
-
-define.sps:8.17: error: DEFINE: Syntax error at `2': expecting !TOKENS, !
+define.sps:1.36-1.40: error: DEFINE: String must contain exactly one token.
+    1 | DEFINE !macro(!POSITIONAL !CHAREND('x y')) !ENDDEFINE.
+      |                                    ^~~~~
+
+define.sps:2.28-2.38: error: DEFINE: Positional parameters must precede keyword
+parameters.
+    2 | DEFINE !macro(a=!TOKENS(1)/!POSITIONAL !TOKENS(1)) !ENDDEFINE.
+      |                            ^~~~~~~~~~~
+
+define.sps:2.15: note: DEFINE: Here is a previous keyword parameter.
+    2 | DEFINE !macro(a=!TOKENS(1)/!POSITIONAL !TOKENS(1)) !ENDDEFINE.
+      |               ^
+
+define.sps:3.15-3.16: error: DEFINE: Keyword macro parameter must be named in
+definition without "!" prefix.
+    3 | DEFINE !macro(!a=!TOKENS(1)) !ENDDEFINE.
+      |               ^~
+
+define.sps:4.15-4.16: error: DEFINE: Cannot use macro keyword "do" as an
+argument name.
+    4 | DEFINE !macro(do=!TOKENS(1)) !ENDDEFINE.
+      |               ^~
+
+define.sps:5.8: error: DEFINE: Syntax error expecting identifier.
+    5 | DEFINE 0() !ENDDEFINE.
+      |        ^
+
+define.sps:6.10: error: DEFINE: Syntax error expecting `@{:@'.
+    6 | DEFINE x y () !ENDDEFINE.
+      |          ^
+
+define.sps:7.15: error: DEFINE: Syntax error expecting identifier.
+    7 | DEFINE !macro(1) !ENDDEFINE.
+      |               ^
+
+define.sps:8.17: error: DEFINE: Syntax error expecting !TOKENS, !CHAREND, !
+ENCLOSE, or !CMDEND.
+    8 | DEFINE !macro(x 2) !ENDDEFINE.
+      |                 ^
+
+define.sps:9.26: error: DEFINE: Syntax error expecting `@{:@'.
+    9 | DEFINE !macro(x=!DEFAULT 3) !ENDDEFINE.
+      |                          ^
+
+define.sps:10.25: error: DEFINE: Syntax error expecting `@{:@'.
+   10 | DEFINE !macro(x=!TOKENS 4) !ENDDEFINE.
+      |                         ^
+
+define.sps:11.25: error: DEFINE: Syntax error expecting positive integer for !
+TOKENS.
+   11 | DEFINE !macro(x=!TOKENS(x)) !ENDDEFINE.
+      |                         ^
+
+define.sps:12.27: error: DEFINE: Syntax error expecting `@:}@'.
+   12 | DEFINE !macro(x=!TOKENS(1 5)) !ENDDEFINE.
+      |                           ^
+
+define.sps:13.26: error: DEFINE: Syntax error expecting `@{:@'.
+   13 | DEFINE !macro(x=!ENCLOSE 6) !ENDDEFINE.
+      |                          ^
+
+define.sps:14.30: error: DEFINE: Syntax error expecting `,'.
+   14 | DEFINE !macro(x=!ENCLOSE('x' y)) !ENDDEFINE.
+      |                              ^
+
+define.sps:15.30: error: DEFINE: Syntax error expecting string.
+   15 | DEFINE !macro(x=!ENCLOSE('x',y)) !ENDDEFINE.
+      |                              ^
+
+define.sps:16.34: error: DEFINE: Syntax error expecting `@:}@'.
+   16 | DEFINE !macro(x=!ENCLOSE('x','y' z)) !ENDDEFINE.
+      |                                  ^
+
+define.sps:17.26: error: DEFINE: Syntax error expecting `@{:@'.
+   17 | DEFINE !macro(x=!CHAREND 7) !ENDDEFINE.
+      |                          ^
+
+define.sps:18.26: error: DEFINE: Syntax error expecting string.
+   18 | DEFINE !macro(x=!CHAREND(8)) !ENDDEFINE.
+      |                          ^
+
+define.sps:19.30: error: DEFINE: Syntax error expecting `@:}@'.
+   19 | DEFINE !macro(x=!CHAREND('x' 9)) !ENDDEFINE.
+      |                              ^
+
+define.sps:20.17-20.20: error: DEFINE: Syntax error expecting !TOKENS, !
 CHAREND, !ENCLOSE, or !CMDEND.
+   20 | DEFINE !macro(x=!WTF) !ENDDEFINE.
+      |                 ^~~~
 
-define.sps:9.26: error: DEFINE: Syntax error at `3': expecting `@{:@'.
-
-define.sps:10.25: error: DEFINE: Syntax error at `4': expecting `('.
-
-define.sps:11.25: error: DEFINE: Syntax error at `x': Expected positive integer
-for !TOKENS.
-
-define.sps:12.27: error: DEFINE: Syntax error at `5': expecting `)'.
-
-define.sps:13.26: error: DEFINE: Syntax error at `6': expecting `('.
+define.sps:21.28: error: DEFINE: Syntax error expecting `/'.
+   21 | DEFINE !macro(x=!TOKENS(1) x) !ENDDEFINE.
+      |                            ^
 
-define.sps:14.30: error: DEFINE: Syntax error at `y': expecting `,'.
+define.sps:22.28-22.35: error: DEFINE: !DEFAULT is allowed only once per
+argument.
+   22 | DEFINE !macro(x=!DEFAULT() !DEFAULT()) !ENDDEFINE.
+      |                            ^~~~~~~~
 
-define.sps:15.30: error: DEFINE: Syntax error at `y': expecting string.
+define.sps:23.28-23.34: error: DEFINE: Only one of !TOKENS, !CHAREND, !ENCLOSE,
+or !CMDEND is allowed.
+   23 | DEFINE !macro(x=!TOKENS(1) !CMDEND) !ENDDEFINE.
+      |                            ^~~~~~~
 
-define.sps:16.34: error: DEFINE: Syntax error at `z': expecting `)'.
-
-define.sps:17.26: error: DEFINE: Syntax error at `7': expecting `('.
-
-define.sps:18.26: error: DEFINE: Syntax error at `8': expecting string.
-
-define.sps:19.30: error: DEFINE: Syntax error at `9': expecting `)'.
-
-define.sps:20.17-20.20: error: DEFINE: Syntax error at `!WTF': expecting !
-TOKENS, !CHAREND, !ENCLOSE, or !CMDEND.
-
-define.sps:21.28: error: DEFINE: Syntax error at `x': expecting `/'.
-
-define.sps:22.36: error: DEFINE: Syntax error at `(': !DEFAULT is allowed only
-once per argument.
-
-define.sps:23.35: error: DEFINE: Syntax error at `)': Only one of !TOKENS, !
-CHAREND, !ENCLOSE, or !CMDEND is allowed.
-
-define.sps:25.1: error: DEFINE: Syntax error at end of command: Expecting macro
-body or !ENDDEFINE.
+define.sps:25.1: error: DEFINE: Syntax error expecting macro body or !
+ENDDEFINE.
+   25 |
+      | ^
 ])
 AT_CLEANUP
 
@@ -2203,20 +2410,29 @@ foobar
 
 two
 
-define.sps:13.12-13.20: error: ECHO: Syntax error at `"strings"': expecting end
-of command.
-
-define.sps:14.12-14.17: error: N OF CASES: Syntax error at `-/**/1': Expected
-positive integer for N OF CASES.
-
-define.sps:15.12-15.19: error: N OF CASES: Syntax error at `!minus 1': Expected
-positive integer for N OF CASES.
-
-define.sps:16.12-16.17: error: N OF CASES: Syntax error at `- !one': Expected
-positive integer for N OF CASES.
-
-define.sps:17.12-17.22: error: N OF CASES: Syntax error at `!minus !one':
-Expected positive integer for N OF CASES.
+define.sps:13.12-13.20: error: ECHO: Syntax error expecting end of command.
+   13 | ECHO "two" "strings".
+      |            ^~~~~~~~~
+
+define.sps:14.12-14.17: error: N OF CASES: Syntax error expecting positive
+integer for N OF CASES.
+   14 | N OF CASES -/**/1.
+      |            ^~~~~~
+
+define.sps:15.12-15.19: error: N OF CASES: Syntax error expecting positive
+integer for N OF CASES.
+   15 | N OF CASES !minus 1.
+      |            ^~~~~~~~
+
+define.sps:16.12-16.17: error: N OF CASES: Syntax error expecting positive
+integer for N OF CASES.
+   16 | N OF CASES - !one.
+      |            ^~~~~~
+
+define.sps:17.12-17.22: error: N OF CASES: Syntax error expecting positive
+integer for N OF CASES.
+   17 | N OF CASES !minus !one.
+      |            ^~~~~~~~~~~
 ])
 AT_CLEANUP
 
index 1a1889bb97ff65ab5863d9af7bb41059b4846b34..04e965b226ba675a77827929306b8d5ad30043fd 100644 (file)
@@ -98,7 +98,9 @@ do-if.sps:7: error: ELSE: This command cannot appear outside DO IF...END IF.
 
 do-if.sps:8: error: ELSE IF: This command cannot appear outside DO IF...END IF.
 
-do-if.sps:12: error: DO IF: Only one ELSE is allowed within DO IF...END IF.
+"do-if.sps:12.1-12.4: error: DO IF: Only one ELSE is allowed within DO IF...END IF.
+   12 | ELSE.
+      | ^~~~"
 
 "do-if.sps:11.1-11.5: note: DO IF: This is the location of the previous ELSE clause.
    11 | ELSE.
@@ -108,7 +110,9 @@ do-if.sps:12: error: DO IF: Only one ELSE is allowed within DO IF...END IF.
    10 | DO IF 0.
       | ^~~~~~~~"
 
-do-if.sps:17: error: DO IF: ELSE IF is not allowed following ELSE within DO IF...END IF.
+"do-if.sps:17.1-17.7: error: DO IF: ELSE IF is not allowed following ELSE within DO IF...END IF.
+   17 | ELSE IF 0.
+      | ^~~~~~~"
 
 "do-if.sps:16.1-16.5: note: DO IF: This is the location of the previous ELSE clause.
    16 | ELSE.
@@ -118,8 +122,10 @@ do-if.sps:17: error: DO IF: ELSE IF is not allowed following ELSE within DO IF..
    15 | DO IF 0.
       | ^~~~~~~~"
 
-do-if.sps:20.7: error: DO IF: Syntax error at `!'.
+"do-if.sps:20.7: error: DO IF: Syntax error.
+   20 | DO IF !.
+      |       ^"
 
-error: DO IF: Syntax error at end of input.
+error: DO IF: At end of input: Syntax error.
 ])
 AT_CLEANUP
index 91f6b4bd1bb91c8c1e838ad827404baa4c07a701..b40f7cf541259400c45952df4521bfedb8212555 100644 (file)
@@ -161,6 +161,6 @@ DATA LIST NOTABLE /x 1.
 DO REPEAT y = 1 TO 10.
 ])
 AT_CHECK([pspp -O format=csv do-repeat.sps], [1], [dnl
-error: DO REPEAT: Syntax error at end of input: expecting END.
+error: DO REPEAT: At end of input: Syntax error expecting END.
 ])
 AT_CLEANUP
index d8ed2878854904b97ddfacde469bbb9ed479486d..7f1f6e3b3227a4af87e8146cee1216478b833041 100644 (file)
@@ -310,32 +310,56 @@ LOOP.
 
 loop.sps:5: error: LOOP: Only one index clause may be specified.
 
-loop.sps:7.6: error: LOOP: Syntax error at `5'.
+loop.sps:7.6: error: LOOP: Syntax error.
+    7 | LOOP 5.
+      |      ^
 
-loop.sps:9.8: error: LOOP: Syntax error at `!': expecting `='.
+loop.sps:9.8: error: LOOP: Syntax error expecting `='.
+    9 | LOOP B !.
+      |        ^
 
-loop.sps:11.8: error: LOOP: Syntax error at `!'.
+loop.sps:11.8: error: LOOP: Syntax error.
+   11 | LOOP B=!.
+      |        ^
 
-loop.sps:13.13: error: LOOP: Syntax error at `!'.
+loop.sps:13.13: error: LOOP: Syntax error.
+   13 | LOOP A=1 TO !.
+      |             ^
 
-loop.sps:15.13: error: LOOP: Syntax error at `!'.
+loop.sps:15.13: error: LOOP: Syntax error.
+   15 | LOOP A=1 BY !.
+      |             ^
 
-loop.sps:17: error: LOOP: Subcommand TO may only be specified once.
+loop.sps:17.20-17.21: error: LOOP: Subcommand TO may only be specified once.
+   17 | LOOP A=1 TO 5 BY 5 TO !.
+      |                    ^~
 
-loop.sps:19: error: LOOP: Subcommand BY may only be specified once.
+loop.sps:19.21-19.22: error: LOOP: Subcommand BY may only be specified once.
+   19 | LOOP A=1 BY 5 TO 10 BY !.
+      |                     ^~
 
 loop.sps:21: error: LOOP: Required subcommand TO was not specified.
 
-loop.sps:23.6: error: LOOP: Syntax error at `!'.
+loop.sps:23.6: error: LOOP: Syntax error.
+   23 | LOOP !.
+      |      ^
 
-loop.sps:26: error: LOOP: Subcommand IF may only be specified once.
+loop.sps:26.11-26.12: error: LOOP: Subcommand IF may only be specified once.
+   26 | LOOP IF 1 IF 0.
+      |           ^~
 
-loop.sps:29.9: error: LOOP: Syntax error at `!'.
+loop.sps:29.9: error: LOOP: Syntax error.
+   29 | LOOP IF !.
+      |         ^
 
-loop.sps:33.13: error: LOOP: Syntax error at `!'.
+loop.sps:33.13: error: LOOP: Syntax error.
+   33 | END LOOP IF !.
+      |             ^
 
-loop.sps:36.10: error: LOOP: Syntax error at `!': expecting end of command.
+loop.sps:36.10: error: LOOP: Syntax error expecting end of command.
+   36 | END LOOP !.
+      |          ^
 
-error: LOOP: Syntax error at end of input.
+error: LOOP: At end of input: Syntax error.
 ])
 AT_CLEANUP
\ No newline at end of file
index ee576ae474451670fde52863d16de6bc96951422..d7e98db79ef5fa8c88bdc70e3144cfcb5d9b8aef 100644 (file)
@@ -410,7 +410,9 @@ EXECUTE.
 ])
 
 AT_CHECK([pspp -O format=csv data-list.sps], [1], [dnl
-data-list.sps:1.41-1.42: error: DATA LIST: Syntax error at `-1': Expected non-negative integer for SKIP.
+"data-list.sps:1.41-1.42: error: DATA LIST: Syntax error expecting non-negative integer for SKIP.
+    1 | DATA LIST LIST FILE='f.in' NOTABLE SKIP=-1 /a b c d.
+      |                                         ^~"
 
 data-list.sps:3: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
 ])
@@ -426,7 +428,9 @@ EXECUTE.
 ])
 
 AT_CHECK([pspp -O format=csv data-list.sps], [1], [dnl
-data-list.sps:1.44-1.45: error: DATA LIST: Syntax error at `-1': Expected non-negative integer for RECORDS.
+"data-list.sps:1.44-1.45: error: DATA LIST: Syntax error expecting non-negative integer for RECORDS.
+    1 | DATA LIST LIST FILE='f.in' NOTABLE RECORDS=-1 /a b c d.
+      |                                            ^~"
 
 data-list.sps:3: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
 ])
index ea3bcc6f095d98c1c0c442a52fe8b9f4866db90c..c484f16b8114fb98a7a6c6be42bbf16b88c6f4c9 100644 (file)
@@ -132,7 +132,9 @@ x
 Table: Datasets
 second (active dataset)
 
-dataset.pspp:10: error: LIST: LIST is allowed only after the active dataset has been defined.
+"dataset.pspp:10.1-10.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+   10 | LIST.
+      | ^~~~"
 ])
 AT_CLEANUP
 
@@ -213,7 +215,9 @@ one (active dataset)
 
 5 @&t@
 
-dataset.pspp:16: error: LIST: LIST is allowed only after the active dataset has been defined.
+"dataset.pspp:16.1-16.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+   16 | LIST.
+      | ^~~~"
 
 Table: Data List
 x
index 950c2ad7a96cfc66ae231c434dbc60ab18c1fce8..f09dbabc3c6d5409a4b0ddc15a728126b5675226 100644 (file)
@@ -315,15 +315,25 @@ get data /type=txt /file='test.data' /importcase=percent 95 /variables x f8.0.
 get data /type=txt /file='test.data' /importcase=percent 100 /variables x f8.0.
 ])
 AT_CHECK([pspp -O format=csv get-data.sps], [0], [dnl
-get-data.sps:1: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.)
+"get-data.sps:1.39-1.57: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.).
+    1 | get data /type=txt /file='test.data' /importcase=first 10 /variables x f8.0.
+      |                                       ^~~~~~~~~~~~~~~~~~~"
 
-get-data.sps:2: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.)
+"get-data.sps:2.39-2.58: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.).
+    2 | get data /type=txt /file='test.data' /importcase=percent 1 /variables x f8.0.
+      |                                       ^~~~~~~~~~~~~~~~~~~~"
 
-get-data.sps:3: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.)
+"get-data.sps:3.39-3.59: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.).
+    3 | get data /type=txt /file='test.data' /importcase=percent 35 /variables x f8.0.
+      |                                       ^~~~~~~~~~~~~~~~~~~~~"
 
-get-data.sps:4: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.)
+"get-data.sps:4.39-4.59: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.).
+    4 | get data /type=txt /file='test.data' /importcase=percent 95 /variables x f8.0.
+      |                                       ^~~~~~~~~~~~~~~~~~~~~"
 
-get-data.sps:5: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.)
+"get-data.sps:5.39-5.60: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand.  (N OF CASES or SAMPLE may be used to substitute.).
+    5 | get data /type=txt /file='test.data' /importcase=percent 100 /variables x f8.0.
+      |                                       ^~~~~~~~~~~~~~~~~~~~~~"
 ])
 AT_CLEANUP
 
index 0e93ed654a042cc5795acfd827ea5d659b126c6f..79c71be8b66e5c5aac9d7cb7f40ac30834d5a9b8 100644 (file)
@@ -28,7 +28,9 @@ END DATA.
 END INPUT PROGRAM.
 ])
 AT_CHECK([pspp -O format=csv input-program.sps], [1], [dnl
-input-program.sps:3: error: BEGIN DATA: BEGIN DATA is not allowed inside INPUT PROGRAM.
+"input-program.sps:3.1-3.10: error: BEGIN DATA: BEGIN DATA is not allowed inside INPUT PROGRAM.
+    3 | BEGIN DATA
+      | ^~~~~~~~~~"
 ])
 AT_CLEANUP
 
@@ -44,7 +46,7 @@ END INPUT PROGRAM.
 DESCRIPTIVES x.
 ])
 AT_CHECK([pspp -O format=csv input-program.sps], [1], [dnl
-error: DESCRIPTIVES: Syntax error at end of input: expecting BEGIN.
+error: DESCRIPTIVES: At end of input: Syntax error expecting BEGIN.
 ])
 AT_CLEANUP
 
@@ -59,7 +61,9 @@ EXECUTE.
 AT_CHECK([pspp -O format=csv input-program.sps], [1], [dnl
 input-program.sps:3: error: INPUT PROGRAM: Input program must contain DATA LIST or END FILE.
 
-input-program.sps:4: error: EXECUTE: EXECUTE is allowed only after the active dataset has been defined.
+"input-program.sps:4.1-4.7: error: EXECUTE: EXECUTE is allowed only after the active dataset has been defined.
+    4 | EXECUTE.
+      | ^~~~~~~"
 ])
 AT_CLEANUP
 
index 50017c3389f410de5e660c71cb3e03570f700181..9f65d6eca7f033ca8f3cf9a42e37910f43065bfb 100644 (file)
@@ -211,7 +211,9 @@ list.
 ])
 
 AT_CHECK([pspp -O format=csv matrix-data.sps], [0], [dnl
-matrix-data.sps:4: warning: MATRIX DATA: CELLS is ignored when VARIABLES includes ROWTYPE_
+"matrix-data.sps:4.6-4.10: warning: MATRIX DATA: CELLS is ignored when VARIABLES includes ROWTYPE_.
+    4 |     /cells = 2.
+      |      ^~~~~"
 
 Table: Data List
 ROWTYPE_,VARNAME_,var01,var02,var03,var04
@@ -1185,7 +1187,9 @@ MATRIX DATA VARIABLES=v/XYZZY.
 AT_CHECK([pspp matrix-data.sps -O format=csv], [1], [dnl
 matrix-data.sps:1: error: MATRIX DATA: VARIABLES may not include VARNAME_.
 
-matrix-data.sps:2: error: MATRIX DATA: Variable v appears twice in variable list.
+"matrix-data.sps:2.25: error: MATRIX DATA: Variable v appears twice in variable list.
+    2 | MATRIX DATA VARIABLES=v v v.
+      |                         ^"
 
 matrix-data.sps:3: error: MATRIX DATA: ROWTYPE_ is not allowed on SPLIT or FACTORS.
 
@@ -1205,29 +1209,53 @@ matrix-data.sps:13: error: MATRIX DATA: VARIABLES includes ROWTYPE_ but the cont
 
 matrix-data.sps:14: error: MATRIX DATA: Cannot specify N on CONTENTS along with the N subcommand.
 
-matrix-data.sps:15.35-15.39: error: MATRIX DATA: Syntax error at `XYZZY': Row type keyword expected.
+"matrix-data.sps:15.35-15.39: error: MATRIX DATA: Syntax error expecting one of the following: CORR, COV, MAT, N_MATRIX, PROX, COUNT, DFE, MEAN, MSE, STDDEV, N, N_SCALAR, N_VECTOR, SD.
+   15 | MATRIX DATA VARIABLES=v1/CONTENTS=XYZZY.
+      |                                   ^~~~~"
 
-matrix-data.sps:16.36: error: MATRIX DATA: Syntax error at end of command: Row type keyword expected.
+"matrix-data.sps:16.36: error: MATRIX DATA: Syntax error expecting one of the following: CORR, COV, MAT, N_MATRIX, PROX, COUNT, DFE, MEAN, MSE, STDDEV, N, N_SCALAR, N_VECTOR, SD.
+   16 | MATRIX DATA VARIABLES=v1/CONTENTS=@{:@.
+      |                                    ^"
 
-matrix-data.sps:17.40: error: MATRIX DATA: Syntax error at end of command: Row type keyword expected.
+"matrix-data.sps:17.40: error: MATRIX DATA: Syntax error expecting one of the following: CORR, COV, MAT, N_MATRIX, PROX, COUNT, DFE, MEAN, MSE, STDDEV, N, N_SCALAR, N_VECTOR, SD.
+   17 | MATRIX DATA VARIABLES=v1/CONTENTS=@{:@CORR.
+      |                                        ^"
 
-matrix-data.sps:18.35: error: MATRIX DATA: Syntax error at `)': Row type keyword expected.
+"matrix-data.sps:18.35: error: MATRIX DATA: Syntax error expecting one of the following: CORR, COV, MAT, N_MATRIX, PROX, COUNT, DFE, MEAN, MSE, STDDEV, N, N_SCALAR, N_VECTOR, SD.
+   18 | MATRIX DATA VARIABLES=v1/CONTENTS=@:}@.
+      |                                   ^"
 
-matrix-data.sps:19.12: error: MATRIX DATA: Syntax error at end of command: expecting VARIABLES.
+"matrix-data.sps:19.12: error: MATRIX DATA: Syntax error expecting VARIABLES.
+   19 | MATRIX DATA.
+      |            ^"
 
-matrix-data.sps:20.24: error: MATRIX DATA: Syntax error at `*': expecting `/'.
+"matrix-data.sps:20.24: error: MATRIX DATA: Syntax error expecting `/'.
+   20 | MATRIX DATA VARIABLES=v*.
+      |                        ^"
 
-matrix-data.sps:21.27-21.28: error: MATRIX DATA: Syntax error at `-1': Expected non-negative integer for N.
+"matrix-data.sps:21.27-21.28: error: MATRIX DATA: Syntax error expecting non-negative integer for N.
+   21 | MATRIX DATA VARIABLES=v/N=-1.
+      |                           ^~"
 
-matrix-data.sps:22.32-22.36: error: MATRIX DATA: Syntax error at `XYZZY'.
+"matrix-data.sps:22.32-22.36: error: MATRIX DATA: Syntax error.
+   22 | MATRIX DATA VARIABLES=v/FORMAT=XYZZY.
+      |                                ^~~~~"
 
-matrix-data.sps:23.30-23.32: error: MATRIX DATA: Syntax error at `123': expecting a file name or handle name.
+"matrix-data.sps:23.30-23.32: error: MATRIX DATA: Syntax error expecting a file name or handle name.
+   23 | MATRIX DATA VARIABLES=v/FILE=123.
+      |                              ^~~"
 
-matrix-data.sps:24.31-24.33: error: MATRIX DATA: Syntax error at `123': expecting variable name.
+"matrix-data.sps:24.31-24.33: error: MATRIX DATA: Syntax error expecting variable name.
+   24 | MATRIX DATA VARIABLES=v/SPLIT=123.
+      |                               ^~~"
 
-matrix-data.sps:25.31-25.32: error: MATRIX DATA: Syntax error at `-1': Expected non-negative integer for CELLS.
+"matrix-data.sps:25.31-25.32: error: MATRIX DATA: Syntax error expecting non-negative integer for CELLS.
+   25 | MATRIX DATA VARIABLES=v/CELLS=-1.
+      |                               ^~"
 
-matrix-data.sps:26.25-26.29: error: MATRIX DATA: Syntax error at `XYZZY'.
+"matrix-data.sps:26.25-26.29: error: MATRIX DATA: Syntax error.
+   26 | MATRIX DATA VARIABLES=v/XYZZY.
+      |                         ^~~~~"
 ])
 AT_CLEANUP
 
index fccd435f65409ec648d6d45c56b50a1b89f2a2d2..a5b96c3733d3c1f72289b2329d8936fe1c97c2e0 100644 (file)
@@ -158,7 +158,9 @@ LIST.
 
 AT_CHECK([pspp -O format=csv input.sps])
 AT_CHECK([pspp -O format=csv mconvert.sps], [1], [dnl
-mconvert.sps:2: error: LIST: LIST is allowed only after the active dataset has been defined.
+"mconvert.sps:2.1-2.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+    2 | LIST.
+      | ^~~~"
 ])
 AT_CHECK([pspp -O format=csv output.sps], [0], [dnl
 Table: Data List
index c9dd0908eab8ed42a5b2540e183fcc2fbac1e322..465240cc22837f07ca2044532a5787ad8f057f4c 100644 (file)
@@ -174,7 +174,9 @@ PRINT F8.2
 LIST.
 ])
 AT_CHECK([pspp -O format=csv print.sps], [1], [dnl
-print.sps:7.7-7.10: error: PRINT: Syntax error at `F8.2': expecting a valid subcommand.
+"print.sps:7.7-7.10: error: PRINT: Syntax error expecting OUTFILE, ENCODING, RECORDS, TABLE, or NOTABLE.
+    7 | PRINT F8.2
+      |       ^~~~"
 
 Table: Data List
 a,b
index b53f114b8528963800f3839a21aa346e4461a066..98019ae7e5871bca2ddf125211554508b926c429 100644 (file)
@@ -86,13 +86,17 @@ z,A3
 
 "formats.sps:2: error: FORMATS: Output format E6.1 specifies 1 decimal place, but the given width does not allow for any decimals."
 
-formats.sps:3: error: FORMATS: a and y are not the same type.  All variables in this variable list must be of the same type.  y will be omitted from the list.
+"formats.sps:3.11: error: FORMATS: a and y are not the same type.  All variables in this variable list must be of the same type.  y will be omitted from the list.
+    3 | FORMATS a y (F4).
+      |           ^"
 
 formats.sps:4: error: FORMATS: String variable with width 1 is not compatible with format A2.
 
 formats.sps:5: error: FORMATS: String variable with width 2 is not compatible with format AHEX2.
 
-formats.sps:6: error: FORMATS: x and y are string variables with different widths.  All variables in this variable list must have the same width.  y will be omitted from the list.
+"formats.sps:6.11: error: FORMATS: x and y are string variables with different widths.  All variables in this variable list must have the same width.  y will be omitted from the list.
+    6 | FORMATS x y (A2).
+      |           ^"
 
 formats.sps:6: error: FORMATS: String variable with width 1 is not compatible with format A2.
 ])
index fd748385917c770b00b35e85a50f319f3d7f33a0..15a779180f3f6c5866965aef154470a57f9b7d39 100644 (file)
@@ -173,11 +173,17 @@ MISSING VALUES num1 (2 THRU 1).
 AT_CHECK([pspp -O format=csv missing-values.sps], [1], [dnl
 missing-values.sps:5: error: MISSING VALUES: Missing values provided are too long to assign to variable of width 3.
 
-missing-values.sps:8: error: MISSING VALUES: Truncating missing value to maximum acceptable length (8 bytes).
+"missing-values.sps:8.25-8.37: error: MISSING VALUES: Truncating missing value to maximum acceptable length (8 bytes).
+    8 | MISSING VALUES longstr ('abcdefghijk').
+      |                         ^~~~~~~~~~~~~"
 
-missing-values.sps:11.26-11.29: error: MISSING VALUES: Syntax error at `THRU': expecting string.
+"missing-values.sps:11.26-11.29: error: MISSING VALUES: Syntax error expecting string.
+   11 | MISSING VALUES str1 ('a' THRU 'z').
+      |                          ^~~~"
 
-missing-values.sps:11: error: MISSING VALUES: THRU is not a variable name.
+"missing-values.sps:11.26-11.29: error: MISSING VALUES: THRU is not a variable name.
+   11 | MISSING VALUES str1 ('a' THRU 'z').
+      |                          ^~~~"
 
 missing-values.sps:14: error: MISSING VALUES: Cannot mix numeric variables (e.g. num1) and string variables (e.g. str1) within a single list.
 
@@ -189,6 +195,8 @@ missing-values.sps:19: error: MISSING VALUES: Too many numeric missing values.
 
 missing-values.sps:20: error: MISSING VALUES: Too many string missing values.  At most three individual values are allowed.
 
-missing-values.sps:23: warning: MISSING VALUES: The high end of the range (1) is below the low end (2).  The range will be treated as if reversed.
+"missing-values.sps:23.22-23.29: warning: MISSING VALUES: The high end of the range (1) is below the low end (2).  The range will be treated as if reversed.
+   23 | MISSING VALUES num1 (2 THRU 1).
+      |                      ^~~~~~~~"
 ])
 AT_CLEANUP
index 99b2d07305e004c2d035e91d4d1bfb340a2b51f1..bddbbd191905c572c979fcde48fff7585fda7359 100644 (file)
@@ -218,8 +218,10 @@ AT_DATA([mrsets.sps],
   [DEFINE_MRSETS_DATA
 MRSETS /MDGROUP VALUE=1.5.
 ])
-AT_CHECK([pspp -O format=csv mrsets.sps], [1],
-  [mrsets.sps:6: error: MRSETS: Numeric VALUE must be an integer.
+AT_CHECK([pspp -O format=csv mrsets.sps], [1], [dnl
+"mrsets.sps:6.23-6.25: error: MRSETS: Numeric VALUE must be an integer.
+    6 | MRSETS /MDGROUP VALUE=1.5.
+      |                       ^~~"
 ])
 AT_CLEANUP
 
@@ -228,8 +230,10 @@ AT_DATA([mrsets.sps],
   [DEFINE_MRSETS_DATA
 MRSETS /MCGROUP VARIABLES=a b c.
 ])
-AT_CHECK([pspp -O format=csv mrsets.sps], [1],
-  [mrsets.sps:6.32: error: MRSETS: Syntax error at end of command: Required MCGROUP specification missing from NAME subcommand.
+AT_CHECK([pspp -O format=csv mrsets.sps], [1], [dnl
+"mrsets.sps:6.32: error: MRSETS: Required NAME specification missing from MCGROUP subcommand.
+    6 | MRSETS /MCGROUP VARIABLES=a b c.
+      |                                ^"
 ])
 AT_CLEANUP
 
@@ -238,8 +242,10 @@ AT_DATA([mrsets.sps],
   [DEFINE_MRSETS_DATA
 MRSETS /MCGROUP NAME=$Mcgroup.
 ])
-AT_CHECK([pspp -O format=csv mrsets.sps], [1],
-  [mrsets.sps:6.30: error: MRSETS: Syntax error at end of command: Required MCGROUP specification missing from VARIABLES subcommand.
+AT_CHECK([pspp -O format=csv mrsets.sps], [1], [dnl
+"mrsets.sps:6.30: error: MRSETS: Required VARIABLES specification missing from MCGROUP subcommand.
+    6 | MRSETS /MCGROUP NAME=$Mcgroup.
+      |                              ^"
 ])
 AT_CLEANUP
 
@@ -248,10 +254,14 @@ AT_DATA([mrsets.sps],
   [DEFINE_MRSETS_DATA
 MRSETS /MCGROUP NAME=$mygroup VARIABLES=a b x y.
 ])
-AT_CHECK([pspp -O format=csv mrsets.sps], [1],
-  [mrsets.sps:6: error: MRSETS: a and x are not the same type.  All variables in this variable list must be of the same type.  x will be omitted from the list.
+AT_CHECK([pspp -O format=csv mrsets.sps], [1], [dnl
+"mrsets.sps:6.45: error: MRSETS: a and x are not the same type.  All variables in this variable list must be of the same type.  x will be omitted from the list.
+    6 | MRSETS /MCGROUP NAME=$mygroup VARIABLES=a b x y.
+      |                                             ^"
 
-mrsets.sps:6: error: MRSETS: a and y are not the same type.  All variables in this variable list must be of the same type.  y will be omitted from the list.
+"mrsets.sps:6.47: error: MRSETS: a and y are not the same type.  All variables in this variable list must be of the same type.  y will be omitted from the list.
+    6 | MRSETS /MCGROUP NAME=$mygroup VARIABLES=a b x y.
+      |                                               ^"
 ])
 AT_CLEANUP
 
@@ -295,9 +305,13 @@ AT_DATA([mrsets.sps],
 [MRSETS /DISPLAY NAME=[$x].
 MRSETS /DELETE NAME=[$y].
 ]])
-AT_CHECK([pspp -O format=csv mrsets.sps], [1],
-  [mrsets.sps:6: error: MRSETS: No multiple response set named $x.
+AT_CHECK([pspp -O format=csv mrsets.sps], [1], [dnl
+"mrsets.sps:6.23-6.24: error: MRSETS: No multiple response set named $x.
+    6 | MRSETS /DISPLAY NAME=[[$x]].
+      |                       ^~"
 
-mrsets.sps:7: error: MRSETS: No multiple response set named $y.
+"mrsets.sps:7.22-7.23: error: MRSETS: No multiple response set named $y.
+    7 | MRSETS /DELETE NAME=[[$y]].
+      |                      ^~"
 ])
 AT_CLEANUP
index 021698243c183697d35c73a2fe61c3fcca2dd071..2bac98769389c7d59d7c0df856bdfac61ba58a62 100644 (file)
@@ -98,7 +98,9 @@ RENAME VARIABLES warp auxiliary=foobar xyzzy.
 ])
 
 AT_CHECK([pspp -o pspp.csv rename-variables.sps], [1], [dnl
-rename-variables.sps:2.23-2.31: error: RENAME VARIABLES: Syntax error at `auxiliary': expecting `='.
+rename-variables.sps:2.23-2.31: error: RENAME VARIABLES: Syntax error expecting `='.
+    2 | RENAME VARIABLES warp auxiliary=foobar xyzzy.
+      |                       ^~~~~~~~~
 ])
 AT_CLEANUP
 
index c09b8ef080ba11e7acbbd9eb8c24586794be419d..df15982860936c4d0c4455a6d34e0bec6b3b8d47 100644 (file)
@@ -128,7 +128,9 @@ END DATA.
 VALUE LABELS /var=a 'label for a'.
 ])
 AT_CHECK([pspp -O format=csv value-labels.sps], [1], [dnl
-value-labels.sps:9: error: VALUE LABELS: var is not a variable name.
+"value-labels.sps:9.15-9.17: error: VALUE LABELS: var is not a variable name.
+    9 | VALUE LABELS /var=a 'label for a'.
+      |               ^~~"
 ])
 AT_CLEANUP
 
index 14059b83377a15a2311800aaf6868b1e101bea0f..4cce31d174e8d5024d7626e99631feeecfa21ce7 100644 (file)
@@ -725,8 +725,10 @@ c').  To disable this warning, insert parentheses.
 
 1 >= 2 = 2 ge 3 => false
 
-evaluate.sps:17.24: error: DEBUG EVALUATE: Syntax error at `!': expecting end
-of command.
+evaluate.sps:17.24: error: DEBUG EVALUATE: Syntax error expecting end of
+command.
+   17 | DEBUG EVALUATE /3 ne 2 != 1.
+      |                        ^
 
 3 ne 2 != 1 => error
 
@@ -749,7 +751,9 @@ c').  To disable this warning, insert parentheses.
 
 2 le 2 => true
 
-evaluate.sps:25.21: error: DEBUG EVALUATE: Syntax error at `='.
+evaluate.sps:25.21: error: DEBUG EVALUATE: Syntax error.
+   25 | DEBUG EVALUATE /2 < = 2.
+      |                     ^
 
 2 < = 2 => error
 
@@ -950,7 +954,9 @@ for opt in OPT NOOPT; do
 
 2 ge 2 => true
 
-evaluate.sps:8.21: error: DEBUG EVALUATE: Syntax error at `='.
+evaluate.sps:8.21: error: DEBUG EVALUATE: Syntax error.
+    8 | DEBUG EVALUATE /2 > = 2.
+      |                     ^
 
 2 > = 2 => error
 
@@ -1118,12 +1124,16 @@ evaluate.sps:36.27-36.30: note: DEBUG EVALUATE: This operand has type 'number'.
 
 'asdf       ' ~= "asdf   " => false
 
-evaluate.sps:41.21: error: DEBUG EVALUATE: Syntax error at `>'.
+evaluate.sps:41.21: error: DEBUG EVALUATE: Syntax error.
+   41 | DEBUG EVALUATE /1 < > 1.
+      |                     ^
 
 1 < > 1 => error
 
-evaluate.sps:42.19: error: DEBUG EVALUATE: Syntax error at `~': expecting end
-of command.
+evaluate.sps:42.19: error: DEBUG EVALUATE: Syntax error expecting end of
+command.
+   42 | DEBUG EVALUATE /1 ~ = 1.
+      |                   ^
 
 1 ~ = 1 => error
 ])
@@ -2020,15 +2030,21 @@ ANY(string, string[, string]...).
 
 any(a10, 'b', 'c', 'd') => error
 
-evaluate.sps:37: error: DEBUG EVALUATE: Unknown identifier b.
+evaluate.sps:37.26: error: DEBUG EVALUATE: Unknown identifier b.
+   37 | DEBUG EVALUATE /any('a', b, 'c', 'd').
+      |                          ^
 
 any('a', b, 'c', 'd') => error
 
-evaluate.sps:38: error: DEBUG EVALUATE: Unknown identifier c.
+evaluate.sps:38.31: error: DEBUG EVALUATE: Unknown identifier c.
+   38 | DEBUG EVALUATE /any('a', 'b', c, 'd').
+      |                               ^
 
 any('a', 'b', c, 'd') => error
 
-evaluate.sps:39: error: DEBUG EVALUATE: Unknown identifier d.
+evaluate.sps:39.36: error: DEBUG EVALUATE: Unknown identifier d.
+   39 | DEBUG EVALUATE /any('a', 'b', 'c', d).
+      |                                    ^
 
 any('a', 'b', 'c', d) => error
 ]])
index 0ea9486eed3745a5f5e7ccbea8e1b88d9b639c7f..96e42c13ea179aac7cd4b9d4005494013a8544e6 100644 (file)
@@ -33,7 +33,9 @@ COMPUTE x=y.
 END IF.
 ])
 AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-parse.sps:10: error: IF: Unknown identifier y.
+"parse.sps:10.6: error: IF: Unknown identifier y.
+   10 | IF ( y > 0 ) .
+      |      ^"
 
 parse.sps:11: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
 ])
@@ -102,7 +104,9 @@ DATA LIST NOTABLE/x 1.
 COMPUTE x=$nonexistent.
 ])
 AT_CHECK([pspp parse.sps], [1], [dnl
-parse.sps:2: error: COMPUTE: Unknown system variable $nonexistent.
+parse.sps:2.11-2.22: error: COMPUTE: Unknown system variable $nonexistent.
+    2 | COMPUTE x=$nonexistent.
+      |           ^~~~~~~~~~~~
 ])
 AT_CLEANUP
 
@@ -113,7 +117,9 @@ DATA LIST NOTABLE/x 1.
 COMPUTE x=y.
 ])
 AT_CHECK([pspp parse.sps], [1], [dnl
-parse.sps:2: error: COMPUTE: Unknown identifier y.
+parse.sps:2.11: error: COMPUTE: Unknown identifier y.
+    2 | COMPUTE x=y.
+      |           ^
 ])
 AT_CLEANUP
 
@@ -472,19 +478,22 @@ DEBUG EVALUATE (a=1)(b=2) VECTOR/v('abc').
 for opt in OPT NOOPT; do
     AS_BOX([$opt])
     sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
-    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
-[[evaluate.sps:3: error: DEBUG EVALUATE: Unknown system variable $nonexistent.
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+evaluate.sps:3.17-3.28: error: DEBUG EVALUATE: Unknown system variable
+$nonexistent.
+    3 | DEBUG EVALUATE /$nonexistent.
+      |                 ^~~~~~~~~~~~
 
 $nonexistent => error
 
-evaluate.sps:4.17-4.27: error: DEBUG EVALUATE: RANGE(number, number, number[,
-number, number]...) must have an odd number of arguments.
+evaluate.sps:4.17-4.27: error: DEBUG EVALUATE: RANGE(number, number, number[[,
+number, number]]...) must have an odd number of arguments.
     4 | DEBUG EVALUATE /RANGE(1, 2).
       |                 ^~~~~~~~~~~
 
 RANGE(1, 2) => error
 
-evaluate.sps:5.17-5.34: error: DEBUG EVALUATE: CONCAT(string[, string]...)
+evaluate.sps:5.17-5.34: error: DEBUG EVALUATE: CONCAT(string[[, string]]...)
 function cannot accept suffix .1 to specify the minimum number of valid
 arguments.
     5 | DEBUG EVALUATE /CONCAT.1('a', 'b').
@@ -492,12 +501,16 @@ arguments.
 
 CONCAT.1('a', 'b') => error
 
-evaluate.sps:6: error: DEBUG EVALUATE: No function or vector named foobar.
+evaluate.sps:6.17-6.22: error: DEBUG EVALUATE: No function or vector named
+foobar.
+    6 | DEBUG EVALUATE /foobar(x).
+      |                 ^~~~~~
 
 foobar(x) => error
 
-evaluate.sps:7.30: error: DEBUG EVALUATE: Syntax error at `b': expecting `,' or
-`)'.
+evaluate.sps:7.30: error: DEBUG EVALUATE: Syntax error expecting `,' or `@:}@'.
+    7 | DEBUG EVALUATE /CONCAT.1('a' b).
+      |                              ^
 
 CONCAT.1('a' b) => error
 
@@ -518,6 +531,6 @@ evaluate.sps:9.36-9.40: note: DEBUG EVALUATE: This vector index has type
       |                                    ^~~~~
 
 v('abc') => error
-]])
+])
 done
 AT_CLEANUP
index 725c26065706a0ac419a4458de3d0131ba8dea69..5b6b660ecdc9c91cb3be65108c57f5ef0ac05d72 100644 (file)
@@ -50,29 +50,53 @@ u'110000'
 ï¿½
 ])
 AT_CHECK([pspp -O format=csv lexer.sps], [1], [dnl
-"lexer.sps:1.1-1.6: error: Syntax error at `x'123'': String of hex digits has 3 characters, which is not a multiple of 2."
+"lexer.sps:1.1-1.6: error: String of hex digits has 3 characters, which is not a multiple of 2.
+    1 | x'123'
+      | ^~~~~~"
 
-lexer.sps:2.1-2.5: error: Syntax error at `x'1x'': `x' is not a valid hex digit.
+"lexer.sps:2.1-2.5: error: `x' is not a valid hex digit.
+    2 | x'1x'
+      | ^~~~~"
 
-"lexer.sps:3.1-3.3: error: Syntax error at `u''': Unicode string contains 0 bytes, which is not in the valid range of 1 to 8 bytes."
+"lexer.sps:3.1-3.3: error: Unicode string contains 0 bytes, which is not in the valid range of 1 to 8 bytes.
+    3 | u''
+      | ^~~"
 
-"lexer.sps:4.1-4.12: error: Syntax error at `u'012345678'': Unicode string contains 9 bytes, which is not in the valid range of 1 to 8 bytes."
+"lexer.sps:4.1-4.12: error: Unicode string contains 9 bytes, which is not in the valid range of 1 to 8 bytes.
+    4 | u'012345678'
+      | ^~~~~~~~~~~~"
 
-lexer.sps:5.1-5.7: error: Syntax error at `u'd800'': U+D800 is not a valid Unicode code point.
+"lexer.sps:5.1-5.7: error: U+D800 is not a valid Unicode code point.
+    5 | u'd800'
+      | ^~~~~~~"
 
-lexer.sps:6.1-6.9: error: Syntax error at `u'110000'': U+110000 is not a valid Unicode code point.
+"lexer.sps:6.1-6.9: error: U+110000 is not a valid Unicode code point.
+    6 | u'110000'
+      | ^~~~~~~~~"
 
-lexer.sps:7.1-7.4: error: Syntax error at `'foo': Unterminated string constant.
+"lexer.sps:7.1-7.4: error: Unterminated string constant.
+    7 | 'foo
+      | ^~~~"
 
-lexer.sps:8.1-8.70: error: Syntax error at `'very long unterminated string that be ellipsized in its err...': Unterminated string constant.
+"lexer.sps:8.1-8.70: error: Unterminated string constant.
+    8 | 'very long unterminated string that be ellipsized in its error message
+      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
 
-lexer.sps:9.1-9.2: error: Syntax error at `1e': Missing exponent following `1e'.
+"lexer.sps:9.1-9.2: error: Missing exponent following `1e'.
+    9 | 1e .x
+      | ^~"
 
-lexer.sps:9.4: error: Syntax error at `.': expecting command name.
+"lexer.sps:9.4: error: Syntax error expecting command name.
+    9 | 1e .x
+      |    ^"
 
-lexer.sps:10.1: error: Syntax error at `^': Bad character `^' in input.
+"lexer.sps:10.1: error: Bad character `^' in input.
+   10 | ^
+      | ^"
 
-lexer.sps:11.1-11.2: error: Syntax error at `�': Bad character U+FFFD in input.
+"lexer.sps:11.1-11.2: error: Bad character U+FFFD in input.
+   11 | ï¿½
+      | ^~"
 ])
 AT_CLEANUP
 
@@ -83,11 +107,15 @@ printf "datA dist list notable file='input.txt'/a b c.
 lis|.\0" > lexer.sps
 
 AT_CHECK([pspp -O format=csv lexer.sps], [1], [dnl
-lexer.sps:1: error: Unknown command `datA dist'.
+"lexer.sps:1.1-1.9: error: Unknown command `datA dist'.
+    1 | datA dist list notable file='input.txt'/a b c.
+      | ^~~~~~~~~"
 
-lexer.sps:2: error: LIST: LIST is allowed only after the active dataset has been defined.
+"lexer.sps:2.1-2.3: error: LIST: LIST is allowed only after the active dataset has been defined.
+    2 | lis|."
 
-lexer.sps:2.6: error: LIST: Syntax error at `...': Bad character U+0000 in input.
+"lexer.sps:2.6: error: LIST: Bad character U+0000 in input.
+    2 | lis|."
 ])
 AT_CLEANUP
 
@@ -99,7 +127,9 @@ AT_SETUP([lexer crash due to overflow])
 printf "DATA LIST/5555555555555555." > lexer.sps
 
 AT_CHECK([pspp -O format=csv lexer.sps], [1], [dnl
-lexer.sps:1.11-1.26: error: DATA LIST: Syntax error at `5555555555555555': Expected integer between 1 and 2147483647.
+"lexer.sps:1.11-1.26: error: DATA LIST: Syntax error expecting integer between 1 and 2147483647.
+    1 | DATA LIST/5555555555555555.
+      |           ^~~~~~~~~~~~~~~~"
 ])
 
 AT_CLEANUP
index e574fc51f3e835f5441004d1599e495cd30df7c9..2b208d319829f642ebfd03bef445916028668c93 100644 (file)
@@ -57,98 +57,105 @@ m4_if([$1], [active], [OUTFILE=*],
       [outfile=aggregate]) dnl
 m4_if([$2], [presorted], [/PRESORTED]) dnl
 m4_if([$3], [columnwise], [/MISSING=COLUMNWISE])
-       /DOCUMENT
-       /BREAK=g
-       /N = n
-       /NI = n./
-       NU = nu
-       /NUI = nu./
-       NFGT2 = fgt(n, 2)
-       /NFGT2I = fgt.(n, 2)
-       /SFGT2 = fgt(s, '2')
-       /SFGT2I = fgt.(s, '2')
-       /NFIN23 = fin(n, 2, 3)
-       /NFIN23I = fin.(n, 2, 3)
-       /SFIN23 = fin(s, '2', '3')
-       /SFIN23I = fin.(s, '2', '3')
-       /NFLT2 = flt(n, 2)
-       /NFLT2I = flt.(n, 2)
-       /SFLT2 = flt(s, '2')
-       /SFLT2I = flt.(s, '2')
-       /NFIRST = first(n)
-       /NFIRSTI = first.(n)
-       /SFIRST = first(s)
-       /SFIRSTI = first.(s)
-       /NFOUT23 = fout(n, 3, 2)
-       /NFOUT23I = fout.(n, 3, 2)
-       /SFOUT23 = fout(s, '3', '2')
-       /SFOUT23I = fout.(s, '3', '2')
-       /NLAST = last(n)
-       /NLASTI = last.(n)
-       /SLAST = last(s)
-       /SLASTI = last.(s)
-       /NMAX = max(n)
-       /NMAXI = max.(n)
-       /SMAX = max(s)
-       /SMAXI = max.(s)
-       /NMEAN = mean(n)
-       /NMEANI = mean.(n)
-       /NMIN = min(n)
-       /NMINI = min.(n)
-       /SMIN = min(s)
-       /SMINI = min.(s)
-       /NN = n(n)
-       /NNI = n.(n)
-       /SN = n(s)
-       /SNI = n.(s)
-       /NNMISS = nmiss(n)
-       /NNMISSI = nmiss.(n)
-       /SNMISS = nmiss(s)
-       /SNMISSI = nmiss.(s)
-       /NNU = nu(n)
-       /NNUI = nu.(n)
-       /SNU = nu(s)
-       /SNUI = nu.(s)
-       /NNUMISS = numiss(n)
-       /NNUMISSI = numiss.(n)
-       /SNUMISS = numiss(s)
-       /SNUMISSI = numiss.(s)
-       /NPGT2 = pgt(n, 2)
-       /NPGT2I = pgt.(n, 2)
-       /SPGT2 = pgt(s, '2')
-       /SPGT2I = pgt.(s, '2')
-       /NPIN23 = pin(n, 2, 3)
-       /NPIN23I = pin.(n, 2, 3)
-       /SPIN23 = pin(s, '2', '3')
-       /SPIN23I = pin.(s, '2', '3')
-       /NPLT2 = plt(n, 2)
-       /NPLT2I = plt.(n, 2)
-       /SPLT2 = plt(s, '2')
-       /SPLT2I = plt.(s, '2')
-       /NPOUT23 = pout(n, 2, 3)
-       /NPOUT23I = pout.(n, 2, 3)
-       /SPOUT23 = pout(s, '2', '3')
-       /SPOUT23I = pout.(s, '2', '3')
-       /NMEDIAN = median(n)
-       /NMEDIANI = median.(n)
-       /NSD = sd(n)
-       /NSDI = sd.(n)
-       /NSUM = sum(n)
-       /NSUMI = sum.(n).
+        /DOCUMENT
+        /BREAK=g
+        /N = n
+        /NI = n./
+        NU = nu
+        /NUI = nu./
+        NFGT2 = fgt(n, 2)
+        /NFGT2I = fgt.(n, 2)
+        /SFGT2 = fgt(s, '2')
+        /SFGT2I = fgt.(s, '2')
+        /NFIN23 = fin(n, 2, 3)
+        /NFIN23I = fin.(n, 2, 3)
+        /SFIN23 = fin(s, '2', '3')
+        /SFIN23I = fin.(s, '2', '3')
+        /NFLT2 = flt(n, 2)
+        /NFLT2I = flt.(n, 2)
+        /SFLT2 = flt(s, '2')
+        /SFLT2I = flt.(s, '2')
+        /NFIRST = first(n)
+        /NFIRSTI = first.(n)
+        /SFIRST = first(s)
+        /SFIRSTI = first.(s)
+        /NFOUT23 = fout(n, 3, 2)
+        /NFOUT23I = fout.(n, 3, 2)
+        /SFOUT23 = fout(s, '3', '2')
+        /SFOUT23I = fout.(s, '3', '2')
+        /NLAST = last(n)
+        /NLASTI = last.(n)
+        /SLAST = last(s)
+        /SLASTI = last.(s)
+        /NMAX = max(n)
+        /NMAXI = max.(n)
+        /SMAX = max(s)
+        /SMAXI = max.(s)
+        /NMEAN = mean(n)
+        /NMEANI = mean.(n)
+        /NMIN = min(n)
+        /NMINI = min.(n)
+        /SMIN = min(s)
+        /SMINI = min.(s)
+        /NN = n(n)
+        /NNI = n.(n)
+        /SN = n(s)
+        /SNI = n.(s)
+        /NNMISS = nmiss(n)
+        /NNMISSI = nmiss.(n)
+        /SNMISS = nmiss(s)
+        /SNMISSI = nmiss.(s)
+        /NNU = nu(n)
+        /NNUI = nu.(n)
+        /SNU = nu(s)
+        /SNUI = nu.(s)
+        /NNUMISS = numiss(n)
+        /NNUMISSI = numiss.(n)
+        /SNUMISS = numiss(s)
+        /SNUMISSI = numiss.(s)
+        /NPGT2 = pgt(n, 2)
+        /NPGT2I = pgt.(n, 2)
+        /SPGT2 = pgt(s, '2')
+        /SPGT2I = pgt.(s, '2')
+        /NPIN23 = pin(n, 2, 3)
+        /NPIN23I = pin.(n, 2, 3)
+        /SPIN23 = pin(s, '2', '3')
+        /SPIN23I = pin.(s, '2', '3')
+        /NPLT2 = plt(n, 2)
+        /NPLT2I = plt.(n, 2)
+        /SPLT2 = plt(s, '2')
+        /SPLT2I = plt.(s, '2')
+        /NPOUT23 = pout(n, 2, 3)
+        /NPOUT23I = pout.(n, 2, 3)
+        /SPOUT23 = pout(s, '2', '3')
+        /SPOUT23I = pout.(s, '2', '3')
+        /NMEDIAN = median(n)
+        /NMEDIANI = median.(n)
+        /NSD = sd(n)
+        /NSDI = sd.(n)
+        /NSUM = sum(n)
+        /NSUMI = sum.(n).
 m4_if([$1], [external], [GET FILE='aggregate.sys'.],
       [$1], [dataset], [DATASET ACTIVATE aggregate.])
 LIST.
 ])
-  AT_CHECK([pspp -O format=csv aggregate.sps], [0], [stdout])
-  AT_CHECK([[sed 's/^[^:]*:[0-9]*: //' < stdout]], [0],
-    [m4_if([$3], [itemwise],
-      [warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+  AT_CHECK([pspp -O format=csv aggregate.sps], [0], 
+    [m4_if([$3], [itemwise], [dnl
+"aggregate.sps:29.28-29.31: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   29 |         /NFOUT23 = fout(n, 3, 2)
+      |                            ^~~~"
+"aggregate.sps:30.30-30.33: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   30 |         /NFOUT23I = fout.(n, 3, 2)
+      |                              ^~~~"
+"aggregate.sps:31.28-31.35: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   31 |         /SFOUT23 = fout(s, '3', '2')
+      |                            ^~~~~~~~"
+"aggregate.sps:32.30-32.37: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   32 |         /SFOUT23I = fout.(s, '3', '2')
+      |                              ^~~~~~~~"
 
 Table: Data List
 G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT2I,SFLT2,SFLT2I,NFIRST,NFIRSTI,SFIRST,SFIRSTI,NFOUT23,NFOUT23I,SFOUT23,SFOUT23I,NLAST,NLASTI,SLAST,SLASTI,NMAX,NMAXI,SMAX,SMAXI,NMEAN,NMEANI,NMIN,NMINI,SMIN,SMINI,NN,NNI,SN,SNI,NNMISS,NNMISSI,SNMISS,SNMISSI,NNU,NNUI,SNU,SNUI,NNUMISS,NNUMISSI,SNUMISS,SNUMISSI,NPGT2,NPGT2I,SPGT2,SPGT2I,NPIN23,NPIN23I,SPIN23,SPIN23I,NPLT2,NPLT2I,SPLT2,SPLT2I,NPOUT23,NPOUT23I,SPOUT23,SPOUT23I,NMEDIAN,NMEDIANI,NSD,NSDI,NSUM,NSUMI
@@ -157,13 +164,22 @@ G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT
 3,2.00,2.00,1,1,.000,.000,.000,.000,.000,.000,.000,.000,1.000,1.000,1.000,1.000,1,1,1,1,1.000,1.000,1.000,1.000,1,1,1,1,1,1,1,1,1.00,1.00,1,1,1,1,2.00,2.00,2.00,2.00,.00,.00,.00,.00,1,1,1,1,0,0,0,0,.0,.0,.0,.0,.0,.0,.0,.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,1.00,1.00,.00,.00,2.00,2.00
 4,1.00,1.00,1,1,.   ,.   ,.   ,1.000,.   ,.   ,.   ,.000,.   ,.   ,.   ,.000,.,.,,4,.   ,.   ,.   ,1.000,.,.,,4,.,.,,4,.  ,.  ,.,.,,4,.00,.00,.00,1.00,1.00,1.00,1.00,.00,0,0,0,1,1,1,1,0,. ,. ,. ,100.0,. ,. ,. ,.0,. ,. ,. ,.0,. ,. ,. ,100.0,NaN,NaN,.  ,.  ,.  ,.  @&t@
 ],
-      [warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-
-warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+      [dnl
+"aggregate.sps:29.28-29.31: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   29 |         /NFOUT23 = fout(n, 3, 2)
+      |                            ^~~~"
+"aggregate.sps:30.30-30.33: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   30 |         /NFOUT23I = fout.(n, 3, 2)
+      |                              ^~~~"
+"aggregate.sps:31.28-31.35: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   31 |         /SFOUT23 = fout(s, '3', '2')
+      |                            ^~~~~~~~"
+"aggregate.sps:32.30-32.37: warning: AGGREGATE: The value arguments passed to the FOUT function are out of order.  They will be treated as if they had been specified in the correct order.
+   32 |         /SFOUT23I = fout.(s, '3', '2')
+      |                              ^~~~~~~~"
 
 Table: Data List
 G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT2I,SFLT2,SFLT2I,NFIRST,NFIRSTI,SFIRST,SFIRSTI,NFOUT23,NFOUT23I,SFOUT23,SFOUT23I,NLAST,NLASTI,SLAST,SLASTI,NMAX,NMAXI,SMAX,SMAXI,NMEAN,NMEANI,NMIN,NMINI,SMIN,SMINI,NN,NNI,SN,SNI,NNMISS,NNMISSI,SNMISS,SNMISSI,NNU,NNUI,SNU,SNUI,NNUMISS,NNUMISSI,SNUMISS,SNUMISSI,NPGT2,NPGT2I,SPGT2,SPGT2I,NPIN23,NPIN23I,SPIN23,SPIN23I,NPLT2,NPLT2I,SPLT2,SPLT2I,NPOUT23,NPOUT23I,SPOUT23,SPOUT23I,NMEDIAN,NMEDIANI,NSD,NSDI,NSUM,NSUMI
index 9ea42e7572878ca25bc74acaebd3a7524040032c..5bc111787c9428b56d9b1ed60a9b5cf345159911 100644 (file)
@@ -214,7 +214,7 @@ CTABLES /TABLE qn1 /COMPARETEST MERGE=**.
 CTABLES /TABLE qn1 /COMPARETEST STYLE=**.
 CTABLES /TABLE qn1 /COMPARETEST SHOWSIG=**.
 CTABLES /TABLE qn1 /COMPARETEST **.
-CTABLES /TABLE qn1 / **.
+CTABLES /TABLE qn1 /FORMAT.
 CTABLES /TABLE qn1 /CLABELS ROWLABELS=OPPOSITE /CLABELS COLLABELS=OPPOSITE.
 CTABLES /TABLE qn20 > qnd1.
 CTABLES /TABLE qn1 [ROWPCT] > qnsa1.
@@ -222,40 +222,58 @@ NUMERIC datetime (DATETIME17.0).
 CTABLES /TABLE qn1 /CATEGORIES VARIABLES=datetime ['123'].
 ]])
 AT_CHECK([pspp ctables.sps -O box=unicode -O width=80], [1],
-[[ctables.sps:2.8: error: CTABLES: Syntax error at end of command: expecting `/'.
+[[ctables.sps:2.8: error: CTABLES: Syntax error expecting `/'.
+    2 | CTABLES.
+      |        ^
 
-ctables.sps:3.29-3.33: error: CTABLES: Syntax error at `'foo'': Expected non-
-negative number for MINCOLWIDTH.
+ctables.sps:3.29-3.33: error: CTABLES: Syntax error expecting non-negative
+number for MINCOLWIDTH.
+    3 | CTABLES /FORMAT MINCOLWIDTH='foo'.
+      |                             ^~~~~
 
-ctables.sps:4.21-4.22: error: CTABLES: Syntax error at `**': expecting
-identifier.
+ctables.sps:4.21-4.22: error: CTABLES: Syntax error expecting identifier.
+    4 | CTABLES /TABLE qn1 [**].
+      |                     ^~
 
-ctables.sps:5.21-5.32: error: CTABLES: Syntax error at `NOTAFUNCTION': Expecting
-summary function name.
+ctables.sps:5.21-5.32: error: CTABLES: Syntax error expecting summary function
+name.
+    5 | CTABLES /TABLE qn1 [NOTAFUNCTION].
+      |                     ^~~~~~~~~~~~
 
-ctables.sps:6.20: error: CTABLES: Syntax error at end of command: expecting `@:}@'.
+ctables.sps:6.20: error: CTABLES: Syntax error expecting `@:}@'.
+    6 | CTABLES /TABLE @{:@qn1.
+      |                    ^
 
-ctables.sps:7.16-7.17: error: CTABLES: Syntax error at `**': expecting
-identifier.
+ctables.sps:7.16-7.17: error: CTABLES: Syntax error expecting identifier.
+    7 | CTABLES /TABLE **.
+      |                ^~
 
-ctables.sps:8: error: CTABLES: NOTAVAR is not a variable name.
+ctables.sps:8.16-8.22: error: CTABLES: NOTAVAR is not a variable name.
+    8 | CTABLES /TABLE NOTAVAR.
+      |                ^~~~~~~
 
 ctables.sps:10.16-10.24: error: CTABLES: Cannot use string variable string as a
 scale variable.
    10 | CTABLES /TABLE string[S].
       |                ^~~~~~~~~
 
-ctables.sps:11.27-11.29: error: CTABLES: Syntax error at `101': Expected number
-between 0 and 100 for PTILE.
+ctables.sps:11.27-11.29: error: CTABLES: Syntax error expecting number between 0
+and 100 for PTILE.
+   11 | CTABLES /TABLE qn1 [PTILE 101].
+      |                           ^~~
 
 ctables.sps:12: error: CTABLES: Output format F0.1 specifies width 0, but F
 requires a width between 1 and 40.
 
-ctables.sps:13.26-13.36: error: CTABLES: Syntax error at `NEGPAREN1.2': Output
-format NEGPAREN requires width 2 or greater.
+ctables.sps:13.26-13.36: error: CTABLES: Output format NEGPAREN requires width 2
+or greater.
+   13 | CTABLES /TABLE qn1 [MEAN NEGPAREN1.2].
+      |                          ^~~~~~~~~~~
 
-ctables.sps:14.26-14.36: error: CTABLES: Syntax error at `NEGPAREN3.4': Output
-format NEGPAREN requires width greater than decimals.
+ctables.sps:14.26-14.36: error: CTABLES: Output format NEGPAREN requires width
+greater than decimals.
+   14 | CTABLES /TABLE qn1 [MEAN NEGPAREN3.4].
+      |                          ^~~~~~~~~~~
 
 ctables.sps:15.21-15.24: error: CTABLES: Summary function MEAN applies only to
 scale variables.
@@ -266,7 +284,9 @@ ctables.sps:15.16-15.18: note: CTABLES: 'QN1' is not a scale variable.
    15 | CTABLES /TABLE qn1 [MEAN TOTALS].
       |                ^~~
 
-ctables.sps:15.32: error: CTABLES: Syntax error at `@:>@': expecting `@<:@'.
+ctables.sps:15.32: error: CTABLES: Syntax error expecting `@<:@'.
+   15 | CTABLES /TABLE qn1 [MEAN TOTALS].
+      |                                ^
 
 ctables.sps:16.21-16.24: error: CTABLES: Summary function MEAN applies only to
 scale variables.
@@ -277,80 +297,135 @@ ctables.sps:16.16-16.18: note: CTABLES: 'QN1' is not a scale variable.
    16 | CTABLES /TABLE qn1 [MEAN TOTALS[STDDEV]%].
       |                ^~~
 
-ctables.sps:16.40: error: CTABLES: Syntax error at `%': expecting `@:>@'.
+ctables.sps:16.40: error: CTABLES: Syntax error expecting `@:>@'.
+   16 | CTABLES /TABLE qn1 [MEAN TOTALS[STDDEV]%].
+      |                                        ^
 
-ctables.sps:17.56: error: CTABLES: Syntax error at `x': expecting string.
+ctables.sps:17.56: error: CTABLES: Syntax error expecting string.
+   17 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [SUBTOTAL=x].
+      |                                                        ^
 
-ctables.sps:18.50-18.51: error: CTABLES: Syntax error at `**': expecting THRU.
+ctables.sps:18.50-18.51: error: CTABLES: Syntax error expecting THRU.
+   18 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [LO **].
+      |                                                  ^~
 
-ctables.sps:19.55: error: CTABLES: Syntax error at `x': expecting number.
+ctables.sps:19.55: error: CTABLES: Syntax error expecting number.
+   19 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [LO THRU x].
+      |                                                       ^
 
-ctables.sps:20.54-20.55: error: CTABLES: Syntax error at `**': expecting number.
+ctables.sps:20.54-20.55: error: CTABLES: Syntax error expecting number.
+   20 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [1 THRU **].
+      |                                                      ^~
 
-ctables.sps:21.56-21.57: error: CTABLES: Syntax error at `**': expecting string.
+ctables.sps:21.56-21.57: error: CTABLES: Syntax error expecting string.
+   21 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 ['x' THRU **].
+      |                                                        ^~
 
-ctables.sps:22.48-22.49: error: CTABLES: Syntax error at `**': expecting
-identifier.
+ctables.sps:22.48-22.49: error: CTABLES: Syntax error expecting identifier.
+   22 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&**].
+      |                                                ^~
 
 ctables.sps:23.47-23.48: error: CTABLES: Unknown postcompute &x.
    23 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&x].
       |                                               ^~
 
-ctables.sps:24.61-24.63: error: CTABLES: Syntax error at `101': Expected number
-between 0 and 100 for PTILE.
+ctables.sps:24.61-24.63: error: CTABLES: Syntax error expecting number between 0
+and 100 for PTILE.
+   24 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 KEY=PTILE(qn1, 101).
+      |                                                             ^~~
 
-ctables.sps:25.58: error: CTABLES: Syntax error at end of command: expecting
-`@:}@'.
+ctables.sps:25.58: error: CTABLES: Syntax error expecting `@:}@'.
+   25 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 KEY=MEAN(qn1.
+      |                                                          ^
 
-ctables.sps:26.54: error: CTABLES: Syntax error at end of command: expecting
-`@{:@'.
+ctables.sps:26.54: error: CTABLES: Syntax error expecting `@{:@'.
+   26 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 KEY=MEAN.
+      |                                                      ^
 
-ctables.sps:27.54-27.55: error: CTABLES: Syntax error at `**': expecting INCLUDE
-or EXCLUDE.
+ctables.sps:27.54-27.55: error: CTABLES: Syntax error expecting INCLUDE or
+EXCLUDE.
+   27 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 MISSING=**.
+      |                                                      ^~
 
-ctables.sps:28.52-28.53: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:28.52-28.53: error: CTABLES: Syntax error expecting YES or NO.
+   28 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 TOTAL=**.
+      |                                                    ^~
 
-ctables.sps:29.52-29.53: error: CTABLES: Syntax error at `**': expecting string.
+ctables.sps:29.52-29.53: error: CTABLES: Syntax error expecting string.
+   29 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 LABEL=**.
+      |                                                    ^~
 
-ctables.sps:30.55-30.56: error: CTABLES: Syntax error at `**': expecting BEFORE
-or AFTER.
+ctables.sps:30.55-30.56: error: CTABLES: Syntax error expecting BEFORE or AFTER.
+   30 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 POSITION=**.
+      |                                                       ^~
 
-ctables.sps:31.52-31.53: error: CTABLES: Syntax error at `**': expecting INCLUDE
-or EXCLUDE.
+ctables.sps:31.52-31.53: error: CTABLES: Syntax error expecting INCLUDE or
+EXCLUDE.
+   31 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 EMPTY=**.
+      |                                                    ^~
 
-ctables.sps:32.46-32.47: error: CTABLES: Syntax error at `**': expecting ORDER,
-KEY, MISSING, TOTAL, LABEL, POSITION, or EMPTY.
+ctables.sps:32.46-32.47: error: CTABLES: Syntax error expecting ORDER, KEY,
+MISSING, TOTAL, LABEL, POSITION, or EMPTY.
+   32 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 **.
+      |                                              ^~
 
-ctables.sps:33.54-33.55: error: CTABLES: Syntax error at `**': expecting TOTAL,
-LABEL, POSITION, or EMPTY.
+ctables.sps:33.54-33.55: error: CTABLES: Syntax error expecting TOTAL, LABEL,
+POSITION, or EMPTY.
+   33 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 [1,2,3] **.
+      |                                                      ^~
 
-ctables.sps:34.36: error: CTABLES: Syntax error at `0': Expected positive
-integer for SUBTOTAL.
+ctables.sps:34.36: error: CTABLES: Syntax error expecting positive integer for
+SUBTOTAL.
+   34 | CTABLES /PCOMPUTE &k=EXPR(SUBTOTAL[0]).
+      |                                    ^
 
-ctables.sps:35.37-35.38: error: CTABLES: Syntax error at `**': expecting `@:>@'.
+ctables.sps:35.37-35.38: error: CTABLES: Syntax error expecting `@:>@'.
+   35 | CTABLES /PCOMPUTE &k=EXPR(SUBTOTAL[1**]).
+      |                                     ^~
 
-ctables.sps:36.31-36.32: error: CTABLES: Syntax error at `**': expecting THRU.
+ctables.sps:36.31-36.32: error: CTABLES: Syntax error expecting THRU.
+   36 | CTABLES /PCOMPUTE &k=EXPR([LO **]).
+      |                               ^~
 
-ctables.sps:37.36-37.37: error: CTABLES: Syntax error at `**': expecting number.
+ctables.sps:37.36-37.37: error: CTABLES: Syntax error expecting number.
+   37 | CTABLES /PCOMPUTE &k=EXPR([LO THRU **]).
+      |                                    ^~
 
-ctables.sps:38.35-38.36: error: CTABLES: Syntax error at `**': expecting number.
+ctables.sps:38.35-38.36: error: CTABLES: Syntax error expecting number.
+   38 | CTABLES /PCOMPUTE &k=EXPR([1 THRU **]).
+      |                                   ^~
 
-ctables.sps:39.29-39.30: error: CTABLES: Syntax error at `**': expecting `@:>@'.
+ctables.sps:39.29-39.30: error: CTABLES: Syntax error expecting `@:>@'.
+   39 | CTABLES /PCOMPUTE &k=EXPR([1**]).
+      |                             ^~
 
-ctables.sps:40.29: error: CTABLES: Syntax error at `x': expecting `@:}@'.
+ctables.sps:40.29: error: CTABLES: Syntax error expecting `@:}@'.
+   40 | CTABLES /PCOMPUTE &k=EXPR((1x)).
+      |                             ^
 
-ctables.sps:41.19-41.20: error: CTABLES: Syntax error at `**': expecting &.
+ctables.sps:41.19-41.20: error: CTABLES: Syntax error expecting &.
+   41 | CTABLES /PCOMPUTE **k.
+      |                   ^~
 
-ctables.sps:42.20: error: CTABLES: Syntax error at `1': expecting identifier.
+ctables.sps:42.20: error: CTABLES: Syntax error expecting identifier.
+   42 | CTABLES /PCOMPUTE &1.
+      |                    ^
 
-ctables.sps:43.21-43.22: error: CTABLES: Syntax error at `**': expecting `='.
+ctables.sps:43.21-43.22: error: CTABLES: Syntax error expecting `='.
+   43 | CTABLES /PCOMPUTE &k**.
+      |                     ^~
 
-ctables.sps:44.22-44.23: error: CTABLES: Syntax error at `**': expecting EXPR.
+ctables.sps:44.22-44.23: error: CTABLES: Syntax error expecting EXPR.
+   44 | CTABLES /PCOMPUTE &k=**.
+      |                      ^~
 
-ctables.sps:45.26-45.27: error: CTABLES: Syntax error at `**': expecting `('.
+ctables.sps:45.26-45.27: error: CTABLES: Syntax error expecting `@{:@'.
+   45 | CTABLES /PCOMPUTE &k=EXPR**.
+      |                          ^~
 
-ctables.sps:46.28: error: CTABLES: Syntax error at `x': expecting `)'.
+ctables.sps:46.28: error: CTABLES: Syntax error expecting `@:}@'.
+   46 | CTABLES /PCOMPUTE &k=EXPR(1x).
+      |                            ^
 
 ctables.sps:47.31-47.49: warning: CTABLES: New definition of &k will override
 the previous definition.
@@ -361,152 +436,249 @@ ctables.sps:47.10-47.28: note: CTABLES: This is the previous definition.
    47 | CTABLES /PCOMPUTE &k=EXPR(1) /PCOMPUTE &k=EXPR(2).
       |          ^~~~~~~~~~~~~~~~~~~
 
-ctables.sps:47.50: error: CTABLES: Syntax error at end of command: expecting
-`/'.
+ctables.sps:47.50: error: CTABLES: Syntax error expecting `/'.
+   47 | CTABLES /PCOMPUTE &k=EXPR(1) /PCOMPUTE &k=EXPR(2).
+      |                                                  ^
 
-ctables.sps:48.53-48.64: error: CTABLES: Syntax error at `NOTAFUNCTION':
-Expecting summary function name.
+ctables.sps:48.53-48.64: error: CTABLES: Syntax error expecting summary function
+name.
+   48 | CTABLES /PCOMPUTE &k=EXPR(1) /PPROPERTIES &k FORMAT=NOTAFUNCTION.
+      |                                                     ^~~~~~~~~~~~
 
-ctables.sps:49.59-49.60: error: CTABLES: Syntax error at `**': Expected number
-between 0 and 100 for PTILE.
+ctables.sps:49.59-49.60: error: CTABLES: Syntax error expecting number between 0
+and 100 for PTILE.
+   49 | CTABLES /PCOMPUTE &k=EXPR(1) /PPROPERTIES &k FORMAT=PTILE **.
+      |                                                           ^~
 
-ctables.sps:50.52-50.53: error: CTABLES: Syntax error at `**': expecting string.
+ctables.sps:50.52-50.53: error: CTABLES: Syntax error expecting string.
+   50 | CTABLES /PCOMPUTE &k=EXPR(1) /PPROPERTIES &k LABEL=**.
+      |                                                    ^~
 
-ctables.sps:51.61-51.62: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:51.61-51.62: error: CTABLES: Syntax error expecting YES or NO.
+   51 | CTABLES /PCOMPUTE &k=EXPR(1) /PPROPERTIES &k HIDESOURCECATS=**.
+      |                                                             ^~
 
-ctables.sps:52.46-52.47: error: CTABLES: Syntax error at `**': expecting LABEL,
-FORMAT, or HIDESOURCECATS.
+ctables.sps:52.46-52.47: error: CTABLES: Syntax error expecting LABEL, FORMAT,
+or HIDESOURCECATS.
+   52 | CTABLES /PCOMPUTE &k=EXPR(1) /PPROPERTIES &k **.
+      |                                              ^~
 
-ctables.sps:53.23-53.24: error: CTABLES: Syntax error at `**': expecting string.
+ctables.sps:53.23-53.24: error: CTABLES: Syntax error expecting string.
+   53 | CTABLES /FORMAT EMPTY=**.
+      |                       ^~
 
-ctables.sps:54.25-54.26: error: CTABLES: Syntax error at `**': expecting string.
+ctables.sps:54.25-54.26: error: CTABLES: Syntax error expecting string.
+   54 | CTABLES /FORMAT MISSING=**.
+      |                         ^~
 
-ctables.sps:55.17-55.18: error: CTABLES: Syntax error at `**': expecting
-MINCOLWIDTH, MAXCOLWIDTH, UNITS, EMPTY, or MISSING.
+ctables.sps:55.17-55.18: error: CTABLES: Syntax error expecting MINCOLWIDTH,
+MAXCOLWIDTH, UNITS, EMPTY, or MISSING.
+   55 | CTABLES /FORMAT **.
+      |                 ^~
 
-ctables.sps:56: error: CTABLES: MINCOLWIDTH must not be greater than
+ctables.sps:56.17-56.45: error: CTABLES: MINCOLWIDTH must not be greater than
 MAXCOLWIDTH.
+   56 | CTABLES /FORMAT MINCOLWIDTH=20 MAXCOLWIDTH=10/.
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ctables.sps:57.18-57.19: error: CTABLES: Syntax error expecting VARIABLES.
+   57 | CTABLES /VLABELS **.
+      |                  ^~
+
+ctables.sps:58.28-58.34: error: CTABLES: NOTAVAR is not a variable name.
+   58 | CTABLES /VLABELS VARIABLES=NOTAVAR.
+      |                            ^~~~~~~
+
+ctables.sps:59.32-59.33: error: CTABLES: Syntax error expecting DISPLAY.
+   59 | CTABLES /VLABELS VARIABLES=qn1 **.
+      |                                ^~
+
+ctables.sps:60.40-60.41: error: CTABLES: Syntax error expecting DEFAULT, NAME,
+LABEL, BOTH, or NONE.
+   60 | CTABLES /VLABELS VARIABLES=qn1 DISPLAY=**.
+      |                                        ^~
+
+ctables.sps:61.17-61.18: error: CTABLES: Syntax error expecting COUNTDUPLICATES.
+   61 | CTABLES /MRSETS **.
+      |                 ^~
+
+ctables.sps:62.33-62.34: error: CTABLES: Syntax error expecting YES or NO.
+   62 | CTABLES /MRSETS COUNTDUPLICATES=**.
+      |                                 ^~
+
+ctables.sps:63.19-63.20: error: CTABLES: Syntax error expecting VARIABLE or
+LISTWISE.
+   63 | CTABLES /SMISSING **.
+      |                   ^~
+
+ctables.sps:64.17-64.18: error: CTABLES: Syntax error expecting VARIABLE.
+   64 | CTABLES /WEIGHT **.
+      |                 ^~
+
+ctables.sps:65.26-65.32: error: CTABLES: NOTAVAR is not a variable name.
+   65 | CTABLES /WEIGHT VARIABLE=NOTAVAR.
+      |                          ^~~~~~~
+
+ctables.sps:66.32: error: CTABLES: Syntax error expecting integer 2 or greater
+for HIDESMALLCOUNTS COUNT.
+   66 | CTABLES /HIDESMALLCOUNTS COUNT=1.
+      |                                ^
+
+ctables.sps:67.10-67.13: error: CTABLES: Syntax error expecting one of the
+following: FORMAT, VLABELS, MRSETS, SMISSING, PCOMPUTE, PPROPERTIES, WEIGHT,
+HIDESMALLCOUNTS, TABLE.
+   67 | CTABLES /QUUX.
+      |          ^~~~
+
+ctables.sps:68.33: error: CTABLES: Syntax error expecting `/'.
+   68 | CTABLES /HIDESMALLCOUNTS COUNT=2.
+      |                                 ^
+
+ctables.sps:69.19-69.20: error: CTABLES: Syntax error expecting `/'.
+   69 | CTABLES /TABLE qn1**.
+      |                   ^~
+
+ctables.sps:70.38-70.39: error: CTABLES: Syntax error expecting COLUMN, ROW, or
+LAYER.
+   70 | CTABLES /TABLE qn1 /SLABELS POSITION=**.
+      |                                      ^~
+
+ctables.sps:71.37-71.38: error: CTABLES: Syntax error expecting YES or NO.
+   71 | CTABLES /TABLE qn1 /SLABELS VISIBLE=**.
+      |                                     ^~
+
+ctables.sps:72.29-72.30: error: CTABLES: Syntax error expecting POSITION or
+VISIBLE.
+   72 | CTABLES /TABLE qn1 /SLABELS **.
+      |                             ^~
+
+ctables.sps:73.39-73.40: error: CTABLES: Syntax error expecting OPPOSITE or
+LAYER.
+   73 | CTABLES /TABLE qn1 /CLABELS ROWLABELS=**.
+      |                                       ^~
+
+ctables.sps:74.39-74.40: error: CTABLES: Syntax error expecting OPPOSITE or
+LAYER.
+   74 | CTABLES /TABLE qn1 /CLABELS COLLABELS=**.
+      |                                       ^~
+
+ctables.sps:75.29-75.30: error: CTABLES: Syntax error expecting AUTO, ROWLABELS,
+or COLLABELS.
+   75 | CTABLES /TABLE qn1 /CLABELS **.
+      |                             ^~
+
+ctables.sps:76.30-76.31: error: CTABLES: Syntax error expecting CILEVEL.
+   76 | CTABLES /TABLE qn1 /CRITERIA **.
+      |                              ^~
+
+ctables.sps:77.38-77.40: error: CTABLES: Syntax error expecting number in
+@<:@0,100@:}@ for CILEVEL.
+   77 | CTABLES /TABLE qn1 /CRITERIA CILEVEL=101.
+      |                                      ^~~
+
+ctables.sps:78.28-78.29: error: CTABLES: Syntax error expecting CAPTION, CORNER,
+or TITLE.
+   78 | CTABLES /TABLE qn1 /TITLES **.
+      |                            ^~
+
+ctables.sps:79.34-79.35: error: CTABLES: Syntax error expecting CHISQUARE.
+   79 | CTABLES /TABLE qn1 /SIGTEST TYPE=**.
+      |                                  ^~
+
+ctables.sps:80.35-80.36: error: CTABLES: Syntax error expecting number in @<:@0,1@:}@
+for ALPHA.
+   80 | CTABLES /TABLE qn1 /SIGTEST ALPHA=**.
+      |                                   ^~
 
-ctables.sps:57.18-57.19: error: CTABLES: Syntax error at `**': expecting
-VARIABLES.
-
-ctables.sps:58: error: CTABLES: NOTAVAR is not a variable name.
-
-ctables.sps:59.32-59.33: error: CTABLES: Syntax error at `**': expecting
-DISPLAY.
-
-ctables.sps:60.40-60.41: error: CTABLES: Syntax error at `**': expecting
-DEFAULT, NAME, LABEL, BOTH, or NONE.
-
-ctables.sps:61.17-61.18: error: CTABLES: Syntax error at `**': expecting
-COUNTDUPLICATES.
-
-ctables.sps:62.33-62.34: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
-
-ctables.sps:63.19-63.20: error: CTABLES: Syntax error at `**': expecting
-VARIABLE or LISTWISE.
-
-ctables.sps:64.17-64.18: error: CTABLES: Syntax error at `**': expecting
-VARIABLE.
-
-ctables.sps:65: error: CTABLES: NOTAVAR is not a variable name.
-
-ctables.sps:66.32: error: CTABLES: Syntax error at `1': Expected integer 2 or
-greater for HIDESMALLCOUNTS COUNT.
-
-ctables.sps:67.10-67.13: error: CTABLES: Syntax error at `QUUX': expecting one
-of the following: FORMAT, VLABELS, MRSETS, SMISSING, PCOMPUTE, PPROPERTIES,
-WEIGHT, HIDESMALLCOUNTS, TABLE.
-
-ctables.sps:68.33: error: CTABLES: Syntax error at end of command: expecting
-`/'.
-
-ctables.sps:69.19-69.20: error: CTABLES: Syntax error at `**': expecting `/'.
-
-ctables.sps:70.38-70.39: error: CTABLES: Syntax error at `**': expecting COLUMN,
-ROW, or LAYER.
-
-ctables.sps:71.37-71.38: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
-
-ctables.sps:72.29-72.30: error: CTABLES: Syntax error at `**': expecting
-POSITION or VISIBLE.
-
-ctables.sps:73.39-73.40: error: CTABLES: Syntax error at `**': expecting
-OPPOSITE or LAYER.
-
-ctables.sps:74.39-74.40: error: CTABLES: Syntax error at `**': expecting
-OPPOSITE or LAYER.
-
-ctables.sps:75.29-75.30: error: CTABLES: Syntax error at `**': expecting AUTO,
-ROWLABELS, or COLLABELS.
-
-ctables.sps:76.30-76.31: error: CTABLES: Syntax error at `**': expecting
-CILEVEL.
-
-ctables.sps:77.38-77.40: error: CTABLES: Syntax error at `101': Expected number
-in @<:@0,100@:}@ for CILEVEL.
-
-ctables.sps:78.28-78.29: error: CTABLES: Syntax error at `**': expecting
-CAPTION, CORNER, or TITLE.
-
-ctables.sps:79.34-79.35: error: CTABLES: Syntax error at `**': expecting
-CHISQUARE.
-
-ctables.sps:80.35-80.36: error: CTABLES: Syntax error at `**': Expected number
-in @<:@0,1@:}@ for ALPHA.
+ctables.sps:81.43-81.44: error: CTABLES: Syntax error expecting YES or NO.
+   81 | CTABLES /TABLE qn1 /SIGTEST INCLUDEMRSETS=**.
+      |                                           ^~
 
-ctables.sps:81.43-81.44: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:82.40-82.41: error: CTABLES: Syntax error expecting ALLVISIBLE or
+SUBTOTALS.
+   82 | CTABLES /TABLE qn1 /SIGTEST CATEGORIES=**.
+      |                                        ^~
 
-ctables.sps:82.40-82.41: error: CTABLES: Syntax error at `**': expecting
-ALLVISIBLE or SUBTOTALS.
+ctables.sps:83.29-83.30: error: CTABLES: Syntax error expecting TYPE, ALPHA,
+INCLUDEMRSETS, or CATEGORIES.
+   83 | CTABLES /TABLE qn1 /SIGTEST **.
+      |                             ^~
 
-ctables.sps:83.29-83.30: error: CTABLES: Syntax error at `**': expecting TYPE,
-ALPHA, INCLUDEMRSETS, or CATEGORIES.
+ctables.sps:84.38-84.39: error: CTABLES: Syntax error expecting PROP or MEAN.
+   84 | CTABLES /TABLE qn1 /COMPARETEST TYPE=**.
+      |                                      ^~
 
-ctables.sps:84.38-84.39: error: CTABLES: Syntax error at `**': expecting PROP or
-MEAN.
+ctables.sps:85.39-85.40: error: CTABLES: Syntax error expecting number in (0,1)
+for ALPHA.
+   85 | CTABLES /TABLE qn1 /COMPARETEST ALPHA=**.
+      |                                       ^~
 
-ctables.sps:85.39-85.40: error: CTABLES: Syntax error at `**': Expected number
-in (0,1) for ALPHA.
+ctables.sps:86.39: error: CTABLES: Syntax error expecting number in (0,1) for
+ALPHA.
+   86 | CTABLES /TABLE qn1 /COMPARETEST ALPHA=0,5.
+      |                                       ^
 
-ctables.sps:86.39: error: CTABLES: Syntax error at `0': Expected number in (0,1)
-for ALPHA.
+ctables.sps:87.40-87.41: error: CTABLES: Syntax error expecting BONFERRONI, BH,
+or NONE.
+   87 | CTABLES /TABLE qn1 /COMPARETEST ADJUST=**.
+      |                                        ^~
 
-ctables.sps:87.40-87.41: error: CTABLES: Syntax error at `**': expecting
-BONFERRONI, BH, or NONE.
+ctables.sps:88.47-88.48: error: CTABLES: Syntax error expecting YES or NO.
+   88 | CTABLES /TABLE qn1 /COMPARETEST INCLUDEMRSETS=**.
+      |                                               ^~
 
-ctables.sps:88.47-88.48: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:89.47-89.48: error: CTABLES: Syntax error expecting ALLCATS or
+TESTEDCATS.
+   89 | CTABLES /TABLE qn1 /COMPARETEST MEANSVARIANCE=**.
+      |                                               ^~
 
-ctables.sps:89.47-89.48: error: CTABLES: Syntax error at `**': expecting ALLCATS
-or TESTEDCATS.
+ctables.sps:90.44-90.45: error: CTABLES: Syntax error expecting ALLVISIBLE or
+SUBTOTALS.
+   90 | CTABLES /TABLE qn1 /COMPARETEST CATEGORIES=**.
+      |                                            ^~
 
-ctables.sps:90.44-90.45: error: CTABLES: Syntax error at `**': expecting
-ALLVISIBLE or SUBTOTALS.
+ctables.sps:91.39-91.40: error: CTABLES: Syntax error expecting YES or NO.
+   91 | CTABLES /TABLE qn1 /COMPARETEST MERGE=**.
+      |                                       ^~
 
-ctables.sps:91.39-91.40: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:92.39-92.40: error: CTABLES: Syntax error expecting APA or SIMPLE.
+   92 | CTABLES /TABLE qn1 /COMPARETEST STYLE=**.
+      |                                       ^~
 
-ctables.sps:92.39-92.40: error: CTABLES: Syntax error at `**': expecting APA or
-SIMPLE.
+ctables.sps:93.41-93.42: error: CTABLES: Syntax error expecting YES or NO.
+   93 | CTABLES /TABLE qn1 /COMPARETEST SHOWSIG=**.
+      |                                         ^~
 
-ctables.sps:93.41-93.42: error: CTABLES: Syntax error at `**': expecting YES or
-NO.
+ctables.sps:94.33-94.34: error: CTABLES: Syntax error expecting one of the
+following: TYPE, ALPHA, ADJUST, INCLUDEMRSETS, MEANSVARIANCE, CATEGORIES, MERGE,
+STYLE, SHOWSIG.
+   94 | CTABLES /TABLE qn1 /COMPARETEST **.
+      |                                 ^~
 
-ctables.sps:94.33-94.34: error: CTABLES: Syntax error at `**': expecting one of
-the following: TYPE, ALPHA, ADJUST, INCLUDEMRSETS, MEANSVARIANCE, CATEGORIES,
-MERGE, STYLE, SHOWSIG.
+ctables.sps:95.21-95.26: error: CTABLES: Syntax error expecting TABLE, SLABELS,
+CLABELS, CRITERIA, CATEGORIES, TITLES, SIGTEST, or COMPARETEST.
+   95 | CTABLES /TABLE qn1 /FORMAT.
+      |                     ^~~~~~
 
-ctables.sps:95.22-95.23: error: CTABLES: Syntax error at `**': expecting TABLE,
-SLABELS, CLABELS, CRITERIA, CATEGORIES, TITLES, SIGTEST, or COMPARETEST.
+ctables.sps:95.21-95.26: note: CTABLES: This subcommand must appear before
+TABLE.
+   95 | CTABLES /TABLE qn1 /FORMAT.
+      |                     ^~~~~~
 
 ctables.sps:96: error: CTABLES: ROWLABELS and COLLABELS may not both be
 specified.
 
+ctables.sps:96.21-96.46: note: CTABLES: This is the first specification.
+   96 | CTABLES /TABLE qn1 /CLABELS ROWLABELS=OPPOSITE /CLABELS
+COLLABELS=OPPOSITE.
+      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ctables.sps:96.49-96.74: note: CTABLES: This is the second specification.
+   96 | CTABLES /TABLE qn1 /CLABELS ROWLABELS=OPPOSITE /CLABELS
+COLLABELS=OPPOSITE.
+      |
+^~~~~~~~~~~~~~~~~~~~~~~~~~
+
 ctables.sps:97.16-97.26: error: CTABLES: Cannot nest scale variables.
    97 | CTABLES /TABLE qn20 > qnd1.
       |                ^~~~~~~~~~~
@@ -573,140 +745,155 @@ CTABLES /TABLE qn113 /COMPARETEST TYPE=PROP.
 CTABLES /TABLE qn113 [COUNT.UCL].
 
 CTABLES /TABLE qn1 /CATEGORIES **.
+
+CTABLES /TITLES.
 ]])
-AT_CHECK([pspp ctables.sps -O box=unicode -O width=80], [1],
-[[ctables.sps:2.76-2.78: error: CTABLES: Computed category &pc references a
-category not included in the category list.
-    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES
-VARIABLES=qn1 [&pc].
-      |
-^~~
+AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [1],
+[[ctables.sps:2.76-2.78: error: CTABLES: Computed category &pc references a category not included in the category list.
+    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc].
+      |                                                                            ^~~
 
 ctables.sps:2.28-2.35: note: CTABLES: This is the missing category.
-    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES
-VARIABLES=qn1 [&pc].
+    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc].
       |                            ^~~~~~~~
 
-ctables.sps:2.76-2.79: note: CTABLES: To fix the problem, add subtotals to the
-list of categories here.
-    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES
-VARIABLES=qn1 [&pc].
-      |
-^~~~
+ctables.sps:2.76-2.79: note: CTABLES: To fix the problem, add subtotals to the list of categories here.
+    2 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc].
+      |                                                                            ^~~~
 
-ctables.sps:3.73-3.75: error: CTABLES: Computed category &pc references a
-category not included in the category list.
-    3 | CTABLES /PCOMPUTE &pc=EXPR(TOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1
-[&pc].
-      |
-^~~
+ctables.sps:3.73-3.75: error: CTABLES: Computed category &pc references a category not included in the category list.
+    3 | CTABLES /PCOMPUTE &pc=EXPR(TOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc].
+      |                                                                         ^~~
 
 ctables.sps:3.28-3.32: note: CTABLES: This is the missing category.
-    3 | CTABLES /PCOMPUTE &pc=EXPR(TOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1
-[&pc].
+    3 | CTABLES /PCOMPUTE &pc=EXPR(TOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc].
       |                            ^~~~~
 
-ctables.sps:3: note: CTABLES: To fix the problem, add TOTAL=YES to the
-variable's CATEGORIES specification.
+ctables.sps:3: note: CTABLES: To fix the problem, add TOTAL=YES to the variable's CATEGORIES specification.
 
-ctables.sps:4.76-4.99: error: CTABLES: These categories include 2 instances of
-SUBTOTAL or HSUBTOTAL, so references from computed categories must refer to
-subtotals by position, e.g. SUBTOTAL[1].
-    4 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES
-VARIABLES=qn1 [&pc, SUBTOTAL, SUBTOTAL].
-      |
-^~~~~~~~~~~~~~~~~~~~~~~~
+ctables.sps:4.76-4.99: error: CTABLES: These categories include 2 instances of SUBTOTAL or HSUBTOTAL, so references from
+computed categories must refer to subtotals by position, e.g. SUBTOTAL[1].
+    4 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc, SUBTOTAL, SUBTOTAL].
+      |                                                                            ^~~~~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:4.28-4.35: note: CTABLES: This is the reference that lacks a
-position.
-    4 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES
-VARIABLES=qn1 [&pc, SUBTOTAL, SUBTOTAL].
+ctables.sps:4.28-4.35: note: CTABLES: This is the reference that lacks a position.
+    4 | CTABLES /PCOMPUTE &pc=EXPR(SUBTOTAL) /TABLE qn1 /CATEGORIES VARIABLES=qn1 [&pc, SUBTOTAL, SUBTOTAL].
       |                            ^~~~~~~~
 
-ctables.sps:7.47-7.54: error: CTABLES: This category specification may be
-applied only to string variables, but this subcommand tries to apply it to
-numeric variable QN1.
+ctables.sps:7.47-7.54: error: CTABLES: This category specification may be applied only to string variables, but this
+subcommand tries to apply it to numeric variable QN1.
     7 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 ['string'].
       |                                               ^~~~~~~~
 
-ctables.sps:8.53: error: CTABLES: This category specification may be applied
-only to numeric variables, but this subcommand tries to apply it to string
-variable string.
+ctables.sps:8.53: error: CTABLES: This category specification may be applied only to numeric variables, but this
+subcommand tries to apply it to string variable string.
     8 | CTABLES /TABLE string /CATEGORIES VARIABLES=string [1].
       |                                                     ^
 
-ctables.sps:10.74-10.86: error: CTABLES: Syntax error at `KEY=MEAN(qn1)': Data-
-dependent sorting is not implemented.
+ctables.sps:10.74-10.86: error: CTABLES: Data-dependent sorting is not implemented.
+   10 | CTABLES /TABLE qn1 /CLABELS ROWLABELS=OPPOSITE /CATEGORIES VARIABLES=qn1 KEY=MEAN(qn1).
+      |                                                                          ^~~~~~~~~~~~~
+
+ctables.sps:12: error: CTABLES: To move category labels from one axis to another, the variables whose labels are to be
+moved must be categorical, but qnd1 is scale.
+
+ctables.sps:12.22-12.47: note: CTABLES: This syntax moves category labels to another axis.
+   12 | CTABLES /TABLE qnd1 /CLABELS ROWLABELS=OPPOSITE.
+      |                      ^~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ctables.sps:13: error: CTABLES: To move category labels from one axis to another, the variables whose labels are to be
+moved must all have the same width, but QN1 has width 0 and string has width 8.
+
+ctables.sps:13.30-13.55: note: CTABLES: This syntax moves category labels to another axis.
+   13 | CTABLES /TABLE qn1 + string /CLABELS ROWLABELS=OPPOSITE.
+      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:12: error: CTABLES: ROWLABELS=OPPOSITE requires the variables to be
-moved to be categorical, but qnd1 is a scale variable.
+ctables.sps:14: error: CTABLES: To move category labels from one axis to another, the variables whose labels are to be
+moved must all have the same value labels, but QN1 and QNSA1 have different value labels.
 
-ctables.sps:13: error: CTABLES: ROWLABELS=OPPOSITE requires the variables to be
-moved to have the same width, but QN1 has width 0 and string has width 8.
+ctables.sps:14.29-14.54: note: CTABLES: This syntax moves category labels to another axis.
+   14 | CTABLES /TABLE qn1 + qnsa1 /CLABELS ROWLABELS=OPPOSITE.
+      |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:14: error: CTABLES: ROWLABELS=OPPOSITE requires the variables to be
-moved to have the same value labels, but QN1 and QNSA1 have different value
-labels.
+ctables.sps:15: error: CTABLES: To move category labels from one axis to another, the variables whose labels are to be
+moved must all have the same category specifications, but QN105BA and QN105BB have different category specifications.
 
-ctables.sps:15: error: CTABLES: ROWLABELS=OPPOSITE requires the variables to be
-moved to have the same category specifications, but QN105BA and QN105BB have
-different category specifications.
+ctables.sps:15.35-15.60: note: CTABLES: This syntax moves category labels to another axis.
+   15 | CTABLES /TABLE qn105ba + qn105bb /CLABELS ROWLABELS=OPPOSITE /CATEGORIES VARIABLES=qn105ba [1,2,3].
+      |                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:17.27-17.33: warning: CTABLES: The exponentiation operator (`**') is
-left-associative: `a**b**c' equals `(a**b)**c', not `a**(b**c)'.  To disable
-this warning, insert parentheses.
+ctables.sps:17.27-17.33: warning: CTABLES: The exponentiation operator (`**') is left-associative: `a**b**c' equals
+`(a**b)**c', not `a**(b**c)'.  To disable this warning, insert parentheses.
    17 | CTABLES /PCOMPUTE &x=EXPR(1**2**3).
       |                           ^~~~~~~
 
-ctables.sps:17.35: error: CTABLES: Syntax error at end of command: expecting
-`/'.
+ctables.sps:17.35: error: CTABLES: Syntax error expecting `/'.
+   17 | CTABLES /PCOMPUTE &x=EXPR(1**2**3).
+      |                                   ^
 
-ctables.sps:18.28-18.29: error: CTABLES: Syntax error at `**'.
+ctables.sps:18.28-18.29: error: CTABLES: Syntax error.
+   18 | CTABLES /PCOMPUTE &x=EXPR([**]).
+      |                            ^~
 
-ctables.sps:19.27-19.28: error: CTABLES: Syntax error at `**'.
+ctables.sps:19.27-19.28: error: CTABLES: Syntax error.
+   19 | CTABLES /PCOMPUTE &x=EXPR(**).
+      |                           ^~
 
-ctables.sps:21.15: error: CTABLES: Syntax error at end of command: At least one
-variable must be specified.
+ctables.sps:21.15: error: CTABLES: At least one variable must be specified.
+   21 | CTABLES /TABLE.
+      |               ^
 
 ctables.sps:23: error: CTABLES: Summaries may appear only on one axis.
 
-ctables.sps:23.50-23.54: note: CTABLES: This variable on the layers axis has a
-summary.
+ctables.sps:23.50-23.54: note: CTABLES: This variable on the layers axis has a summary.
    23 | CTABLES /TABLE qn113 [COUNT] BY qn114 [COUNT] BY qn116 [COUNT].
       |                                                  ^~~~~
 
-ctables.sps:23.16-23.20: note: CTABLES: This variable on the rows axis has a
-summary.
+ctables.sps:23.16-23.20: note: CTABLES: This variable on the rows axis has a summary.
    23 | CTABLES /TABLE qn113 [COUNT] BY qn114 [COUNT] BY qn116 [COUNT].
       |                ^~~~~
 
-ctables.sps:23.33-23.37: note: CTABLES: This variable on the columns axis has a
-summary.
+ctables.sps:23.33-23.37: note: CTABLES: This variable on the columns axis has a summary.
    23 | CTABLES /TABLE qn113 [COUNT] BY qn114 [COUNT] BY qn116 [COUNT].
       |                                 ^~~~~
 
-ctables.sps:23.33-23.37: note: CTABLES: This is a scale variable, so it always
-has a summary even if the syntax does not explicitly specify one.
+ctables.sps:23.33-23.37: note: CTABLES: This is a scale variable, so it always has a summary even if the syntax does not
+explicitly specify one.
    23 | CTABLES /TABLE qn113 [COUNT] BY qn114 [COUNT] BY qn116 [COUNT].
       |                                 ^~~~~
 
-ctables.sps:25.46-25.63: error: CTABLES: Syntax error at `KEY=PTILE(qn1, 50)':
-Data-dependent sorting is not implemented.
+ctables.sps:25.46-25.63: error: CTABLES: Data-dependent sorting is not implemented.
+   25 | CTABLES /TABLE qn1 /CATEGORIES VARIABLES=qn1 KEY=PTILE(qn1, 50).
+      |                                              ^~~~~~~~~~~~~~~~~~
+
+ctables.sps:27.16-27.21: error: CTABLES: Multiple response set support not implemented.
+   27 | CTABLES /TABLE $mrset.
+      |                ^~~~~~
+
+ctables.sps:29.23-29.44: error: CTABLES: Support for SIGTEST not yet implemented.
+   29 | CTABLES /TABLE qn113 /SIGTEST TYPE=CHISQUARE.
+      |                       ^~~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:27.16-27.21: error: CTABLES: Syntax error at `$mrset': Multiple
-response set support not implemented.
+ctables.sps:30.23-30.43: error: CTABLES: Support for COMPARETEST not yet implemented.
+   30 | CTABLES /TABLE qn113 /COMPARETEST TYPE=PROP.
+      |                       ^~~~~~~~~~~~~~~~~~~~~
 
-ctables.sps:29.23-29.44: error: CTABLES: Syntax error at `SIGTEST
-TYPE=CHISQUARE': Support for SIGTEST not yet implemented.
+ctables.sps:32.23-32.31: error: CTABLES: Support for LCL, UCL, and SE summary functions is not yet implemented.
+   32 | CTABLES /TABLE qn113 [COUNT.UCL].
+      |                       ^~~~~~~~~
 
-ctables.sps:30.35-30.43: error: CTABLES: Syntax error at `TYPE=PROP': Support
-for COMPARETEST not yet implemented.
+ctables.sps:34.32-34.33: error: CTABLES: Syntax error expecting VARIABLES.
+   34 | CTABLES /TABLE qn1 /CATEGORIES **.
+      |                                ^~
 
-ctables.sps:32.23-32.31: error: CTABLES: Syntax error at `COUNT.UCL': Support
-for LCL, UCL, and SE summary functions is not yet implemented.
+ctables.sps:36.10-36.15: error: CTABLES: Syntax error expecting one of the following: FORMAT, VLABELS, MRSETS, SMISSING,
+PCOMPUTE, PPROPERTIES, WEIGHT, HIDESMALLCOUNTS, TABLE.
+   36 | CTABLES /TITLES.
+      |          ^~~~~~
 
-ctables.sps:34.32-34.33: error: CTABLES: Syntax error at `**': expecting
-VARIABLES.
+ctables.sps:36.10-36.15: note: CTABLES: TABLE must appear before this subcommand.
+   36 | CTABLES /TITLES.
+      |          ^~~~~~
 ]])
 AT_CLEANUP
 
index 8a1c26a7d472af03fa8c2b8efdef2d9f944ea10c..c06737d2b251f20d46547f18123fb302bd612080 100644 (file)
@@ -454,9 +454,14 @@ END DATA.
 DESCRIPTIVES ALL/STATISTICS=COUNT MEAN.
 ])
 AT_CHECK([pspp descriptives.sps], [1], [dnl
-descriptives.sps:5.29-5.33: error: DESCRIPTIVES: Syntax error at `COUNT':
-expecting statistic name: reverting to default.
-
-descriptives.sps:5.35-5.38: error: DESCRIPTIVES: Syntax error at `MEAN'.
+descriptives.sps:5.29-5.33: error: DESCRIPTIVES: Syntax error expecting one of
+the following: MEAN, SEMEAN, STDDEV, VARIANCE, KURTOSIS, SEKURTOSIS, SKEWNESS,
+SESKEWNESS, RANGE, MINIMUM, MAXIMUM, SUM.
+    5 | DESCRIPTIVES ALL/STATISTICS=COUNT MEAN.
+      |                             ^~~~~
+
+descriptives.sps:5.35-5.38: error: DESCRIPTIVES: Syntax error.
+    5 | DESCRIPTIVES ALL/STATISTICS=COUNT MEAN.
+      |                                   ^~~~
 ])
 AT_CLEANUP
index 155350dde7e20c675770909901a49d2d1ca66901..f462b1fe2b132af4733131ec411f3beff86954d2 100644 (file)
@@ -43,7 +43,9 @@ x,11,12,13,14
 y,16,17,18,19
 z,21,22,23,24
 
-flip.sps:12: warning: FLIP: FLIP ignores TEMPORARY.  Temporary transformations will be made permanent.
+"flip.sps:12.1-12.4: warning: FLIP: FLIP ignores TEMPORARY.  Temporary transformations will be made permanent.
+   12 | flip newnames=n.
+      | ^~~~"
 
 Table: Data List
 CASE_LBL,v,w,x,y,z
index b0e62d0b1cc2cf4cf36e1e5e292f26e0f3a2e4c0..a152cbf009c6b228bd34c7c0499a05bebd3a5cdb 100644 (file)
@@ -930,15 +930,21 @@ COMPUTE y(5)=1.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.10: error: COMPUTE: Syntax error at end of command: expecting `='.
+matrix.sps:2.10: error: COMPUTE: Syntax error expecting `='.
+    2 | COMPUTE x.
+      |          ^
 
-matrix.sps:3.11: error: COMPUTE: Syntax error at end of command.
+matrix.sps:3.11: error: COMPUTE: Syntax error.
+    3 | COMPUTE x=.
+      |           ^
 
 matrix.sps:4.9: error: MATRIX: Undefined variable x.
     4 | COMPUTE x(5)=1.
       |         ^
 
-matrix.sps:5: error: COMPUTE: Undefined variable y.
+matrix.sps:5.9: error: COMPUTE: Undefined variable y.
+    5 | COMPUTE y(5)=1.
+      |         ^
 ])
 AT_CLEANUP
 
@@ -3121,7 +3127,9 @@ BREAK.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2: error: BREAK: BREAK not inside LOOP.
+matrix.sps:2.1-2.5: error: BREAK: BREAK not inside LOOP.
+    2 | BREAK.
+      | ^~~~~
 ])
 AT_CLEANUP
 
@@ -3302,59 +3310,130 @@ xyzzy
 .
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.6: error: READ: Syntax error at `!': expecting identifier.
+matrix.sps:2.6: error: READ: Syntax error expecting identifier.
+    2 | READ !.
+      |      ^
 
-matrix.sps:3.13: error: READ: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:3.13: error: READ: Syntax error expecting a file name or handle
+name.
+    3 | READ x/FILE=!.
+      |             ^
 
-matrix.sps:4.17: error: READ: Syntax error at `!': expecting string.
+matrix.sps:4.17: error: READ: Syntax error expecting string.
+    4 | READ x/ENCODING=!.
+      |                 ^
 
-matrix.sps:5.14: error: READ: Syntax error at `!': Expected positive integer
-for FIELD.
+matrix.sps:5.14: error: READ: Syntax error expecting positive integer for
+FIELD.
+    5 | READ x/FIELD=!.
+      |              ^
 
-matrix.sps:6.16: error: READ: Syntax error at `!': expecting `TO'.
+matrix.sps:6.16: error: READ: Syntax error expecting `TO'.
+    6 | READ x/FIELD=1 !.
+      |                ^
 
-matrix.sps:7.19: error: READ: Syntax error at `!': Expected positive integer
-for TO.
+matrix.sps:7.19: error: READ: Syntax error expecting positive integer for TO.
+    7 | READ x/FIELD=1 TO !.
+      |                   ^
 
-matrix.sps:8.19: error: READ: Syntax error at `0': Expected positive integer
-for TO.
+matrix.sps:8.19: error: READ: Syntax error expecting positive integer for TO.
+    8 | READ x/FIELD=1 TO 0.
+      |                   ^
 
-matrix.sps:9.25: error: READ: Syntax error at `!': Expected integer between 1
-and 10 for BY.
+matrix.sps:9.25: error: READ: Syntax error expecting integer between 1 and 10
+for BY.
+    9 | READ x/FIELD=1 TO 10 BY !.
+      |                         ^
 
-matrix.sps:10: error: READ: BY 6 does not evenly divide record width 10.
+matrix.sps:10.14-10.25: error: READ: Field width 6 does not evenly divide
+record width 10.
+   10 | READ x/FIELD=1 TO 10 BY 6.
+      |              ^~~~~~~~~~~~
+
+matrix.sps:10.14-10.20: note: READ: This syntax designates the record width.
+   10 | READ x/FIELD=1 TO 10 BY 6.
+      |              ^~~~~~~
 
-matrix.sps:11.13: error: READ: Syntax error at `!'.
+matrix.sps:10.25: note: READ: This syntax specifies the field width.
+   10 | READ x/FIELD=1 TO 10 BY 6.
+      |                         ^
 
-matrix.sps:12.13: error: READ: Syntax error at `!': expecting RECTANGULAR or
-SYMMETRIC.
+matrix.sps:11.13: error: READ: Syntax error.
+   11 | READ x/SIZE=!.
+      |             ^
 
-matrix.sps:13.15: error: READ: Syntax error at `!': expecting identifier.
+matrix.sps:12.13: error: READ: Syntax error expecting RECTANGULAR or SYMMETRIC.
+   12 | READ x/MODE=!.
+      |             ^
 
-matrix.sps:14: error: READ: Subcommand FORMAT may only be specified once.
+matrix.sps:13.15: error: READ: Syntax error expecting identifier.
+   13 | READ x/FORMAT=!.
+      |               ^
+
+matrix.sps:14.20-14.25: error: READ: Subcommand FORMAT may only be specified
+once.
+   14 | READ x/FORMAT=F8.2/FORMAT=F8.2.
+      |                    ^~~~~~
 
-matrix.sps:15.15-15.22: error: READ: Syntax error at `'5XYZZY'': Unknown format
-XYZZY.
+matrix.sps:15.15-15.22: error: READ: Unknown format XYZZY.
+   15 | READ x/FORMAT='5XYZZY'.
+      |               ^~~~~~~~
 
-matrix.sps:16: error: READ: Unknown format type `XYZZY'.
+matrix.sps:16.15-16.19: error: READ: Unknown format type `XYZZY'.
+   16 | READ x/FORMAT=XYZZY.
+      |               ^~~~~
 
-matrix.sps:17.8: error: READ: Syntax error at `!': expecting FILE, FIELD, MODE,
-REREAD, or FORMAT.
+matrix.sps:17.8: error: READ: Syntax error expecting FILE, FIELD, MODE, REREAD,
+or FORMAT.
+   17 | READ x/!.
+      |        ^
 
 matrix.sps:18: error: READ: Required subcommand FIELD was not specified.
 
 matrix.sps:19: error: READ: SIZE is required for reading data into a full
 matrix (as opposed to a submatrix).
 
+matrix.sps:19.6: note: READ: This expression designates a full matrix.
+   19 | READ x/FIELD=1 TO 10.
+      |      ^
+
 matrix.sps:20: error: READ: Required subcommand FILE was not specified.
 
 matrix.sps:21: error: READ: 15 repetitions cannot fit in record width 10.
 
-matrix.sps:22: error: READ: FORMAT specifies field width 5 but BY specifies 2.
+matrix.sps:21.57-21.61: note: READ: This syntax designates the number of
+repetitions.
+   21 | READ x/FIELD=1 TO 10/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT='15F'.
+      |                                                         ^~~~~
+
+matrix.sps:21.14-21.20: note: READ: This syntax designates the record width.
+   21 | READ x/FIELD=1 TO 10/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT='15F'.
+      |              ^~~~~~~
+
+matrix.sps:22: error: READ: This command specifies two different field widths.
 
-matrix.sps:23: error: READ: FORMAT specifies 2 repetitions with record width
-10, which implies field width 5, but BY specifies field width 2.
+matrix.sps:22.62-22.63: note: READ: This syntax specifies field width 5.
+   22 | READ x/FIELD=1 TO 10 BY 2/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT=F5.
+      |                                                              ^~
+
+matrix.sps:22.25: note: READ: This syntax specifies field width 2.
+   22 | READ x/FIELD=1 TO 10 BY 2/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT=F5.
+      |                         ^
+
+matrix.sps:23: error: READ: This command specifies two different field widths.
+
+matrix.sps:23.62-23.65: note: READ: This syntax specifies 2 repetitions.
+   23 | READ x/FIELD=1 TO 10 BY 2/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT='2F'.
+      |                                                              ^~~~
+
+matrix.sps:23.14-23.20: note: READ: This syntax designates record width 10,
+which divided by 2 repetitions implies field width 5.
+   23 | READ x/FIELD=1 TO 10 BY 2/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT='2F'.
+      |              ^~~~~~~
+
+matrix.sps:23.25: note: READ: This syntax specifies field width 2.
+   23 | READ x/FIELD=1 TO 10 BY 2/SIZE={1,2}/FILE='xyzzy.txt'/FORMAT='2F'.
+      |                         ^
 
 matrix.sps:24.27-24.35: error: MATRIX: SIZE must evaluate to a scalar or a 2-
 element vector, not a 2×2 matrix.
@@ -3456,52 +3535,108 @@ WRITE {1,2}/FIELD=1 TO 10/OUTFILE='matrix.txt'/MODE=TRIANGULAR.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.7: error: WRITE: Syntax error at `!'.
+matrix.sps:2.7: error: WRITE: Syntax error.
+    2 | WRITE !.
+      |       ^
 
-matrix.sps:3.17: error: WRITE: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:3.17: error: WRITE: Syntax error expecting a file name or handle
+name.
+    3 | WRITE 1/OUTFILE=!.
+      |                 ^
 
-matrix.sps:4.18: error: WRITE: Syntax error at `!': expecting string.
+matrix.sps:4.18: error: WRITE: Syntax error expecting string.
+    4 | WRITE 1/ENCODING=!.
+      |                  ^
 
-matrix.sps:5.15: error: WRITE: Syntax error at `!': Expected positive integer
-for FIELD.
+matrix.sps:5.15: error: WRITE: Syntax error expecting positive integer for
+FIELD.
+    5 | WRITE 1/FIELD=!.
+      |               ^
 
-matrix.sps:6.17: error: WRITE: Syntax error at `!': expecting `TO'.
+matrix.sps:6.17: error: WRITE: Syntax error expecting `TO'.
+    6 | WRITE 1/FIELD=1 !.
+      |                 ^
 
-matrix.sps:7.20: error: WRITE: Syntax error at `0': Expected positive integer
-for TO.
+matrix.sps:7.20: error: WRITE: Syntax error expecting positive integer for TO.
+    7 | WRITE 1/FIELD=1 TO 0.
+      |                    ^
 
-matrix.sps:8.26-8.27: error: WRITE: Syntax error at `20': Expected integer
-between 1 and 10 for BY.
+matrix.sps:8.26-8.27: error: WRITE: Syntax error expecting integer between 1
+and 10 for BY.
+    8 | WRITE 1/FIELD=1 TO 10 BY 20.
+      |                          ^~
+
+matrix.sps:9.15-9.26: error: WRITE: Field width 6 does not evenly divide record
+width 10.
+    9 | WRITE 1/FIELD=1 TO 10 BY 6.
+      |               ^~~~~~~~~~~~
+
+matrix.sps:9.15-9.21: note: WRITE: This syntax designates the record width.
+    9 | WRITE 1/FIELD=1 TO 10 BY 6.
+      |               ^~~~~~~
+
+matrix.sps:9.26: note: WRITE: This syntax specifies the field width.
+    9 | WRITE 1/FIELD=1 TO 10 BY 6.
+      |                          ^
+
+matrix.sps:10.14-10.24: error: WRITE: Syntax error expecting RECTANGULAR or
+TRIANGULAR.
+   10 | WRITE 1/MODE=TRAPEZOIDAL.
+      |              ^~~~~~~~~~~
+
+matrix.sps:11.19-11.24: error: WRITE: Subcommand FORMAT may only be specified
+once.
+   11 | WRITE 1/FORMAT=F5/FORMAT=F5.
+      |                   ^~~~~~
 
-matrix.sps:9: error: WRITE: BY 6 does not evenly divide record width 10.
+matrix.sps:12.16-12.22: error: WRITE: Unknown format ASDF.
+   12 | WRITE 1/FORMAT='5ASDF'.
+      |                ^~~~~~~
 
-matrix.sps:10.14-10.24: error: WRITE: Syntax error at `TRAPEZOIDAL': expecting
-RECTANGULAR or TRIANGULAR.
+matrix.sps:13.16-13.20: error: WRITE: Unknown format type `ASDF'.
+   13 | WRITE 1/FORMAT=ASDF5.
+      |                ^~~~~
 
-matrix.sps:11: error: WRITE: Subcommand FORMAT may only be specified once.
+matrix.sps:14.9: error: WRITE: Syntax error expecting OUTFILE, FIELD, MODE,
+HOLD, or FORMAT.
+   14 | WRITE 1/!.
+      |         ^
 
-matrix.sps:12.16-12.22: error: WRITE: Syntax error at `'5ASDF'': Unknown format
-ASDF.
+matrix.sps:15: error: WRITE: Required subcommand FIELD was not specified.
 
-matrix.sps:13: error: WRITE: Unknown format type `ASDF'.
+matrix.sps:16: error: WRITE: Required subcommand OUTFILE was not specified.
 
-matrix.sps:14.9: error: WRITE: Syntax error at `!': expecting OUTFILE, FIELD,
-MODE, HOLD, or FORMAT.
+matrix.sps:17.51-17.55: note: WRITE: This syntax designates the number of
+repetitions.
+   17 | WRITE 1/FIELD=1 TO 10/OUTFILE='matrix.txt'/FORMAT='15F'.
+      |                                                   ^~~~~
 
-matrix.sps:15: error: WRITE: Required subcommand FIELD was not specified.
+matrix.sps:17.15-17.21: note: WRITE: This syntax designates the record width.
+   17 | WRITE 1/FIELD=1 TO 10/OUTFILE='matrix.txt'/FORMAT='15F'.
+      |               ^~~~~~~
 
-matrix.sps:16: error: WRITE: Required subcommand OUTFILE was not specified.
+matrix.sps:18: error: WRITE: This command specifies two different field widths.
+
+matrix.sps:18.56-18.59: note: WRITE: This syntax specifies 5 repetitions.
+   18 | WRITE 1/FIELD=1 TO 10 BY 5/OUTFILE='matrix.txt'/FORMAT='5F'.
+      |                                                        ^~~~
 
-matrix.sps:17: error: WRITE: 15 repetitions cannot fit in record width 10.
+matrix.sps:18.15-18.21: note: WRITE: This syntax designates record width 10,
+which divided by 5 repetitions implies field width 2.
+   18 | WRITE 1/FIELD=1 TO 10 BY 5/OUTFILE='matrix.txt'/FORMAT='5F'.
+      |               ^~~~~~~
 
-matrix.sps:18: error: WRITE: FORMAT specifies 5 repetitions with record width
-10, which implies field width 2, but BY specifies field width 5.
+matrix.sps:18.26: note: WRITE: This syntax specifies field width 5.
+   18 | WRITE 1/FIELD=1 TO 10 BY 5/OUTFILE='matrix.txt'/FORMAT='5F'.
+      |                          ^
 
 matrix.sps:19: error: WRITE: Output format E5.0 specifies width 5, but E
 requires a width between 6 and 40.
 
-matrix.sps:20: error: WRITE: Format A9 is too wide for 8-byte matrix elements.
+matrix.sps:20.51-20.52: error: WRITE: Format A9 is too wide for 8-byte matrix
+elements.
+   20 | WRITE 1/FIELD=1 TO 10/OUTFILE='matrix.txt'/FORMAT=A9.
+      |                                                   ^~
 
 matrix.sps:21.7-21.11: error: MATRIX: WRITE with MODE=TRIANGULAR requires a
 square matrix but the matrix to be written has dimensions 1×2.
@@ -3697,38 +3832,64 @@ GET x/VARIABLES=a.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:12.5: error: GET: Syntax error at `!': expecting identifier.
+matrix.sps:12.5: error: GET: Syntax error expecting identifier.
+   12 | GET !.
+      |     ^
 
-matrix.sps:13.17: error: GET: Syntax error at `!': expecting variable name.
+matrix.sps:13.17: error: GET: Syntax error expecting variable name.
+   13 | GET x/VARIABLES=!.
+      |                 ^
 
-matrix.sps:14.12: error: GET: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:14.12: error: GET: Syntax error expecting a file name or handle
+name.
+   14 | GET x/FILE=!.
+      |            ^
 
-matrix.sps:15.16: error: GET: Syntax error at `!': expecting string.
+matrix.sps:15.16: error: GET: Syntax error expecting string.
+   15 | GET x/ENCODING=!.
+      |                ^
 
-matrix.sps:16.13: error: GET: Syntax error at `!': expecting identifier.
+matrix.sps:16.13: error: GET: Syntax error expecting identifier.
+   16 | GET x/NAMES=!.
+      |             ^
 
-matrix.sps:17.15: error: GET: Syntax error at `!'.
+matrix.sps:17.15: error: GET: Syntax error.
+   17 | GET x/MISSING=!.
+      |               ^
 
-matrix.sps:18.14: error: GET: Syntax error at `!'.
+matrix.sps:18.14: error: GET: Syntax error.
+   18 | GET x/SYSMIS=!.
+      |              ^
 
-matrix.sps:19.7: error: GET: Syntax error at `!': expecting FILE, VARIABLES,
-NAMES, MISSING, or SYSMIS.
+matrix.sps:19.7: error: GET: Syntax error expecting FILE, VARIABLES, NAMES,
+MISSING, or SYSMIS.
+   19 | GET x/!.
+      |       ^
 
-matrix.sps:20.17: error: GET: Syntax error at `!': expecting variable name.
+matrix.sps:20.17: error: GET: Syntax error expecting variable name.
+   20 | GET x/VARIABLES=!.
+      |                 ^
 
-matrix.sps:21.22: error: GET: Syntax error at `!': expecting variable name.
+matrix.sps:21.22: error: GET: Syntax error expecting variable name.
+   21 | GET x/VARIABLES=x TO !.
+      |                      ^
 
-matrix.sps:22: error: MATRIX: x is not a variable name.
+matrix.sps:22.17: error: MATRIX: x is not a variable name.
+   22 | GET x/VARIABLES=x.
+      |                 ^
 
-matrix.sps:23: error: MATRIX: c TO a is not valid syntax since c precedes a in
-the dictionary.
+matrix.sps:23.17-23.22: error: MATRIX: c TO a is not valid syntax since c
+precedes a in the dictionary.
+   23 | GET x/VARIABLES=c TO a.
+      |                 ^~~~~~
 
-matrix.sps:24: warning: MATRIX: d is not a numeric variable.
+matrix.sps:24.17: error: MATRIX: d is not a numeric variable.
+   24 | GET x/VARIABLES=d.
+      |                 ^
 
 matrix.sps:25: error: MATRIX: Variable d is not numeric.
 
-error: The GET command cannot read an empty active file.
+matrix.sps:30: error: MATRIX: The GET command cannot read an empty active file.
 ])
 AT_CLEANUP
 
@@ -3821,17 +3982,27 @@ SAVE {1,2}/OUTFILE='matrix5.sav'/STRINGS=a, b.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.6: error: SAVE: Syntax error at `!'.
+matrix.sps:2.6: error: SAVE: Syntax error.
+    2 | SAVE !.
+      |      ^
 
-matrix.sps:3.16: error: SAVE: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:3.16: error: SAVE: Syntax error expecting a file name or handle
+name.
+    3 | SAVE 1/OUTFILE=!.
+      |                ^
 
-matrix.sps:4.18: error: SAVE: Syntax error at `!': expecting variable name.
+matrix.sps:4.18: error: SAVE: Syntax error expecting variable name.
+    4 | SAVE 1/VARIABLES=!.
+      |                  ^
 
-matrix.sps:5.14: error: SAVE: Syntax error at `!'.
+matrix.sps:5.14: error: SAVE: Syntax error.
+    5 | SAVE 1/NAMES=!.
+      |              ^
 
-matrix.sps:6.8: error: SAVE: Syntax error at `!': expecting OUTFILE, VARIABLES,
-NAMES, or STRINGS.
+matrix.sps:6.8: error: SAVE: Syntax error expecting OUTFILE, VARIABLES, NAMES,
+or STRINGS.
+    6 | SAVE 1/!.
+      |        ^
 
 matrix.sps:7: error: SAVE: Required subcommand OUTFILE was not specified.
 
@@ -4185,18 +4356,28 @@ MGET TYPE=CORR !.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.6: error: MGET: Syntax error at `!': expecting FILE or TYPE.
+matrix.sps:2.6: error: MGET: Syntax error expecting FILE or TYPE.
+    2 | MGET !.
+      |      ^
 
-matrix.sps:3.11: error: MGET: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:3.11: error: MGET: Syntax error expecting a file name or handle
+name.
+    3 | MGET FILE=!.
+      |           ^
 
-matrix.sps:4.15: error: MGET: Syntax error at `!': expecting string.
+matrix.sps:4.15: error: MGET: Syntax error expecting string.
+    4 | MGET ENCODING=!.
+      |               ^
 
-matrix.sps:5.11: error: MGET: Syntax error at `!': expecting COV, CORR, MEAN,
-STDDEV, N, or COUNT.
+matrix.sps:5.11: error: MGET: Syntax error expecting COV, CORR, MEAN, STDDEV,
+N, or COUNT.
+    5 | MGET TYPE=!.
+      |           ^
 
-matrix.sps:6.16: error: MGET: Syntax error at `!': expecting COV, CORR, MEAN,
-STDDEV, N, or COUNT.
+matrix.sps:6.16: error: MGET: Syntax error expecting COV, CORR, MEAN, STDDEV,
+N, or COUNT.
+    6 | MGET TYPE=CORR !.
+      |                ^
 ])
 AT_CLEANUP
 
@@ -4554,52 +4735,108 @@ MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/VARIABLES=ROWTYPE_.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.7: error: MSAVE: Syntax error at `!'.
+matrix.sps:2.7: error: MSAVE: Syntax error.
+    2 | MSAVE !.
+      |       ^
 
-matrix.sps:3.14: error: MSAVE: Syntax error at `!': expecting COV, CORR, MEAN,
-STDDEV, N, or COUNT.
+matrix.sps:3.14: error: MSAVE: Syntax error expecting COV, CORR, MEAN, STDDEV,
+N, or COUNT.
+    3 | MSAVE 1/TYPE=!.
+      |              ^
 
-matrix.sps:4.17: error: MSAVE: Syntax error at `!': expecting a file name or
-handle name.
+matrix.sps:4.17: error: MSAVE: Syntax error expecting a file name or handle
+name.
+    4 | MSAVE 1/OUTFILE=!.
+      |                 ^
 
-matrix.sps:5.19: error: MSAVE: Syntax error at `!': expecting variable name.
+matrix.sps:5.19: error: MSAVE: Syntax error expecting variable name.
+    5 | MSAVE 1/VARIABLES=!.
+      |                   ^
 
-matrix.sps:6.16: error: MSAVE: Syntax error at `!': expecting variable name.
+matrix.sps:6.16: error: MSAVE: Syntax error expecting variable name.
+    6 | MSAVE 1/FNAMES=!.
+      |                ^
 
-matrix.sps:7.16: error: MSAVE: Syntax error at `!': expecting variable name.
+matrix.sps:7.16: error: MSAVE: Syntax error expecting variable name.
+    7 | MSAVE 1/SNAMES=!.
+      |                ^
 
-matrix.sps:8.15: error: MSAVE: Syntax error at `!'.
+matrix.sps:8.15: error: MSAVE: Syntax error.
+    8 | MSAVE 1/SPLIT=!.
+      |               ^
 
-matrix.sps:9.16: error: MSAVE: Syntax error at `!'.
+matrix.sps:9.16: error: MSAVE: Syntax error.
+    9 | MSAVE 1/FACTOR=!.
+      |                ^
 
-matrix.sps:10.9: error: MSAVE: Syntax error at `!': expecting TYPE, OUTFILE,
-VARIABLES, FNAMES, SNAMES, SPLIT, or FACTOR.
+matrix.sps:10.9: error: MSAVE: Syntax error expecting TYPE, OUTFILE, VARIABLES,
+FNAMES, SNAMES, SPLIT, or FACTOR.
+   10 | MSAVE 1/!.
+      |         ^
 
 matrix.sps:11: error: MSAVE: Required subcommand TYPE was not specified.
 
-matrix.sps:12: error: MSAVE: FNAMES requires FACTOR.
+matrix.sps:12.25: error: MSAVE: FNAMES requires FACTOR.
+   12 | MSAVE 1/TYPE=COV/FNAMES=x.
+      |                         ^
 
-matrix.sps:13: error: MSAVE: SNAMES requires SPLIT.
+matrix.sps:13.25: error: MSAVE: SNAMES requires SPLIT.
+   13 | MSAVE 1/TYPE=COV/SNAMES=x.
+      |                         ^
 
 matrix.sps:14: error: MSAVE: Required subcommand OUTFILE was not specified.
 
 matrix.sps:20: error: MSAVE: OUTFILE must name the same file on each MSAVE
 within a single MATRIX command.
 
-matrix.sps:21: error: MSAVE: VARIABLES must specify the same variables each
-time within a given MATRIX.
+matrix.sps:16.26-16.37: note: MSAVE: This is the OUTFILE on the first MSAVE
+command.
+   16 | MSAVE 1/TYPE=COV/OUTFILE='matrix.sav'
+      |                          ^~~~~~~~~~~~
+
+matrix.sps:20.26-20.38: note: MSAVE: This is the OUTFILE on a later MSAVE
+command.
+   20 | MSAVE 1/TYPE=COV/OUTFILE='matrix2.sav'.
+      |                          ^~~~~~~~~~~~~
 
-matrix.sps:16-19: note: MSAVE: This is the location of the first MSAVE command.
+matrix.sps:21: error: MSAVE: VARIABLES must specify the same variables on each
+MSAVE within a given MATRIX.
 
-matrix.sps:22: error: MSAVE: FNAMES must specify the same variables each time
-within a given MATRIX.
+matrix.sps:19.16: error: MSAVE: This is the specification of VARIABLES on the
+first MSAVE.
+   19 |     /VARIABLES=w.
+      |                ^
 
-matrix.sps:16-19: note: MSAVE: This is the location of the first MSAVE command.
+matrix.sps:21.28: error: MSAVE: This is a different specification of VARIABLES
+on a later MSAVE.
+   21 | MSAVE 1/TYPE=COV/VARIABLES=x.
+      |                            ^
 
-matrix.sps:23: error: MSAVE: SNAMES must specify the same variables each time
-within a given MATRIX.
+matrix.sps:22: error: MSAVE: FNAMES must specify the same variables on each
+MSAVE within a given MATRIX.
 
-matrix.sps:16-19: note: MSAVE: This is the location of the first MSAVE command.
+matrix.sps:17.23: error: MSAVE: This is the specification of FNAMES on the
+first MSAVE.
+   17 |     /FACTOR=1 /FNAMES=y
+      |                       ^
+
+matrix.sps:22.25: error: MSAVE: This is a different specification of FNAMES on
+a later MSAVE.
+   22 | MSAVE 1/TYPE=COV/FNAMES=x.
+      |                         ^
+
+matrix.sps:23: error: MSAVE: SNAMES must specify the same variables on each
+MSAVE within a given MATRIX.
+
+matrix.sps:18.22: error: MSAVE: This is the specification of SNAMES on the
+first MSAVE.
+   18 |     /SPLIT=2 /SNAMES=z
+      |                      ^
+
+matrix.sps:23.25: error: MSAVE: This is a different specification of SNAMES on
+a later MSAVE.
+   23 | MSAVE 1/TYPE=COV/SNAMES=x.
+      |                         ^
 
 matrix.sps:28.7-28.11: error: MATRIX: Matrix on MSAVE has 2 columns but there
 are 1 variables.
@@ -4630,25 +4867,46 @@ values were supplied.
    31 | MSAVE 0/TYPE=COV/FACTOR=1/SPLIT={1;2}.
       |                                 ^~~~~
 
-matrix.sps:35: error: MSAVE: Variable x appears twice in variable list.
+matrix.sps:35.49: error: MSAVE: Variable x appears twice in variable list.
+   35 | MSAVE 1/TYPE=COV/OUTFILE='matrix4.sav'/SNAMES=x,x/SPLIT=1.
+      |                                                 ^
 
-matrix.sps:39: error: MATRIX: Duplicate or invalid FACTOR variable name x.
+matrix.sps:39.56: error: MATRIX: Duplicate or invalid FACTOR variable name x.
+   39 | MSAVE 1/TYPE=COV/OUTFILE='matrix5.sav'/SNAMES=x/FNAMES=x/SPLIT=1/
+FACTOR=1.
+      |                                                        ^
 
-matrix.sps:43: error: MATRIX: Duplicate or invalid variable name x.
+matrix.sps:43.50: error: MATRIX: Duplicate or invalid variable name x.
+   43 | MSAVE 1/TYPE=COV/OUTFILE='matrix6.sav'/VARIABLES=x/FNAMES=x/FACTOR=1.
+      |                                                  ^
 
-matrix.sps:47: error: MATRIX: Duplicate or invalid variable name x.
+matrix.sps:47.50: error: MATRIX: Duplicate or invalid variable name x.
+   47 | MSAVE 1/TYPE=COV/OUTFILE='matrix6.sav'/VARIABLES=x/SNAMES=x/SPLIT=1.
+      |                                                  ^
 
-matrix.sps:51: error: MSAVE: Variable name VARNAME_ is reserved.
+matrix.sps:51.47-51.54: error: MSAVE: Variable name VARNAME_ is reserved.
+   51 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/SNAMES=VARNAME_.
+      |                                               ^~~~~~~~
 
-matrix.sps:52: error: MSAVE: Variable name ROWTYPE_ is reserved.
+matrix.sps:52.47-52.54: error: MSAVE: Variable name ROWTYPE_ is reserved.
+   52 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/SNAMES=ROWTYPE_.
+      |                                               ^~~~~~~~
 
-matrix.sps:53: error: MSAVE: Variable name VARNAME_ is reserved.
+matrix.sps:53.47-53.54: error: MSAVE: Variable name VARNAME_ is reserved.
+   53 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/FNAMES=VARNAME_.
+      |                                               ^~~~~~~~
 
-matrix.sps:54: error: MSAVE: Variable name ROWTYPE_ is reserved.
+matrix.sps:54.47-54.54: error: MSAVE: Variable name ROWTYPE_ is reserved.
+   54 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/FNAMES=ROWTYPE_.
+      |                                               ^~~~~~~~
 
-matrix.sps:55: error: MSAVE: Variable name VARNAME_ is reserved.
+matrix.sps:55.50-55.57: error: MSAVE: Variable name VARNAME_ is reserved.
+   55 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/VARIABLES=VARNAME_.
+      |                                                  ^~~~~~~~
 
-matrix.sps:56: error: MSAVE: Variable name ROWTYPE_ is reserved.
+matrix.sps:56.50-56.57: error: MSAVE: Variable name ROWTYPE_ is reserved.
+   56 | MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/VARIABLES=ROWTYPE_.
+      |                                                  ^~~~~~~~
 ])
 AT_CLEANUP
 
@@ -4683,8 +4941,9 @@ DISPLAY !.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.9: error: DISPLAY: Syntax error at `!': expecting DICTIONARY or
-STATUS.
+matrix.sps:2.9: error: DISPLAY: Syntax error expecting DICTIONARY or STATUS.
+    2 | DISPLAY !.
+      |         ^
 ])
 AT_CLEANUP
 
@@ -4722,12 +4981,20 @@ RELEASE x.
 END MATRIX.
 ])
 AT_CHECK([pspp matrix.sps], [1], [dnl
-matrix.sps:2.9: error: RELEASE: Syntax error at `!': expecting end of command.
+matrix.sps:2.9: error: RELEASE: Syntax error expecting end of command.
+    2 | RELEASE !.
+      |         ^
 
-matrix.sps:3.9: error: RELEASE: Syntax error at `x': Variable name expected.
+matrix.sps:3.9: error: RELEASE: Syntax error expecting variable name.
+    3 | RELEASE x.
+      |         ^
 
-matrix.sps:5.12: error: RELEASE: Syntax error at `!': expecting end of command.
+matrix.sps:5.12: error: RELEASE: Syntax error expecting end of command.
+    5 | RELEASE x, !.
+      |            ^
 
-matrix.sps:7.11: error: RELEASE: Syntax error at `y': expecting end of command.
+matrix.sps:7.11: error: RELEASE: Syntax error expecting end of command.
+    7 | RELEASE x y.
+      |           ^
 ])
 AT_CLEANUP
\ No newline at end of file
index f3d0d24283e39d355c72fd5e79db4a54faf61439..62d9facf32dc705a1001505d785f2265733b2ac4 100644 (file)
@@ -270,7 +270,9 @@ end data.
 QUICK CLUSTER x y /UNSUPPORTED.
 ])
 AT_CHECK([pspp -O format=csv quick-cluster.sps], [1], [dnl
-quick-cluster.sps:7.20-7.30: error: QUICK CLUSTER: Syntax error at `UNSUPPORTED'.
+"quick-cluster.sps:7.20-7.30: error: QUICK CLUSTER: Syntax error.
+    7 | QUICK CLUSTER x y /UNSUPPORTED.
+      |                    ^~~~~~~~~~~"
 ])
 AT_CLEANUP
 
index bd3f8becb478495b37e66574fe2db0db7f2d10c7..3351741f1bc640b7c29dc68856f8adf75c4dc7a6 100644 (file)
@@ -595,12 +595,20 @@ RANK x
  /RANK INTO foo  bar wiz.
 ])
 AT_CHECK([pspp -O format=csv rank.sps], [1], [dnl
-rank.sps:15.1: error: RANK: Syntax error at end of command: expecting `@{:@'.
+"rank.sps:15.1: error: RANK: Syntax error expecting `@{:@'.
+   15 | .
+      | ^"
 
-rank.sps:19.11: error: RANK: Syntax error at `d': Expected positive integer for NTILES.
+"rank.sps:19.11: error: RANK: Syntax error expecting positive integer for NTILES.
+   19 |   /NTILES(d)
+      |           ^"
 
-rank.sps:25: error: RANK: Variable x already exists.
+"rank.sps:25.13: error: RANK: Variable x already exists.
+   25 |  /RANK INTO x.
+      |             ^"
 
-rank.sps:30: error: RANK: Too many variables in INTO clause.
+"rank.sps:30.18-30.20: error: RANK: Too many variables in INTO clause.
+   30 |  /RANK INTO foo  bar wiz.
+      |                  ^~~"
 ])
 AT_CLEANUP
index f4977f359a1b91a9477a9903cd65e8b8e610a175..686c4edd6a92a61f46fb08735a42f10216aaceb1 100644 (file)
@@ -38,7 +38,9 @@ list.
 ])
 
 AT_CHECK([pspp -O format=csv regression.sps], [0], [dnl
-regression.sps:16: warning: REGRESSION: REGRESSION with SAVE ignores FILTER.  All cases will be processed.
+"regression.sps:16.82-16.96: warning: REGRESSION: REGRESSION with SAVE ignores FILTER.  All cases will be processed.
+   16 | regression /variables=v0 v1 v2 /statistics defaults /dependent=v2 /method=enter /save=pred resid.
+      |                                                                                  ^~~~~~~~~~~~~~~"
 
 Table: Model Summary (v2)
 R,R Square,Adjusted R Square,Std. Error of the Estimate
index 77cd27620d81bc903b318df61af5f09cc96dd0c3..6897195084eb1fa4eef294a107d64670df2ce5c4 100644 (file)
@@ -199,8 +199,10 @@ RELIABILITY
   /VARIABLES=var6 var8 var15 var17
   .
 ])
-AT_CHECK([pspp -o pspp.csv -o pspp.txt reliability.sps], [0],
-  [reliability.sps:174: warning: RELIABILITY: The STATISTICS subcommand is not yet implemented.  No statistics will be produced.
+AT_CHECK([pspp -o pspp.csv -o pspp.txt reliability.sps], [0], [dnl
+reliability.sps:174.4-174.40: warning: RELIABILITY: The STATISTICS subcommand is not yet implemented.  No statistics will be produced.
+  174 |   /STATISTICS = DESCRIPTIVES COVARIANCES
+      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ])
 AT_CHECK([cat pspp.csv], [0], [dnl
 Scale: Everything
@@ -234,7 +236,9 @@ Spearman-Brown Coefficient,Equal Length,,.75
 ,Unequal Length,,.75
 Guttman Split-Half Coefficient,,,.75
 
-reliability.sps:174: warning: RELIABILITY: The STATISTICS subcommand is not yet implemented.  No statistics will be produced.
+"reliability.sps:174.4-174.40: warning: RELIABILITY: The STATISTICS subcommand is not yet implemented.  No statistics will be produced.
+  174 |   /STATISTICS = DESCRIPTIVES COVARIANCES
+      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
 
 Scale: Totals
 
index c1e96e557eaad96f72c97582561c4334dccffff8..7b36fb4aaeb3f21dec552c52b40924aa9cf1a21c 100644 (file)
@@ -805,7 +805,9 @@ end data.
 t-test /GROUPS=indep('a') /var=dep1 dep2.
 ])
 AT_CHECK([pspp -O format=csv t-test.sps], [1], [dnl
-"t-test.sps:17: error: T-TEST: When applying GROUPS to a string variable, two values must be specified."
+"t-test.sps:17.16-17.25: error: T-TEST: When applying GROUPS to a string variable, two values must be specified.
+   17 | t-test /GROUPS=indep('a') /var=dep1 dep2.
+      |                ^~~~~~~~~~"
 ])
 AT_CLEANUP
 
index 53aac78d8a56932b6f5c184551df4c611168d402..a460e4977fcb91e12612c2c282e388a7208f28f3 100644 (file)
@@ -138,6 +138,8 @@ SET SAFER=ON.
 HOST COMMAND=[['sleep 10']] TIMELIMIT=0.1.
 ])
 AT_CHECK([pspp -O format=csv host.sps], [1], [dnl
-host.sps:2: error: HOST: This command not allowed when the SAFER option is set.
+"host.sps:2.1-2.4: error: HOST: This command not allowed when the SAFER option is set.
+    2 | HOST COMMAND=[['sleep 10']] TIMELIMIT=0.1.
+      | ^~~~"
 ])
 AT_CLEANUP
index 8867ac28c19e6e06b9cfe231a8666eeaab6cb0cd..91485cbaf6387318af81498694c4ba4d5a9051e6 100644 (file)
@@ -37,10 +37,18 @@ INSERT
 LIST.
 ])
 AT_CHECK([pspp -o pspp.csv insert.sps], [1], [dnl
-batch.sps:2.1-2.4: error: INPUT PROGRAM: Syntax error at `loop': expecting end of command.
-batch.sps:3: error: COMPUTE: COMPUTE is allowed only after the active dataset has been defined or inside INPUT PROGRAM.
-batch.sps:4: error: END CASE: END CASE is allowed only inside INPUT PROGRAM.
-insert.sps:4: error: LIST: LIST is allowed only after the active dataset has been defined.
+batch.sps:2.1-2.4: error: INPUT PROGRAM: Syntax error expecting end of command.
+    2 | loop #i = 1 to 5
+      | ^~~~
+batch.sps:3.4-3.10: error: COMPUTE: COMPUTE is allowed only after the active dataset has been defined or inside INPUT PROGRAM.
+    3 | +  compute z = #i
+      |    ^~~~~~~
+batch.sps:4.4-4.11: error: END CASE: END CASE is allowed only inside INPUT PROGRAM.
+    4 | +  end case
+      |    ^~~~~~~~
+insert.sps:4.1-4.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+    4 | LIST.
+      | ^~~~
 ])
 AT_CLEANUP
 
@@ -81,7 +89,9 @@ END DATA.
 ])
 AT_CHECK([pspp -o pspp.csv insert.sps], [1], [dnl
 Dir1/foo.sps:1: error: INSERT: Can't find `bar.sps' in include file search path.
-insert.sps:2: error: LIST: LIST is allowed only after the active dataset has been defined.
+insert.sps:2.1-2.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+    2 | LIST.
+      | ^~~~
 ])
 AT_CLEANUP
 
@@ -134,7 +144,9 @@ CREATE_ERROR_SPS
 AT_DATA([insert.sps], [INSERT FILE='error.sps' ERROR=STOP.
 ])
 AT_CHECK([pspp -o pspp.csv insert.sps], [1], [dnl
-error.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+error.sps:10.9-10.13: error: DISPLAY: AKSDJ is not a variable name.
+   10 | DISPLAY AKSDJ.
+      |         ^~~~~
 warning: Error encountered while ERROR=STOP is effective.
 ])
 AT_CLEANUP
@@ -144,10 +156,14 @@ CREATE_ERROR_SPS
 AT_DATA([insert.sps], [INSERT FILE='error.sps' ERROR=CONTINUE.
 ])
 AT_CHECK([pspp -o pspp.csv insert.sps], [1], [dnl
-error.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+error.sps:10.9-10.13: error: DISPLAY: AKSDJ is not a variable name.
+   10 | DISPLAY AKSDJ.
+      |         ^~~~~
 ])
 AT_CHECK([cat pspp.csv], [0], [dnl
-error.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+"error.sps:10.9-10.13: error: DISPLAY: AKSDJ is not a variable name.
+   10 | DISPLAY AKSDJ.
+      |         ^~~~~"
 
 Table: Data List
 x
@@ -171,7 +187,9 @@ LIST.
 AT_CHECK([pspp -O format=csv insert.sps], [1], [dnl
 insert.sps:2: error: INSERT: Can't find `nonexistent' in include file search path.
 
-insert.sps:6: error: LIST: LIST is allowed only after the active dataset has been defined.
+"insert.sps:6.1-6.4: error: LIST: LIST is allowed only after the active dataset has been defined.
+    6 | LIST.
+      | ^~~~"
 ])
 AT_CLEANUP
 
index 6d2baa20b898a368d54e9da8f18d3c573a353360..23a1f1298e5b96ef67e70abcf0a2e28a3e17dfd6 100644 (file)
@@ -47,6 +47,8 @@ AT_DATA([pe.sps], [[PERMI|SIONS /FILE='foobar' PERMISSIONS=WRITEABLE.
 ]])
 
 AT_CHECK([pspp -O format=csv pe.sps], [1], [dnl
-pe.sps:1.6: error: PERMISSIONS: Syntax error at `|': expecting STRING.
+"pe.sps:1.6: error: PERMISSIONS: Syntax error expecting STRING.
+    1 | PERMI|SIONS /FILE='foobar' PERMISSIONS=WRITEABLE.
+      |      ^"
 ])
 AT_CLEANUP
index 1ca75106675c500be63423800c99531e8b970457..ce000d65fbe1ae8dee620cb07d6555623f877e14 100644 (file)
@@ -615,8 +615,10 @@ END DATA.
 REGRESSION
 /VARIABLES= a
 
-ascii.sps:11: warning: REGRESSION: a is not a numeric variable.  It will not be
-included in the variable list.
+ascii.sps:10.13: warning: REGRESSION: a is not a numeric variable.  It will not
+be included in the variable list.
+   10 | /VARIABLES= a
+      |             ^
 
 /DEPENDENT= x y
 /STATISTICS=COEFF R ANOVA.
index 7aef458d2479fa4f44451f9b3af67e3fd69357d0..67edc0481fa8d8d99c0df7fb563f794eb9b4a2e4 100644 (file)
@@ -661,7 +661,7 @@ read_value_option (struct lexer *lexer, const struct pivot_table *pt,
       return;
     }
 
-  lex_error (lexer, "Expecting valid value option");
+  lex_error (lexer, "Syntax error expecting valid value option.");
   exit (1);
 }
 
@@ -826,7 +826,7 @@ read_stroke (struct lexer *lexer)
     if (lex_match_id (lexer, table_stroke_to_string (stroke)))
       return stroke;
 
-  lex_error (lexer, "expecting stroke");
+  lex_error (lexer, "Syntax error expecting stroke.");
   exit (1);
 }
 
index 99a254ba44972f3814afa0534b53f0fc4402cc1e..a381a98746e6c95442f45cd42f28bb60337c0096 100644 (file)
@@ -80,7 +80,7 @@ parse_variables_option (const char *arg, struct dictionary *dict,
   bool ok = parse_variables (lexer, dict, vars, n_vars, 0);
   if (ok && (lex_token (lexer) != T_STOP && lex_token (lexer) != T_ENDCMD))
     {
-      lex_error (lexer, _("expecting variable name"));
+      lex_error (lexer, _("Syntax error expecting variable name."));
       ok = false;
     }