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"))
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);
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)
{
if (lex_match_id (lexer, "OUTFILE"))
{
+ outfile_start = lex_ofs (lexer) - 1;
if (handle != NULL)
{
lex_sbc_only_once (lexer, "OUTFILE");
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"))
{
goto error;
}
}
- else if (!parse_dict_trim (lexer, dict, true))
+ else if (!parse_dict_trim (lexer, dict))
goto error;
}
}
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;
}
: ';'),
.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;
case_map_stage_destroy (stage);
fh_unref (handle);
dict_unref (dict);
- case_map_destroy (map);
return CMD_FAILURE;
}
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))
#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"))
{
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");
}
}
-/* 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
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 *);
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 *);
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
/FIELDNAMES
/Unselected=DELETE
/RENAME =
- Var4 = foobar
+ Var4 = Var5
(Var1 Var2 = one Var3 )
(Var3 = "The second")
/CELLS=VALUES
])
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"")
+ | -----------------------------"
])
])
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