From 8011f9998ed6cc3cd8948d080a1b7e36d932e5c5 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 12 Sep 2022 19:19:38 -0700 Subject: [PATCH] SAVE TRANSLATE: Improve error messages and implementation of RENAME. --- src/language/data-io/combine-files.c | 2 +- src/language/data-io/get.c | 2 +- src/language/data-io/save-translate.c | 69 +++--- src/language/data-io/save.c | 2 +- src/language/data-io/trim.c | 279 ++++++----------------- src/language/data-io/trim.h | 4 +- src/language/lexer/variable-parser.h | 20 +- tests/language/data-io/save-translate.at | 208 ++++++++++++++++- 8 files changed, 308 insertions(+), 278 deletions(-) diff --git a/src/language/data-io/combine-files.c b/src/language/data-io/combine-files.c index b95683ef8c..dde261a68f 100644 --- a/src/language/data-io/combine-files.c +++ b/src/language/data-io/combine-files.c @@ -234,7 +234,7 @@ combine_files (enum comb_command_type command, while (lex_match (lexer, T_SLASH)) if (lex_match_id (lexer, "RENAME")) { - if (!parse_dict_rename (lexer, file->dict, false)) + if (!parse_dict_rename (lexer, file->dict)) goto error; } else if (lex_match_id (lexer, "IN")) diff --git a/src/language/data-io/get.c b/src/language/data-io/get.c index cb1789aa37..5650b4174c 100644 --- a/src/language/data-io/get.c +++ b/src/language/data-io/get.c @@ -139,7 +139,7 @@ parse_read_command (struct lexer *lexer, struct dataset *ds, while (lex_token (lexer) != T_ENDCMD) { lex_match (lexer, T_SLASH); - if (!parse_dict_trim (lexer, dict, false)) + if (!parse_dict_trim (lexer, dict)) goto error; } dict_compact_values (dict); diff --git a/src/language/data-io/save-translate.c b/src/language/data-io/save-translate.c index c157701e6c..29ad78ac21 100644 --- a/src/language/data-io/save-translate.c +++ b/src/language/data-io/save-translate.c @@ -41,48 +41,29 @@ int cmd_save_translate (struct lexer *lexer, struct dataset *ds) { - enum { CSV_FILE = 1, TAB_FILE } type; + enum { CSV_FILE = 1, TAB_FILE } type = 0; - struct dictionary *dict; - struct case_map_stage *stage; - struct case_map *map; - struct casewriter *writer; - struct file_handle *handle; - - bool replace; - - bool retain_unselected; - bool recode_user_missing; - bool include_var_names; - bool use_value_labels; - bool use_print_formats; - char decimal; - char delimiter; - char qualifier; - - bool ok; + struct dictionary *dict = dict_clone (dataset_dict (ds)); + dict_set_names_must_be_ids (dict, false); - type = 0; + struct case_map_stage *stage = case_map_stage_create (dict); + dict_delete_scratch_vars (dict); - dict = dict_clone (dataset_dict (ds)); - dict_set_names_must_be_ids (dict, false); - stage = NULL; - map = NULL; + struct file_handle *handle = NULL; - handle = NULL; - replace = false; + bool replace = false; - retain_unselected = true; - recode_user_missing = false; - include_var_names = false; - use_value_labels = false; - use_print_formats = false; - decimal = settings_get_fmt_settings ()->decimal; - delimiter = 0; - qualifier = '"'; + bool retain_unselected = true; + bool recode_user_missing = false; + bool include_var_names = false; + bool use_value_labels = false; + bool use_print_formats = false; + char decimal = settings_get_fmt_settings ()->decimal; + char delimiter = 0; + char qualifier = '"'; - stage = case_map_stage_create (dict); - dict_delete_scratch_vars (dict); + int outfile_start = 0; + int outfile_end = 0; while (lex_token (lexer) != T_ENDCMD) { @@ -91,6 +72,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds) if (lex_match_id (lexer, "OUTFILE")) { + outfile_start = lex_ofs (lexer) - 1; if (handle != NULL) { lex_sbc_only_once (lexer, "OUTFILE"); @@ -102,6 +84,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds) handle = fh_parse (lexer, FH_REF_FILE, NULL); if (handle == NULL) goto error; + outfile_end = lex_ofs (lexer) - 1; } else if (lex_match_id (lexer, "TYPE")) { @@ -230,7 +213,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds) goto error; } } - else if (!parse_dict_trim (lexer, dict, true)) + else if (!parse_dict_trim (lexer, dict)) goto error; } @@ -246,8 +229,9 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds) } else if (!replace && fn_exists (handle)) { - msg (SE, _("Output file `%s' exists but %s was not specified."), - fh_get_file_name (handle), "REPLACE"); + lex_ofs_error (lexer, outfile_start, outfile_end, + _("Output file `%s' exists but %s was not specified."), + fh_get_file_name (handle), "REPLACE"); goto error; } @@ -266,19 +250,19 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds) : ';'), .qualifier = qualifier, }; - writer = csv_writer_open (handle, dict, &csv_opts); + struct casewriter *writer = csv_writer_open (handle, dict, &csv_opts); if (writer == NULL) goto error; fh_unref (handle); - map = case_map_stage_get_case_map (stage); + struct case_map *map = case_map_stage_get_case_map (stage); case_map_stage_destroy (stage); if (map != NULL) writer = case_map_create_output_translator (map, writer); dict_unref (dict); casereader_transfer (proc_open_filtering (ds, !retain_unselected), writer); - ok = casewriter_destroy (writer); + bool ok = casewriter_destroy (writer); ok = proc_commit (ds) && ok; return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; @@ -287,6 +271,5 @@ error: case_map_stage_destroy (stage); fh_unref (handle); dict_unref (dict); - case_map_destroy (map); return CMD_FAILURE; } diff --git a/src/language/data-io/save.c b/src/language/data-io/save.c index bf78276b84..a445870716 100644 --- a/src/language/data-io/save.c +++ b/src/language/data-io/save.c @@ -294,7 +294,7 @@ parse_write_command (struct lexer *lexer, struct dataset *ds, porfile_opts.digits = lex_integer (lexer); lex_get (lexer); } - else if (!parse_dict_trim (lexer, dict, false)) + else if (!parse_dict_trim (lexer, dict)) goto error; if (!lex_match (lexer, T_SLASH)) diff --git a/src/language/data-io/trim.c b/src/language/data-io/trim.c index e0150e04bc..fab97d5c9a 100644 --- a/src/language/data-io/trim.c +++ b/src/language/data-io/trim.c @@ -33,14 +33,12 @@ #include "gettext.h" #define _(msgid) gettext (msgid) -/* Commands that read and write system files share a great deal - of common syntactic structure for rearranging and dropping - variables. This function parses this syntax and modifies DICT - appropriately. If RELAX is true, then the modified dictionary - need not conform to the usual variable name rules. Returns - true on success, false on failure. */ +/* Commands that read and write system files share a great deal of common + syntactic structure for rearranging and dropping variables. This function + parses this syntax and modifies DICT appropriately. Returns true on + success, false on failure. */ bool -parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax) +parse_dict_trim (struct lexer *lexer, struct dictionary *dict) { if (lex_match_id (lexer, "MAP")) { @@ -52,7 +50,7 @@ parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax) else if (lex_match_id (lexer, "KEEP")) return parse_dict_keep (lexer, dict); else if (lex_match_id (lexer, "RENAME")) - return parse_dict_rename (lexer, dict, relax); + return parse_dict_rename (lexer, dict); else { lex_error_expecting (lexer, "MAP", "DROP", "KEEP", "RENAME"); @@ -60,230 +58,81 @@ parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax) } } -/* Check that OLD_NAME can be renamed to NEW_NAME in DICT. */ -static bool -check_rename (struct lexer *lexer, int start_ofs, int end_ofs, - const struct dictionary *dict, - const char *old_name, const char *new_name) -{ - if (dict_lookup_var (dict, new_name) != NULL) - { - lex_ofs_error (lexer, start_ofs, end_ofs, - _("Cannot rename %s as %s because a variable named %s " - "already exists."), - old_name, new_name, new_name); - msg (SN, _("To rename variables with overlapping names, use a single " - "RENAME subcommand such as `/RENAME (A=B)(B=C)(C=A)', or " - "equivalently, `/RENAME (A B C=B C A)'.")); - return false; - } - return true; -} - -/* Parse a "VarX TO VarY" sequence where X and Y are integers - such that X >= Y. - If successfull, returns a string to the prefix Var and sets FIRST - to X and LAST to Y. Returns NULL on failure. - The caller must free the return value. */ -static char * -try_to_sequence (struct lexer *lexer, const struct dictionary *dict, - int *first, int *last) -{ - /* Check that the next 3 tokens are of the correct type. */ - if (lex_token (lexer) != T_ID - || lex_next_token (lexer, 1) != T_TO - || lex_next_token (lexer, 2) != T_ID) - return NULL; - - /* Check that the first and last tokens are suitable as - variable names. */ - const char *s0 = lex_tokcstr (lexer); - char *error = id_is_valid__ (s0, dict_get_encoding (dict)); - if (error) - { - lex_error (lexer, "%s", error); - free (error); - return NULL; - } - - const char *s1 = lex_next_tokcstr (lexer, 2); - error = id_is_valid__ (s1, dict_get_encoding (dict)); - if (error) - { - lex_next_error (lexer, 2, 2, "%s", error); - free (error); - return NULL; - } - - int x0 = strcspn (s0, "0123456789"); - int x1 = strcspn (s1, "0123456789"); - - /* The non-digit parts of s0 and s1 must be the same length. */ - if (x0 != x1) - return NULL; - - /* Both s0 and s1 must have some digits. */ - if (strlen (s0) <= x0) - return NULL; - - if (strlen (s1) <= x1) - return NULL; - - /* The non-digit parts of s0 and s1 must be identical. */ - if (0 != strncmp (s0, s1, x0)) - return NULL; - - /* Both names must end with digits. */ - int len_s0_pfx = strspn (s0 + x0, "0123456789"); - if (len_s0_pfx + x0 != strlen (s0)) - return NULL; - - int len_s1_pfx = strspn (s1 + x1, "0123456789"); - if (len_s1_pfx + x1 != strlen (s1)) - return NULL; - - const char *n_start = s0 + x0; - const char *n_stop = s1 + x1; - - /* The first may not be greater than the last. */ - if (atoi (n_start) > atoi (n_stop)) - return NULL; - - char *prefix = xstrndup (s0, x1); - - *first = atoi (n_start); - *last = atoi (n_stop); - - return prefix; -} - - /* Parses and performs the RENAME subcommand of GET, SAVE, and - related commands. If RELAX is true, then the new variable - names need not conform to the normal dictionary rules. -*/ + related commands. */ bool -parse_dict_rename (struct lexer *lexer, struct dictionary *dict, - bool relax) +parse_dict_rename (struct lexer *lexer, struct dictionary *dict) { - struct variable **oldvars = NULL; - size_t n_newvars = 0; - int group = 0; - char **newnames = NULL; lex_match (lexer, T_EQUALS); + int start_ofs = lex_ofs (lexer); + + struct variable **old_vars = NULL; + size_t n_old_vars = 0; + char **new_vars = NULL; + size_t n_new_vars = 0; + + bool ok = false; while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD) { - size_t n_oldvars = 0; - oldvars = NULL; - n_newvars = 0; - n_oldvars = 0; - oldvars = NULL; + size_t prev_n_old = n_old_vars; + size_t prev_n_new = n_new_vars; bool paren = lex_match (lexer, T_LPAREN); - group++; - if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE)) - goto fail; + int pv_opts = PV_NO_DUPLICATE | PV_APPEND | (paren ? 0 : PV_SINGLE); + + int old_vars_start = lex_ofs (lexer); + if (!parse_variables (lexer, dict, &old_vars, &n_old_vars, pv_opts)) + goto done; + int old_vars_end = lex_ofs (lexer) - 1; if (!lex_force_match (lexer, T_EQUALS)) - goto fail; - - newnames = xmalloc (sizeof *newnames * n_oldvars); - - char *prefix = NULL; - int first, last; - /* First attempt to parse v1 TO v10 format. */ - if ((prefix = try_to_sequence (lexer, dict, &first, &last))) - { - /* These 3 tokens have already been checked in the - try_to_sequence function. */ - int start_ofs = lex_ofs (lexer); - lex_get (lexer); - lex_get (lexer); - lex_get (lexer); - int end_ofs = lex_ofs (lexer) - 1; - - /* Make sure the new names are suitable. */ - for (int i = first; i <= last; ++i) - { - char *vn = xasprintf ("%s%d", prefix, i); - - if (!check_rename (lexer, start_ofs, end_ofs, - dict, var_get_name (oldvars[n_newvars]), vn)) - { - free (vn); - free (prefix); - goto fail; - } - - newnames[i - first] = vn; - n_newvars++; - } - } - else - while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING) - { - if (n_newvars >= n_oldvars) - break; - const char *new_name = lex_tokcstr (lexer); - if (!relax) - { - char *error = id_is_plausible__ (new_name); - if (error) - { - lex_error (lexer, "%s", error); - free (error); - goto fail; - } - } - - int ofs = lex_ofs (lexer); - if (!check_rename (lexer, ofs, ofs, - dict, var_get_name (oldvars[n_newvars]), new_name)) - goto fail; - newnames[n_newvars] = xstrdup (new_name); - lex_get (lexer); - n_newvars++; - } - free (prefix); - - if (n_newvars != n_oldvars) - { - msg (SE, _("Number of variables on left side of `=' (%zu) does not " - "match number of variables on right side (%zu), in " - "parenthesized group %d of RENAME subcommand."), - n_oldvars, n_newvars, group); - goto fail; - } + goto done; + + int new_vars_start = lex_ofs (lexer); + if (!parse_DATA_LIST_vars (lexer, dict, &new_vars, &n_new_vars, pv_opts)) + goto done; + int new_vars_end = lex_ofs (lexer) - 1; - if (paren) - if (!lex_force_match (lexer, T_RPAREN)) - goto fail; + if (paren && !lex_force_match (lexer, T_RPAREN)) + goto done; - char *errname = 0; - if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname)) + if (n_new_vars != n_old_vars) { - msg (SE, - _("Requested renaming duplicates variable name %s."), - errname); - goto fail; + size_t added_old = n_old_vars - prev_n_old; + size_t added_new = n_new_vars - prev_n_new; + + msg (SE, _("Old and new variable counts do not match.")); + lex_ofs_msg (lexer, SN, old_vars_start, old_vars_end, + ngettext ("There is %zu old variable.", + "There are %zu old variables.", added_old), + added_old); + lex_ofs_msg (lexer, SN, new_vars_start, new_vars_end, + ngettext ("There is %zu new variable name.", + "There are %zu new variable names.", + added_new), + added_new); + goto done; } - free (oldvars); - for (int i = 0; i < n_newvars; ++i) - free (newnames[i]); - free (newnames); - newnames = NULL; } + int end_ofs = lex_ofs (lexer) - 1; - return true; - - fail: - free (oldvars); - for (int i = 0; i < n_newvars; ++i) - free (newnames[i]); - free (newnames); - newnames = NULL; - return false; + char *dup_name = NULL; + if (!dict_rename_vars (dict, old_vars, new_vars, n_new_vars, &dup_name)) + { + lex_ofs_error (lexer, start_ofs, end_ofs, + _("Requested renaming duplicates variable name %s."), + dup_name); + goto done; + } + ok = true; + +done: + free (old_vars); + for (size_t i = 0; i < n_new_vars; ++i) + free (new_vars[i]); + free (new_vars); + return ok; } /* Parses and performs the DROP subcommand of GET, SAVE, and diff --git a/src/language/data-io/trim.h b/src/language/data-io/trim.h index 188acb4781..106d1a8f32 100644 --- a/src/language/data-io/trim.h +++ b/src/language/data-io/trim.h @@ -21,8 +21,8 @@ struct lexer; struct dictionary; -bool parse_dict_trim (struct lexer *, struct dictionary *, bool); -bool parse_dict_rename (struct lexer *, struct dictionary *, bool); +bool parse_dict_trim (struct lexer *, struct dictionary *); +bool parse_dict_rename (struct lexer *, struct dictionary *); bool parse_dict_drop (struct lexer *, struct dictionary *); bool parse_dict_keep (struct lexer *, struct dictionary *); diff --git a/src/language/lexer/variable-parser.h b/src/language/lexer/variable-parser.h index 5815dc458b..4419c9ebf6 100644 --- a/src/language/lexer/variable-parser.h +++ b/src/language/lexer/variable-parser.h @@ -40,16 +40,16 @@ void var_set_destroy (struct var_set *vs); enum { - PV_NONE = 0, /* No options. */ - PV_SINGLE = 0001, /* Restrict to a single name or TO use. */ - PV_DUPLICATE = 0002, /* Don't merge duplicates. */ - PV_APPEND = 0004, /* Append to existing list. */ - PV_NO_DUPLICATE = 0010, /* Error on duplicates. */ - PV_NUMERIC = 0020, /* Vars must be numeric. */ - PV_STRING = 0040, /* Vars must be string. */ - PV_SAME_TYPE = 00100, /* All vars must be the same type. */ - PV_SAME_WIDTH = 00200, /* All vars must be the same type and width. */ - PV_NO_SCRATCH = 00400 /* Disallow scratch variables. */ + PV_NONE = 0, /* No options. */ + PV_SINGLE = 1 << 0, /* Restrict to a single name or TO use. */ + PV_DUPLICATE = 1 << 1, /* Don't merge duplicates. */ + PV_APPEND = 1 << 2, /* Append to existing list. */ + PV_NO_DUPLICATE = 1 << 3, /* Error on duplicates. */ + PV_NUMERIC = 1 << 4, /* Vars must be numeric. */ + PV_STRING = 1 << 5, /* Vars must be string. */ + PV_SAME_TYPE = 1 << 6, /* All vars must be the same type. */ + PV_SAME_WIDTH = 1 << 7, /* All vars must be the same type and width. */ + PV_NO_SCRATCH = 1 << 8, /* Disallow scratch variables. */ }; struct variable *parse_variable (struct lexer *, const struct dictionary *); diff --git a/tests/language/data-io/save-translate.at b/tests/language/data-io/save-translate.at index b1f860e8e7..ebfca92d4c 100644 --- a/tests/language/data-io/save-translate.at +++ b/tests/language/data-io/save-translate.at @@ -115,6 +115,7 @@ AT_CLEANUP AT_SETUP([CSV output -- KEEP, RENAME bad name ]) +AT_KEYWORDS([SAVE TRANSLATE]) AT_DATA([bad.sps], [ data list notable list /Var1 Var2 Var3 Var4 Var5 *. begin data @@ -129,7 +130,7 @@ SAVE TRANSLATE /FIELDNAMES /Unselected=DELETE /RENAME = - Var4 = foobar + Var4 = Var5 (Var1 Var2 = one Var3 ) (Var3 = "The second") /CELLS=VALUES @@ -137,11 +138,13 @@ SAVE TRANSLATE ]) AT_CHECK([pspp -O format=csv bad.sps], [1], [dnl -"bad.sps:16.26-16.29: error: SAVE TRANSLATE: Cannot rename Var2 as Var3 because a variable named Var3 already exists. +"bad.sps:15.9-17.29: error: SAVE TRANSLATE: Requested renaming duplicates variable name Var5. + 15 | Var4 = Var5 + | ^~~~~~~~~~~ 16 | (Var1 Var2 = one Var3 ) - | ^~~~" - -"bad.sps:16: note: SAVE TRANSLATE: To rename variables with overlapping names, use a single RENAME subcommand such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, `/RENAME (A B C=B C A)'." + | ------------------------------- + 17 | (Var3 = ""The second"") + | -----------------------------" ]) @@ -161,3 +164,198 @@ number time date datetime string filter ]) AT_CLEANUP +AT_SETUP([SAVE TRANSLATE syntax errors]) +: > xyzzy.csv +AT_DATA([save-translate.sps], [dnl +DATA LIST LIST NOTABLE /v1 to v10. +SAVE TRANSLATE **. +SAVE TRANSLATE/OUTFILE=**. +SAVE TRANSLATE/OUTFILE='xyzzy.txt'/OUTFILE='xyzzy.txt'. +SAVE TRANSLATE/TYPE=CSV/TYPE=**. +SAVE TRANSLATE/TYPE=**. +SAVE TRANSLATE/MISSING=**. +SAVE TRANSLATE/CELLS=**. +SAVE TRANSLATE/TEXTOPTIONS DELIMITER=**. +SAVE TRANSLATE/TEXTOPTIONS DELIMITER='ab'. +SAVE TRANSLATE/TEXTOPTIONS QUALIFIER=**. +SAVE TRANSLATE/TEXTOPTIONS QUALIFIER='ab'. +SAVE TRANSLATE/TEXTOPTIONS DECIMAL=**. +SAVE TRANSLATE/UNSELECTED=**. +SAVE TRANSLATE/ **. +SAVE TRANSLATE/OUTFILE='xyzzy.csv'. +SAVE TRANSLATE/TYPE=CSV. +SAVE TRANSLATE/OUTFILE='xyzzy.csv'/TYPE=CSV. +SAVE TRANSLATE/RENAME **. +SAVE TRANSLATE/RENAME v1**. +SAVE TRANSLATE/RENAME(v1**). +SAVE TRANSLATE/RENAME v1=. +SAVE TRANSLATE/RENAME v1=**. +SAVE TRANSLATE/RENAME v1 to v5=v6. +SAVE TRANSLATE/RENAME (v1=v2 v3). +SAVE TRANSLATE/RENAME (v1 v2=v3). +SAVE TRANSLATE/RENAME (v1=v3**. +SAVE TRANSLATE/RENAME v1=v5. +SAVE TRANSLATE/RENAME v1 v5=v5 v1. +SAVE TRANSLATE/RENAME(v1 v5=v5 v1). +SAVE TRANSLATE/RENAME(v1 to v10=v01 to v10). +SAVE TRANSLATE/RENAME=v1=v1. +SAVE TRANSLATE/DROP=ALL. +SAVE TRANSLATE/DROP=**. +SAVE TRANSLATE/KEEP=**. +]) +AT_CHECK([pspp -O format=csv save-translate.sps], [1], [dnl +"save-translate.sps:2.16-2.17: error: SAVE TRANSLATE: Syntax error expecting `/'. + 2 | SAVE TRANSLATE **. + | ^~" + +"save-translate.sps:3.24-3.25: error: SAVE TRANSLATE: Syntax error expecting a file name or handle name. + 3 | SAVE TRANSLATE/OUTFILE=**. + | ^~" + +"save-translate.sps:4.36-4.42: error: SAVE TRANSLATE: Subcommand OUTFILE may only be specified once. + 4 | SAVE TRANSLATE/OUTFILE='xyzzy.txt'/OUTFILE='xyzzy.txt'. + | ^~~~~~~" + +"save-translate.sps:5.25-5.28: error: SAVE TRANSLATE: Subcommand TYPE may only be specified once. + 5 | SAVE TRANSLATE/TYPE=CSV/TYPE=**. + | ^~~~" + +"save-translate.sps:6.21-6.22: error: SAVE TRANSLATE: Syntax error expecting CSV or TAB. + 6 | SAVE TRANSLATE/TYPE=**. + | ^~" + +"save-translate.sps:7.24-7.25: error: SAVE TRANSLATE: Syntax error expecting IGNORE or RECODE. + 7 | SAVE TRANSLATE/MISSING=**. + | ^~" + +"save-translate.sps:8.22-8.23: error: SAVE TRANSLATE: Syntax error expecting VALUES or LABELS. + 8 | SAVE TRANSLATE/CELLS=**. + | ^~" + +"save-translate.sps:9.38-9.39: error: SAVE TRANSLATE: Syntax error expecting string. + 9 | SAVE TRANSLATE/TEXTOPTIONS DELIMITER=**. + | ^~" + +"save-translate.sps:10.38-10.41: error: SAVE TRANSLATE: The DELIMITER string must contain exactly one character. + 10 | SAVE TRANSLATE/TEXTOPTIONS DELIMITER='ab'. + | ^~~~" + +"save-translate.sps:11.38-11.39: error: SAVE TRANSLATE: Syntax error expecting string. + 11 | SAVE TRANSLATE/TEXTOPTIONS QUALIFIER=**. + | ^~" + +"save-translate.sps:12.38-12.41: error: SAVE TRANSLATE: The QUALIFIER string must contain exactly one character. + 12 | SAVE TRANSLATE/TEXTOPTIONS QUALIFIER='ab'. + | ^~~~" + +"save-translate.sps:13.36-13.37: error: SAVE TRANSLATE: Syntax error expecting DOT or COMMA. + 13 | SAVE TRANSLATE/TEXTOPTIONS DECIMAL=**. + | ^~" + +"save-translate.sps:14.27-14.28: error: SAVE TRANSLATE: Syntax error expecting RETAIN or DELETE. + 14 | SAVE TRANSLATE/UNSELECTED=**. + | ^~" + +"save-translate.sps:15.17-15.18: error: SAVE TRANSLATE: Syntax error expecting MAP, DROP, KEEP, or RENAME. + 15 | SAVE TRANSLATE/ **. + | ^~" + +"save-translate.sps:16.1-16.35: error: SAVE TRANSLATE: Required subcommand TYPE was not specified. + 16 | SAVE TRANSLATE/OUTFILE='xyzzy.csv'. + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:17.1-17.24: error: SAVE TRANSLATE: Required subcommand OUTFILE was not specified. + 17 | SAVE TRANSLATE/TYPE=CSV. + | ^~~~~~~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:18.16-18.34: error: SAVE TRANSLATE: Output file `xyzzy.csv' exists but REPLACE was not specified. + 18 | SAVE TRANSLATE/OUTFILE='xyzzy.csv'/TYPE=CSV. + | ^~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:19.23-19.24: error: SAVE TRANSLATE: Syntax error expecting variable name. + 19 | SAVE TRANSLATE/RENAME **. + | ^~" + +"save-translate.sps:20.25-20.26: error: SAVE TRANSLATE: Syntax error expecting `='. + 20 | SAVE TRANSLATE/RENAME v1**. + | ^~" + +"save-translate.sps:21.25-21.26: error: SAVE TRANSLATE: Syntax error expecting `='. + 21 | SAVE TRANSLATE/RENAME(v1**). + | ^~" + +"save-translate.sps:22.26: error: SAVE TRANSLATE: Syntax error expecting variable name. + 22 | SAVE TRANSLATE/RENAME v1=. + | ^" + +"save-translate.sps:23.26-23.27: error: SAVE TRANSLATE: Syntax error expecting variable name. + 23 | SAVE TRANSLATE/RENAME v1=**. + | ^~" + +save-translate.sps:24: error: SAVE TRANSLATE: Old and new variable counts do not match. + +"save-translate.sps:24.23-24.30: note: SAVE TRANSLATE: There are 5 old variables. + 24 | SAVE TRANSLATE/RENAME v1 to v5=v6. + | ^~~~~~~~" + +"save-translate.sps:24.32-24.33: note: SAVE TRANSLATE: There is 1 new variable name. + 24 | SAVE TRANSLATE/RENAME v1 to v5=v6. + | ^~" + +save-translate.sps:25: error: SAVE TRANSLATE: Old and new variable counts do not match. + +"save-translate.sps:25.24-25.25: note: SAVE TRANSLATE: There is 1 old variable. + 25 | SAVE TRANSLATE/RENAME (v1=v2 v3). + | ^~" + +"save-translate.sps:25.27-25.31: note: SAVE TRANSLATE: There are 2 new variable names. + 25 | SAVE TRANSLATE/RENAME (v1=v2 v3). + | ^~~~~" + +save-translate.sps:26: error: SAVE TRANSLATE: Old and new variable counts do not match. + +"save-translate.sps:26.24-26.28: note: SAVE TRANSLATE: There are 2 old variables. + 26 | SAVE TRANSLATE/RENAME (v1 v2=v3). + | ^~~~~" + +"save-translate.sps:26.30-26.31: note: SAVE TRANSLATE: There is 1 new variable name. + 26 | SAVE TRANSLATE/RENAME (v1 v2=v3). + | ^~" + +"save-translate.sps:27.29-27.30: error: SAVE TRANSLATE: Syntax error expecting `)'. + 27 | SAVE TRANSLATE/RENAME (v1=v3**. + | ^~" + +"save-translate.sps:28.23-28.27: error: SAVE TRANSLATE: Requested renaming duplicates variable name v5. + 28 | SAVE TRANSLATE/RENAME v1=v5. + | ^~~~~" + +"save-translate.sps:29.26-29.27: error: SAVE TRANSLATE: Syntax error expecting `='. + 29 | SAVE TRANSLATE/RENAME v1 v5=v5 v1. + | ^~" + +"save-translate.sps:30.1-30.35: error: SAVE TRANSLATE: Required subcommand TYPE was not specified. + 30 | SAVE TRANSLATE/RENAME(v1 v5=v5 v1). + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:31.1-31.44: error: SAVE TRANSLATE: Required subcommand TYPE was not specified. + 31 | SAVE TRANSLATE/RENAME(v1 to v10=v01 to v10). + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:32.1-32.28: error: SAVE TRANSLATE: Required subcommand TYPE was not specified. + 32 | SAVE TRANSLATE/RENAME=v1=v1. + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +"save-translate.sps:33.16-33.23: error: SAVE TRANSLATE: Cannot DROP all variables from dictionary. + 33 | SAVE TRANSLATE/DROP=ALL. + | ^~~~~~~~" + +"save-translate.sps:34.21-34.22: error: SAVE TRANSLATE: Syntax error expecting variable name. + 34 | SAVE TRANSLATE/DROP=**. + | ^~" + +"save-translate.sps:35.21-35.22: error: SAVE TRANSLATE: Syntax error expecting variable name. + 35 | SAVE TRANSLATE/KEEP=**. + | ^~" +]) +AT_CLEANUP -- 2.30.2