DATA LIST: Improve error messages and coding style.
[pspp] / src / language / data-io / matrix-data.c
index 31d6b710b0ba68a54402d33e50e559b9f581bf2e..83490d4db824fe67756d7c1fc824971bcb815003 100644 (file)
@@ -253,12 +253,13 @@ parse_msg (struct dfm_reader *reader, const struct substring *token,
 
   int line_number = dfm_get_line_number (reader);
   struct msg_location *location = xmalloc (sizeof *location);
+  int last_column = (first_column && token->length
+                     ? first_column + token->length - 1
+                     : 0);
   *location = (struct msg_location) {
     .file_name = intern_new (dfm_get_file_name (reader)),
-    .first_line = line_number,
-    .last_line = line_number + 1,
-    .first_column = first_column,
-    .last_column = first_column ? first_column + token->length : 0,
+    .start = { .line = line_number, .column = first_column },
+    .end = { .line = line_number, .column = last_column },
   };
   struct msg *m = xmalloc (sizeof *m);
   *m = (struct msg) {
@@ -836,17 +837,22 @@ parse_matrix_data_variables (struct lexer *lexer)
 
   size_t n_names = 0;
   char **names = NULL;
+  int vars_start = lex_ofs (lexer);
   if (!parse_DATA_LIST_vars (lexer, dict, &names, &n_names, PV_NO_DUPLICATE))
     {
       dict_unref (dict);
       return NULL;
     }
+  int vars_end = lex_ofs (lexer) - 1;
 
   for (size_t i = 0; i < n_names; i++)
     if (!strcasecmp (names[i], "ROWTYPE_"))
       dict_create_var_assert (dict, "ROWTYPE_", 8);
     else
-      dict_create_var_assert (dict, names[i], 0);
+      {
+        struct variable *var = dict_create_var_assert (dict, names[i], 0);
+        var_set_measure (var, MEASURE_SCALE);
+      }
 
   for (size_t i = 0; i < n_names; ++i)
     free (names[i]);
@@ -854,7 +860,8 @@ parse_matrix_data_variables (struct lexer *lexer)
 
   if (dict_lookup_var (dict, "VARNAME_"))
     {
-      msg (SE, _("VARIABLES may not include VARNAME_."));
+      lex_ofs_error (lexer, vars_start, vars_end,
+                     _("VARIABLES may not include VARNAME_."));
       dict_unref (dict);
       return NULL;
     }
@@ -867,8 +874,10 @@ parse_matrix_data_subvars (struct lexer *lexer, struct dictionary *dict,
                            struct variable ***vars, size_t **indexes,
                            size_t *n_vars)
 {
+  int start_ofs = lex_ofs (lexer);
   if (!parse_variables (lexer, dict, vars, n_vars, 0))
     return false;
+  int end_ofs = lex_ofs (lexer) - 1;
 
   *indexes = xnmalloc (*n_vars, sizeof **indexes);
   for (size_t i = 0; i < *n_vars; i++)
@@ -876,7 +885,8 @@ parse_matrix_data_subvars (struct lexer *lexer, struct dictionary *dict,
       struct variable *v = (*vars)[i];
       if (!strcasecmp (var_get_name (v), "ROWTYPE_"))
         {
-          msg (SE, _("ROWTYPE_ is not allowed on SPLIT or FACTORS."));
+          lex_ofs_error (lexer, start_ofs, end_ofs,
+                         _("ROWTYPE_ is not allowed on SPLIT or FACTORS."));
           goto error;
         }
       (*indexes)[i] = var_get_dict_index (v);
@@ -884,12 +894,14 @@ parse_matrix_data_subvars (struct lexer *lexer, struct dictionary *dict,
       bool *tv = &taken_vars[var_get_dict_index (v)];
       if (*tv)
         {
-          msg (SE, _("%s may not appear on both SPLIT and FACTORS."),
-               var_get_name (v));
+          lex_ofs_error (lexer, start_ofs, end_ofs,
+                         _("%s may not appear on both SPLIT and FACTORS."),
+                         var_get_name (v));
           goto error;
         }
       *tv = true;
 
+      var_set_measure (v, MEASURE_NOMINAL);
       var_set_both_formats (v, &(struct fmt_spec) { .type = FMT_F, .w = 4 });
     }
   return true;
@@ -906,11 +918,13 @@ error:
 int
 cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
 {
+  int input_vars_start = lex_ofs (lexer);
   struct dictionary *dict = parse_matrix_data_variables (lexer);
   if (!dict)
     return CMD_FAILURE;
+  int input_vars_end = lex_ofs (lexer) - 1;
 
-  size_t n_input_vars = dict_get_var_cnt (dict);
+  size_t n_input_vars = dict_get_n_vars (dict);
   struct variable **input_vars = xnmalloc (n_input_vars, sizeof *input_vars);
   for (size_t i = 0; i < n_input_vars; i++)
     input_vars[i] = dict_get_var (dict, i);
@@ -946,6 +960,8 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
     taken_vars[var_get_dict_index (rowtype)] = true;
 
   struct file_handle *fh = NULL;
+  int n_start = 0;
+  int n_end = 0;
   while (lex_token (lexer) != T_ENDCMD)
     {
       if (!lex_force_match (lexer, T_SLASH))
@@ -953,16 +969,19 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
 
       if (lex_match_id (lexer, "N"))
        {
+          n_start = lex_ofs (lexer) - 1;
          lex_match (lexer, T_EQUALS);
 
          if (!lex_force_int_range (lexer, "N", 0, INT_MAX))
            goto error;
 
          mf.n = lex_integer (lexer);
+          n_end = lex_ofs (lexer);
          lex_get (lexer);
        }
       else if (lex_match_id (lexer, "FORMAT"))
        {
+          int start_ofs = lex_ofs (lexer) - 1;
          lex_match (lexer, T_EQUALS);
 
          while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
@@ -987,6 +1006,15 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
                  goto error;
                }
            }
+          int end_ofs = lex_ofs (lexer) - 1;
+
+          if (mf.diagonal == NO_DIAGONAL && mf.triangle == FULL)
+            {
+              lex_ofs_error (lexer, start_ofs, end_ofs,
+                             _("FORMAT=FULL and FORMAT=NODIAGONAL are "
+                               "mutually exclusive."));
+              goto error;
+            }
        }
       else if (lex_match_id (lexer, "FILE"))
        {
@@ -1006,6 +1034,7 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
               mf.svars = xmalloc (sizeof *mf.svars);
               mf.svars[0] = dict_create_var_assert (dict, lex_tokcstr (lexer),
                                                     0);
+              var_set_measure (mf.svars[0], MEASURE_NOMINAL);
               var_set_both_formats (
                 mf.svars[0], &(struct fmt_spec) { .type = FMT_F, .w = 4 });
               mf.n_svars = 1;
@@ -1027,7 +1056,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);
 
@@ -1052,7 +1083,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;
@@ -1084,11 +1122,6 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
          goto error;
        }
     }
-  if (mf.diagonal == NO_DIAGONAL && mf.triangle == FULL)
-    {
-      msg (SE, _("FORMAT=FULL and FORMAT=NODIAGONAL are mutually exclusive."));
-      goto error;
-    }
   if (!mf.input_rowtype)
     {
       if (mf.cells < 0)
@@ -1123,7 +1156,8 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
       }
   if (!mf.n_cvars)
     {
-      msg (SE, _("At least one continuous variable is required."));
+      lex_ofs_error (lexer, input_vars_start, input_vars_end,
+                     _("At least one continuous variable is required."));
       goto error;
     }
   if (mf.input_rowtype)
@@ -1131,19 +1165,22 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
       for (size_t i = 0; i < mf.n_cvars; i++)
         if (mf.cvars[i] != input_vars[n_input_vars - mf.n_cvars + i])
           {
-            msg (SE, _("VARIABLES includes ROWTYPE_ but the continuous "
-                       "variables are not the last ones on VARIABLES."));
+            lex_ofs_error (lexer, input_vars_start, input_vars_end,
+                           _("VARIABLES includes ROWTYPE_ but the continuous "
+                             "variables are not the last ones on VARIABLES."));
             goto error;
           }
     }
   unsigned int rowtype_mask = mf.pooled_rowtype_mask | mf.factor_rowtype_mask;
   if (rowtype_mask & (1u << C_N) && mf.n >= 0)
     {
-      msg (SE, _("Cannot specify N on CONTENTS along with the N subcommand."));
+      lex_ofs_error (lexer, n_start, n_end,
+                     _("Cannot specify N on CONTENTS along with the "
+                       "N subcommand."));
       goto error;
     }
 
-  struct variable **order = xnmalloc (dict_get_var_cnt (dict), sizeof *order);
+  struct variable **order = xnmalloc (dict_get_n_vars (dict), sizeof *order);
   size_t n_order = 0;
   for (size_t i = 0; i < mf.n_svars; i++)
     order[n_order++] = mf.svars[i];
@@ -1153,11 +1190,11 @@ cmd_matrix_data (struct lexer *lexer, struct dataset *ds)
   order[n_order++] = mf.varname;
   for (size_t i = 0; i < mf.n_cvars; i++)
     order[n_order++] = mf.cvars[i];
-  assert (n_order == dict_get_var_cnt (dict));
+  assert (n_order == dict_get_n_vars (dict));
   dict_reorder_vars (dict, order, n_order);
   free (order);
 
-  dict_set_split_vars (dict, mf.svars, mf.n_svars);
+  dict_set_split_vars (dict, mf.svars, mf.n_svars, SPLIT_LAYERED);
 
   schedule_matrices (&mf);