Improve error messages for format specifiers.
[pspp] / src / language / data-io / print.c
index 5c9ef7af993e903c0b0ebf18781e7379b525611f..91d2d9c180c85748b0b71889e6b468a7ac15a244 100644 (file)
@@ -96,13 +96,16 @@ enum which_formats
     WRITE
   };
 
+static const struct trns_class print_binary_trns_class;
+static const struct trns_class print_text_trns_class;
+
 static int internal_cmd_print (struct lexer *, struct dataset *ds,
                               enum which_formats, bool eject);
-static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
-static trns_free_func print_trns_free;
 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
                         struct dictionary *dict, enum which_formats);
 static void dump_table (struct print_trns *);
+
+static bool print_trns_free (void *trns_);
 \f
 /* Basic parsing. */
 
@@ -187,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;
        }
     }
@@ -239,11 +243,9 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
     dump_table (trns);
 
   /* Put the transformation in the queue. */
-  add_transformation (ds,
-                      (binary
-                       ? print_binary_trns_proc
-                       : print_text_trns_proc),
-                      print_trns_free, trns);
+  add_transformation (ds, (binary
+                           ? &print_binary_trns_class
+                           : &print_text_trns_class), trns);
 
   pool_destroy (tmp_pool);
   fh_unref (fh);
@@ -354,15 +356,15 @@ parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
                          enum which_formats which_formats)
 {
   const struct variable **vars;
-  size_t n_vars, var_idx;
-  struct fmt_spec *formats, *f;
-  size_t n_formats;
-  bool add_space;
-
+  size_t n_vars;
   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
-                            &vars, &n_vars, PV_DUPLICATE))
+                                   &vars, &n_vars, PV_DUPLICATE))
     return false;
 
+  struct fmt_spec *formats, *f;
+  size_t n_formats;
+  bool add_space;
+  int formats_start = lex_ofs (lexer);
   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
     {
       if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
@@ -372,13 +374,11 @@ parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
     }
   else
     {
-      size_t i;
-
       lex_match (lexer, T_ASTERISK);
 
       formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
       n_formats = n_vars;
-      for (i = 0; i < n_vars; i++)
+      for (size_t i = 0; i < n_vars; i++)
         {
           const struct variable *v = vars[i];
           formats[i] = (which_formats == PRINT
@@ -387,35 +387,40 @@ parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
         }
       add_space = which_formats == PRINT;
     }
+  int formats_end = lex_ofs (lexer) - 1;
 
-  var_idx = 0;
+  size_t var_idx = 0;
   for (f = formats; f < &formats[n_formats]; f++)
     if (!execute_placement_format (f, record, column))
       {
-        const struct variable *var;
-        struct prt_out_spec *spec;
-
-        var = vars[var_idx++];
-        if (!fmt_check_width_compat (f, var_get_width (var)))
-          return false;
-
-        spec = pool_alloc (trns->pool, sizeof *spec);
-        spec->type = PRT_VAR;
-        spec->record = *record;
-        spec->first_column = *column;
-        spec->var = var;
-        spec->format = *f;
-        spec->add_space = add_space;
-
-        /* This is a completely bizarre twist for compatibility:
-           WRITE outputs the system-missing value as a field
-           filled with spaces, instead of using the normal format
-           that usually contains a period. */
-        spec->sysmis_as_spaces = (which_formats == WRITE
-                                  && var_is_numeric (var)
-                                  && (fmt_get_category (spec->format.type)
-                                      != FMT_CAT_BINARY));
-
+        const struct variable *var = vars[var_idx++];
+        char *error = fmt_check_width_compat__ (f, var_get_name (var),
+                                                var_get_width (var));
+        if (error)
+          {
+            lex_ofs_error (lexer, formats_start, formats_end, "%s", error);
+            free (error);
+            return false;
+          }
+
+        struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
+        *spec = (struct prt_out_spec) {
+          .type = PRT_VAR,
+          .record = *record,
+          .first_column = *column,
+          .var = var,
+          .format = *f,
+          .add_space = add_space,
+
+          /* This is a completely bizarre twist for compatibility: WRITE
+             outputs the system-missing value as a field filled with spaces,
+             instead of using the normal format that usually contains a
+             period. */
+          .sysmis_as_spaces = (which_formats == WRITE
+                               && var_is_numeric (var)
+                               && (fmt_get_category (f->type)
+                                   != FMT_CAT_BINARY)),
+        };
         ll_push_tail (&trns->specs, &spec->ll);
 
         *column += f->w + add_space;
@@ -474,7 +479,7 @@ static void print_text_flush_records (struct print_trns *, struct u8_line *,
                                       bool *eject, int *record);
 
 /* Performs the transformation inside print_trns T on case C. */
-static int
+static enum trns_result
 print_text_trns_proc (void *trns_, struct ccase **c,
                       casenumber case_num UNUSED)
 {
@@ -587,7 +592,7 @@ static void print_binary_flush_records (struct print_trns *,
                                         bool *eject, int *record);
 
 /* Performs the transformation inside print_trns T on case C. */
-static int
+static enum trns_result
 print_binary_trns_proc (void *trns_, struct ccase **c,
                         casenumber case_num UNUSED)
 {
@@ -685,3 +690,15 @@ print_trns_free (void *trns_)
   return ok;
 }
 
+static const struct trns_class print_binary_trns_class = {
+  .name = "PRINT",
+  .execute = print_binary_trns_proc,
+  .destroy = print_trns_free,
+};
+
+static const struct trns_class print_text_trns_class = {
+  .name = "PRINT",
+  .execute = print_text_trns_proc,
+  .destroy = print_trns_free,
+};
+