From fccff7b9926def9d360b317239c3b98cea7bb843 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 19 Sep 2022 23:13:16 -0700 Subject: [PATCH] RECODE: Improve error messages and coding style. --- src/language/xforms/recode.c | 342 +++++++++++++++++--------------- tests/language/stats/t-test.at | 2 +- tests/language/xforms/recode.at | 146 +++++++++++++- 3 files changed, 328 insertions(+), 162 deletions(-) diff --git a/src/language/xforms/recode.c b/src/language/xforms/recode.c index 42fa9ff215..286b2ae641 100644 --- a/src/language/xforms/recode.c +++ b/src/language/xforms/recode.c @@ -70,6 +70,7 @@ struct map_out bool copy_input; /* If true, copy input to output. */ union value value; /* If copy_input false, recoded value. */ int width; /* If copy_input false, output value width. */ + int ofs; /* Lexical location. */ }; /* Describes how to recode a single value or range of values into a @@ -106,7 +107,10 @@ struct recode_trns static bool parse_src_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict); static bool parse_mappings (struct lexer *, struct recode_trns *, const char *dict_encoding); -static bool parse_dst_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict); +static bool parse_dst_vars (struct lexer *, struct recode_trns *, + const struct dictionary *, + int src_start, int src_end, + int mappings_start, int mappings_end); static void add_mapping (struct recode_trns *, size_t *map_allocated, const struct map_in *); @@ -114,18 +118,16 @@ static void add_mapping (struct recode_trns *, static bool parse_map_in (struct lexer *lexer, struct map_in *, struct pool *, enum val_type src_type, size_t max_src_width, const char *dict_encoding); -static void set_map_in_generic (struct map_in *, enum map_in_type); -static void set_map_in_num (struct map_in *, enum map_in_type, double, double); static void set_map_in_str (struct map_in *, struct pool *, struct substring, size_t width, const char *dict_encoding); static bool parse_map_out (struct lexer *lexer, struct pool *, struct map_out *); -static void set_map_out_num (struct map_out *, double); static void set_map_out_str (struct map_out *, struct pool *, struct substring); -static bool enlarge_dst_widths (struct recode_trns *); +static bool enlarge_dst_widths (struct lexer *, struct recode_trns *, + int dst_start, int dst_end); static void create_dst_vars (struct recode_trns *, struct dictionary *); static bool recode_trns_free (void *trns_); @@ -134,47 +136,71 @@ static const struct trns_class recode_trns_class; /* Parser. */ +static bool +parse_one_recoding (struct lexer *lexer, struct dataset *ds, + struct recode_trns *trns) +{ + struct dictionary *dict = dataset_dict (ds); + + /* Parse source variable names, + then input to output mappings, + then destination variable names. */ + int src_start = lex_ofs (lexer); + if (!parse_src_vars (lexer, trns, dict)) + return false; + int src_end = lex_ofs (lexer) - 1; + + int mappings_start = lex_ofs (lexer); + if (!parse_mappings (lexer, trns, dict_get_encoding (dict))) + return false; + int mappings_end = lex_ofs (lexer) - 1; + + int dst_start = lex_ofs (lexer); + if (!parse_dst_vars (lexer, trns, dict, + src_start, src_end, mappings_start, mappings_end)) + return false; + int dst_end = lex_ofs (lexer) - 1; + if (dst_end < dst_start) + { + /* There was no target variable syntax, so the target variables are the + same as the source variables. */ + dst_start = src_start; + dst_end = src_end; + } + + /* Ensure that all the output strings are at least as wide + as the widest destination variable. */ + if (trns->dst_type == VAL_STRING + && !enlarge_dst_widths (lexer, trns, dst_start, dst_end)) + return false; + + /* Create destination variables, if needed. + This must be the final step; otherwise we'd have to + delete destination variables on failure. */ + trns->dst_dict = dict; + if (trns->src_vars != trns->dst_vars) + create_dst_vars (trns, dict); + + /* Done. */ + add_transformation (ds, &recode_trns_class, trns); + return true; +} + /* Parses the RECODE transformation. */ int cmd_recode (struct lexer *lexer, struct dataset *ds) { do { - struct dictionary *dict = dataset_dict (ds); - struct recode_trns *trns - = pool_create_container (struct recode_trns, pool); - - /* Parse source variable names, - then input to output mappings, - then destintation variable names. */ - if (!parse_src_vars (lexer, trns, dict) - || !parse_mappings (lexer, trns, dict_get_encoding (dict)) - || !parse_dst_vars (lexer, trns, dict)) + struct pool *pool = pool_create (); + struct recode_trns *trns = pool_alloc (pool, sizeof *trns); + *trns = (struct recode_trns) { .pool = pool }; + + if (!parse_one_recoding (lexer, ds, trns)) { recode_trns_free (trns); return CMD_FAILURE; } - - /* Ensure that all the output strings are at least as wide - as the widest destination variable. */ - if (trns->dst_type == VAL_STRING) - { - if (! enlarge_dst_widths (trns)) - { - recode_trns_free (trns); - return CMD_FAILURE; - } - } - - /* Create destination variables, if needed. - This must be the final step; otherwise we'd have to - delete destination variables on failure. */ - trns->dst_dict = dict; - if (trns->src_vars != trns->dst_vars) - create_dst_vars (trns, dict); - - /* Done. */ - add_transformation (ds, &recode_trns_class, trns); } while (lex_match (lexer, T_SLASH)); @@ -203,13 +229,9 @@ static bool parse_mappings (struct lexer *lexer, struct recode_trns *trns, const char *dict_encoding) { - size_t map_allocated; - bool have_dst_type; - size_t i; - /* Find length of longest source variable. */ trns->max_src_width = var_get_width (trns->src_vars[0]); - for (i = 1; i < trns->n_vars; i++) + for (size_t i = 1; i < trns->n_vars; i++) { size_t var_width = var_get_width (trns->src_vars[i]); if (var_width > trns->max_src_width) @@ -217,10 +239,8 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns, } /* Parse the mappings in parentheses. */ - trns->mappings = NULL; - trns->n_maps = 0; - map_allocated = 0; - have_dst_type = false; + size_t map_allocated = 0; + bool have_dst_type = false; if (!lex_force_match (lexer, T_LPAREN)) return false; do @@ -229,11 +249,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns, if (!lex_match_id (lexer, "CONVERT")) { - struct map_out out; - size_t first_map_idx; - size_t i; - - first_map_idx = trns->n_maps; + size_t first_map_idx = trns->n_maps; /* Parse source specifications. */ do @@ -249,42 +265,56 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns, } while (!lex_match (lexer, T_EQUALS)); + struct map_out out; if (!parse_map_out (lexer, trns->pool, &out)) return false; - if (out.copy_input) - dst_type = trns->src_type; - else - dst_type = val_type_from_width (out.width); - if (have_dst_type && dst_type != trns->dst_type) - { - msg (SE, _("Inconsistent target variable types. " - "Target variables " - "must be all numeric or all string.")); - return false; - } - - for (i = first_map_idx; i < trns->n_maps; i++) + dst_type = (out.copy_input + ? trns->src_type + : val_type_from_width (out.width)); + for (size_t i = first_map_idx; i < trns->n_maps; i++) trns->mappings[i].out = out; } else { /* Parse CONVERT as a special case. */ - struct map_in in; - set_map_in_generic (&in, MAP_CONVERT); + struct map_in in = { .type = MAP_CONVERT }; add_mapping (trns, &map_allocated, &in); - set_map_out_num (&trns->mappings[trns->n_maps - 1].out, 0.0); + + int ofs = lex_ofs (lexer) - 1; + trns->mappings[trns->n_maps - 1].out = (struct map_out) { + .ofs = ofs, + }; dst_type = VAL_NUMERIC; - if (trns->src_type != VAL_STRING - || (have_dst_type && trns->dst_type != VAL_NUMERIC)) + if (trns->src_type != VAL_STRING) { - lex_next_error (lexer, -1, -1, - _("CONVERT requires string input values and " - "numeric output values.")); + lex_ofs_error (lexer, ofs, ofs, + _("CONVERT requires string input values.")); return false; } } + if (have_dst_type && dst_type != trns->dst_type) + { + msg (SE, _("Output values must be all numeric or all string.")); + + assert (trns->n_maps > 1); + const struct map_out *numeric = &trns->mappings[trns->n_maps - 2].out; + const struct map_out *string = &trns->mappings[trns->n_maps - 1].out; + + if (trns->dst_type == VAL_STRING) + { + const struct map_out *tmp = numeric; + numeric = string; + string = tmp; + } + + lex_ofs_msg (lexer, SN, numeric->ofs, numeric->ofs, + _("This output value is numeric.")); + lex_ofs_msg (lexer, SN, string->ofs, string->ofs, + _("This output value is string.")); + return false; + } trns->dst_type = dst_type; have_dst_type = true; @@ -308,25 +338,29 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool, { if (lex_match_id (lexer, "ELSE")) - set_map_in_generic (in, MAP_ELSE); + *in = (struct map_in) { .type = MAP_ELSE }; else if (src_type == VAL_NUMERIC) { if (lex_match_id (lexer, "MISSING")) - set_map_in_generic (in, MAP_MISSING); + *in = (struct map_in) { .type = MAP_MISSING }; else if (lex_match_id (lexer, "SYSMIS")) - set_map_in_generic (in, MAP_SYSMIS); + *in = (struct map_in) { .type = MAP_SYSMIS }; else { double x, y; if (!parse_num_range (lexer, &x, &y, NULL)) return false; - set_map_in_num (in, x == y ? MAP_SINGLE : MAP_RANGE, x, y); + *in = (struct map_in) { + .type = x == y ? MAP_SINGLE : MAP_RANGE, + .x = { .f = x }, + .y = { .f = y }, + }; } } else { if (lex_match_id (lexer, "MISSING")) - set_map_in_generic (in, MAP_MISSING); + *in = (struct map_in) { .type = MAP_MISSING }; else if (!lex_force_string (lexer)) return false; else @@ -334,11 +368,11 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool, set_map_in_str (in, pool, lex_tokss (lexer), max_src_width, dict_encoding); lex_get (lexer); - if (lex_token (lexer) == T_ID - && lex_id_match (ss_cstr ("THRU"), lex_tokss (lexer))) + if (lex_match_id (lexer, "THRU")) { - lex_error (lexer, _("%s is not allowed with string variables."), - "THRU"); + lex_next_error (lexer, -1, -1, + _("%s is not allowed with string variables."), + "THRU"); return false; } } @@ -363,23 +397,6 @@ add_mapping (struct recode_trns *trns, m->in = *in; } -/* Sets IN as a mapping of the given TYPE. */ -static void -set_map_in_generic (struct map_in *in, enum map_in_type type) -{ - in->type = type; -} - -/* Sets IN as a numeric mapping of the given TYPE, - with X and Y as the two numeric values. */ -static void -set_map_in_num (struct map_in *in, enum map_in_type type, double x, double y) -{ - in->type = type; - in->x.f = x; - in->y.f = y; -} - /* Sets IN as a string mapping, with STRING as the string, allocated from POOL. The string is padded with spaces on the right to WIDTH characters long. */ @@ -388,9 +405,10 @@ set_map_in_str (struct map_in *in, struct pool *pool, struct substring string, size_t width, const char *dict_encoding) { + *in = (struct map_in) { .type = MAP_SINGLE }; + char *s = recode_string (dict_encoding, "UTF-8", ss_data (string), ss_length (string)); - in->type = MAP_SINGLE; value_init_pool (pool, &in->x, width); value_copy_buf_rpad (&in->x, width, CHAR_CAST (uint8_t *, s), strlen (s), ' '); @@ -404,38 +422,27 @@ parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out) { if (lex_is_number (lexer)) { - set_map_out_num (out, lex_number (lexer)); + *out = (struct map_out) { .value = { .f = lex_number (lexer) } }; lex_get (lexer); } else if (lex_match_id (lexer, "SYSMIS")) - set_map_out_num (out, SYSMIS); + *out = (struct map_out) { .value = { .f = SYSMIS } }; else if (lex_is_string (lexer)) { set_map_out_str (out, pool, lex_tokss (lexer)); lex_get (lexer); } else if (lex_match_id (lexer, "COPY")) - { - out->copy_input = true; - out->width = 0; - } + *out = (struct map_out) { .copy_input = true }; else { lex_error (lexer, _("Syntax error expecting output value.")); return false; } + out->ofs = lex_ofs (lexer) - 1; return true; } -/* Sets OUT as a numeric mapping output with the given VALUE. */ -static void -set_map_out_num (struct map_out *out, double value) -{ - out->copy_input = false; - out->value.f = value; - out->width = 0; -} - /* Sets OUT as a string mapping output with the given VALUE. */ static void set_map_out_str (struct map_out *out, struct pool *pool, @@ -452,82 +459,96 @@ set_map_out_str (struct map_out *out, struct pool *pool, length = 1; } - out->copy_input = false; + *out = (struct map_out) { .width = length }; value_init_pool (pool, &out->value, length); memcpy (out->value.s, string, length); - out->width = length; } /* Parses a set of target variables into TRNS->dst_vars and TRNS->dst_names. */ static bool parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, - const struct dictionary *dict) + const struct dictionary *dict, int src_start, int src_end, + int mappings_start, int mappings_end) { - size_t i; - + int dst_start, dst_end; if (lex_match_id (lexer, "INTO")) { + dst_start = lex_ofs (lexer); size_t n_names; - size_t i; - if (!parse_mixed_vars_pool (lexer, dict, trns->pool, &trns->dst_names, &n_names, PV_NONE)) return false; + dst_end = lex_ofs (lexer) - 1; if (n_names != trns->n_vars) { - msg (SE, _("%zu variable(s) cannot be recoded into " - "%zu variable(s). Specify the same number " - "of variables as source and target variables."), - trns->n_vars, n_names); + msg (SE, _("Source and target variable counts must match.")); + lex_ofs_msg (lexer, SN, src_start, src_end, + ngettext ("There is %zu source variable.", + "There are %zu source variables.", + trns->n_vars), + trns->n_vars); + lex_ofs_msg (lexer, SN, dst_start, dst_end, + ngettext ("There is %zu target variable.", + "There are %zu target variables.", + n_names), + n_names); return false; } trns->dst_vars = pool_nalloc (trns->pool, trns->n_vars, sizeof *trns->dst_vars); - for (i = 0; i < trns->n_vars; i++) + for (size_t i = 0; i < trns->n_vars; i++) { const struct variable *v; v = trns->dst_vars[i] = dict_lookup_var (dict, trns->dst_names[i]); if (v == NULL && trns->dst_type == VAL_STRING) { - msg (SE, _("There is no variable named " - "%s. (All string variables specified " - "on INTO must already exist. Use the " - "STRING command to create a string " - "variable.)"), - trns->dst_names[i]); + msg (SE, _("All string variables specified on INTO must already " + "exist. (Use the STRING command to create a string " + "variable.)")); + lex_ofs_msg (lexer, SN, dst_start, dst_end, + _("There is no variable named %s."), + trns->dst_names[i]); return false; } } - } else { + dst_start = src_start; + dst_end = src_end; + trns->dst_vars = trns->src_vars; if (trns->src_type != trns->dst_type) { - msg (SE, _("INTO is required with %s input values " - "and %s output values."), - trns->src_type == VAL_NUMERIC ? _("numeric") : _("string"), - trns->dst_type == VAL_NUMERIC ? _("numeric") : _("string")); + if (trns->src_type == VAL_NUMERIC) + lex_ofs_error (lexer, mappings_start, mappings_end, + _("INTO is required with numeric input values " + "and string output values.")); + else + lex_ofs_error (lexer, mappings_start, mappings_end, + _("INTO is required with string input values " + "and numeric output values.")); return false; } } - for (i = 0; i < trns->n_vars; i++) + for (size_t i = 0; i < trns->n_vars; i++) { const struct variable *v = trns->dst_vars[i]; - if (v != NULL && var_get_type (v) != trns->dst_type) + if (v && var_get_type (v) != trns->dst_type) { if (trns->dst_type == VAL_STRING) - msg (SE, _("Type mismatch. Cannot store string data in " - "numeric variable %s."), var_get_name (v)); + lex_ofs_error (lexer, dst_start, dst_end, + _("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)); + lex_ofs_error (lexer, dst_start, dst_end, + _("Type mismatch: cannot store numeric data in " + "string variable %s."), var_get_name (v)); return false; } } @@ -538,14 +559,14 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, /* Ensures that all the output values in TRNS are as wide as the widest destination variable. */ static bool -enlarge_dst_widths (struct recode_trns *trns) +enlarge_dst_widths (struct lexer *lexer, struct recode_trns *trns, + int dst_start, int dst_end) { - size_t i; const struct variable *narrow_var = NULL; int min_dst_width = INT_MAX; trns->max_dst_width = 0; - for (i = 0; i < trns->n_vars; i++) + for (size_t i = 0; i < trns->n_vars; i++) { const struct variable *v = trns->dst_vars[i]; if (var_get_width (v) > trns->max_dst_width) @@ -558,16 +579,22 @@ enlarge_dst_widths (struct recode_trns *trns) } } - for (i = 0; i < trns->n_maps; i++) + for (size_t i = 0; i < trns->n_maps; i++) { struct map_out *out = &trns->mappings[i].out; if (!out->copy_input) { if (out->width > min_dst_width) { - msg (ME, - _("Cannot recode because the variable %s would require a width of %d bytes or greater, but it has a width of only %d bytes."), - var_get_name (narrow_var), out->width, min_dst_width); + msg (SE, _("At least one target variable is too narrow for " + "the output values.")); + lex_ofs_msg (lexer, SN, out->ofs, out->ofs, + _("This recoding output value has width %d."), + out->width); + lex_ofs_msg (lexer, SN, dst_start, dst_end, + _("Target variable %s only has width %d."), + var_get_name (narrow_var), + var_get_width (narrow_var)); return false; } @@ -583,9 +610,7 @@ enlarge_dst_widths (struct recode_trns *trns) static void create_dst_vars (struct recode_trns *trns, struct dictionary *dict) { - size_t i; - - for (i = 0; i < trns->n_vars; i++) + for (size_t i = 0; i < trns->n_vars; i++) { const struct variable **var = &trns->dst_vars[i]; const char *name = trns->dst_names[i]; @@ -604,9 +629,8 @@ create_dst_vars (struct recode_trns *trns, struct dictionary *dict) static const struct map_out * find_src_numeric (struct recode_trns *trns, double value, const struct variable *v) { - struct mapping *m; - - for (m = trns->mappings; m < trns->mappings + trns->n_maps; m++) + for (struct mapping *m = trns->mappings; m < trns->mappings + trns->n_maps; + m++) { const struct map_in *in = &m->in; const struct map_out *out = &m->out; @@ -648,9 +672,8 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, { const char *encoding = dict_get_encoding (trns->dst_dict); int width = var_get_width (src_var); - struct mapping *m; - - for (m = trns->mappings; m < trns->mappings + trns->n_maps; m++) + for (struct mapping *m = trns->mappings; m < trns->mappings + trns->n_maps; + m++) { const struct map_in *in = &m->in; struct map_out *out = &m->out; @@ -697,10 +720,9 @@ static enum trns_result recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED) { struct recode_trns *trns = trns_; - size_t i; *c = case_unshare (*c); - for (i = 0; i < trns->n_vars; i++) + for (size_t i = 0; i < trns->n_vars; i++) { const struct variable *src_var = trns->src_vars[i]; const struct variable *dst_var = trns->dst_vars[i]; diff --git a/tests/language/stats/t-test.at b/tests/language/stats/t-test.at index bab4f9c186..706b57a093 100644 --- a/tests/language/stats/t-test.at +++ b/tests/language/stats/t-test.at @@ -1021,4 +1021,4 @@ AT_CHECK([pspp -O format=csv t-test.sps], [1], [dnl 21 | T-TEST MISSING=INCLUDE/TESTVAL=1. | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" ]) -AT_CLEANUP \ No newline at end of file +AT_CLEANUP diff --git a/tests/language/xforms/recode.at b/tests/language/xforms/recode.at index 9927d3c3d5..f12185a410 100644 --- a/tests/language/xforms/recode.at +++ b/tests/language/xforms/recode.at @@ -330,7 +330,15 @@ list. ]) AT_CHECK([pspp -O format=csv recode.sps], [1], [dnl -"error: Cannot recode because the variable x would require a width of 5 bytes or greater, but it has a width of only 1 bytes." +recode.sps:9: error: RECODE: At least one target variable is too narrow for the output values. + +"recode.sps:9.19-9.25: note: RECODE: This recoding output value has width 5. + 9 | recode x y (""a"" = ""first"") . + | ^~~~~~~" + +"recode.sps:9.8-9.10: note: RECODE: Target variable x only has width 1. + 9 | recode x y (""a"" = ""first"") . + | ^~~" Table: Data List x,y,z @@ -358,8 +366,144 @@ END DATA. RECODE x (1=9) INTO ". EXECUTE. +dnl " ]) AT_CHECK([pspp -O format=csv recode.sps], [1], [ignore]) AT_CLEANUP + + +AT_SETUP([RECODE syntax errors]) +AT_DATA([recode.sps], [dnl +DATA LIST LIST NOTABLE/n1 to n4 (F8.0) s1 (A1) s2 (A2) s3 (A3) s4 (A4). +RECODE **. +RECODE n1 **. +RECODE n1(**). +RECODE s1(**). +RECODE s1('x' THRU 'y'). +RECODE n1(1=**). +RECODE n1(CONVERT). +RECODE n1(1=2)(1='x'). +RECODE n1(1='x')(1=2). +RECODE s1(CONVERT)('1'='1'). +RECODE n1(1=2 **). +RECODE n1(CONVERT). +RECODE s1(CONVERT) INTO n1 n2. +RECODE n1(1='1') INTO xyzzy. +RECODE n1(1='1'). +RECODE s1('1'=1). +RECODE n1(1='1') INTO n2. +RECODE s1(CONVERT) INTO s2. +RECODE n1 TO n4(1='123456') INTO s1 TO s4. +]) +AT_CHECK([pspp -O format=csv recode.sps], [1], [dnl +"recode.sps:2.8-2.9: error: RECODE: Syntax error expecting variable name. + 2 | RECODE **. + | ^~" + +"recode.sps:3.11-3.12: error: RECODE: Syntax error expecting `('. + 3 | RECODE n1 **. + | ^~" + +"recode.sps:4.11-4.12: error: RECODE: Syntax error expecting number. + 4 | RECODE n1(**). + | ^~" + +"recode.sps:5.11-5.12: error: RECODE: Syntax error expecting string. + 5 | RECODE s1(**). + | ^~" + +"recode.sps:6.15-6.18: error: RECODE: THRU is not allowed with string variables. + 6 | RECODE s1('x' THRU 'y'). + | ^~~~" + +"recode.sps:7.13-7.14: error: RECODE: Syntax error expecting output value. + 7 | RECODE n1(1=**). + | ^~" + +"recode.sps:8.11-8.17: error: RECODE: CONVERT requires string input values. + 8 | RECODE n1(CONVERT). + | ^~~~~~~" + +recode.sps:9: error: RECODE: Output values must be all numeric or all string. + +"recode.sps:9.13: note: RECODE: This output value is numeric. + 9 | RECODE n1(1=2)(1='x'). + | ^" + +"recode.sps:9.18-9.20: note: RECODE: This output value is string. + 9 | RECODE n1(1=2)(1='x'). + | ^~~" + +recode.sps:10: error: RECODE: Output values must be all numeric or all string. + +"recode.sps:10.20: note: RECODE: This output value is numeric. + 10 | RECODE n1(1='x')(1=2). + | ^" + +"recode.sps:10.13-10.15: note: RECODE: This output value is string. + 10 | RECODE n1(1='x')(1=2). + | ^~~" + +recode.sps:11: error: RECODE: Output values must be all numeric or all string. + +"recode.sps:11.11-11.17: note: RECODE: This output value is numeric. + 11 | RECODE s1(CONVERT)('1'='1'). + | ^~~~~~~" + +"recode.sps:11.24-11.26: note: RECODE: This output value is string. + 11 | RECODE s1(CONVERT)('1'='1'). + | ^~~" + +"recode.sps:12.15-12.16: error: RECODE: Syntax error expecting `)'. + 12 | RECODE n1(1=2 **). + | ^~" + +"recode.sps:13.11-13.17: error: RECODE: CONVERT requires string input values. + 13 | RECODE n1(CONVERT). + | ^~~~~~~" + +recode.sps:14: error: RECODE: Source and target variable counts must match. + +"recode.sps:14.8-14.9: note: RECODE: There is 1 source variable. + 14 | RECODE s1(CONVERT) INTO n1 n2. + | ^~" + +"recode.sps:14.25-14.29: note: RECODE: There are 2 target variables. + 14 | RECODE s1(CONVERT) INTO n1 n2. + | ^~~~~" + +recode.sps:15: error: RECODE: All string variables specified on INTO must already exist. (Use the STRING command to create a string variable.) + +"recode.sps:15.23-15.27: note: RECODE: There is no variable named xyzzy. + 15 | RECODE n1(1='1') INTO xyzzy. + | ^~~~~" + +"recode.sps:16.10-16.16: error: RECODE: INTO is required with numeric input values and string output values. + 16 | RECODE n1(1='1'). + | ^~~~~~~" + +"recode.sps:17.10-17.16: error: RECODE: INTO is required with string input values and numeric output values. + 17 | RECODE s1('1'=1). + | ^~~~~~~" + +"recode.sps:18.23-18.24: error: RECODE: Type mismatch: cannot store string data in numeric variable n2. + 18 | RECODE n1(1='1') INTO n2. + | ^~" + +"recode.sps:19.25-19.26: error: RECODE: Type mismatch: cannot store numeric data in string variable s2. + 19 | RECODE s1(CONVERT) INTO s2. + | ^~" + +recode.sps:20: error: RECODE: At least one target variable is too narrow for the output values. + +"recode.sps:20.19-20.26: note: RECODE: This recoding output value has width 6. + 20 | RECODE n1 TO n4(1='123456') INTO s1 TO s4. + | ^~~~~~~~" + +"recode.sps:20.29-20.41: note: RECODE: Target variable s1 only has width 1. + 20 | RECODE n1 TO n4(1='123456') INTO s1 TO s4. + | ^~~~~~~~~~~~~" +]) +AT_CLEANUP \ No newline at end of file -- 2.30.2