struct string_array variables;
struct string_array fnames;
struct string_array snames;
- bool has_factors;
- bool has_splits;
size_t n_varnames;
+ /* Collects and owns factors and splits. The individual msave_command
+ structs point to these but do not own them. */
+ struct matrix_expr **factors;
+ size_t n_factors, allocated_factors;
+ struct matrix_expr **splits;
+ size_t n_splits, allocated_splits;
+
/* Execution state. */
struct dictionary *dict;
struct casewriter *writer;
assert (!v.owner);
v.owner = 1;
m->owner = 0;
+ gsl_matrix_free (m);
return xmemdup (&v, sizeof v);
}
struct msave_command
{
struct msave_common *common;
- char *varname_;
struct matrix_expr *expr;
const char *rowtype;
- struct matrix_expr *factors;
- struct matrix_expr *splits;
+ const struct matrix_expr *factors;
+ const struct matrix_expr *splits;
}
msave;
string_array_clear (sa);
struct dictionary *dict = dict_create (get_default_encoding ());
- dict_create_var_assert (dict, "ROWTYPE_", 8);
- dict_create_var_assert (dict, "VARNAME_", 8);
char **names;
size_t n_names;
bool ok = parse_DATA_LIST_vars (lexer, dict, &names, &n_names,
if (ok)
{
+ for (size_t i = 0; i < n_names; i++)
+ if (ss_equals_case (ss_cstr (names[i]), ss_cstr ("ROWTYPE_"))
+ || ss_equals_case (ss_cstr (names[i]), ss_cstr ("VARNAME_")))
+ {
+ msg (SE, _("Variable name %s is reserved."), names[i]);
+ for (size_t j = 0; j < n_names; j++)
+ free (names[i]);
+ free (names);
+ return false;
+ }
+
string_array_clear (sa);
sa->strings = names;
sa->n = sa->allocated = n_names;
}
static void
-msave_common_uninit (struct msave_common *common)
+msave_common_destroy (struct msave_common *common)
{
if (common)
{
string_array_destroy (&common->variables);
string_array_destroy (&common->fnames);
string_array_destroy (&common->snames);
+
+ for (size_t i = 0; i < common->n_factors; i++)
+ matrix_expr_destroy (common->factors[i]);
+ free (common->factors);
+
+ for (size_t i = 0; i < common->n_splits; i++)
+ matrix_expr_destroy (common->splits[i]);
+ free (common->splits);
+
+ dict_unref (common->dict);
+ casewriter_destroy (common->writer);
+
+ free (common);
}
}
static struct matrix_cmd *
matrix_parse_msave (struct matrix_state *s)
{
- struct msave_common common = { .outfile = NULL };
+ struct msave_common *common = xmalloc (sizeof *common);
+ *common = (struct msave_common) { .outfile = NULL };
+
struct matrix_cmd *cmd = xmalloc (sizeof *cmd);
*cmd = (struct matrix_cmd) { .type = MCMD_MSAVE, .msave = { .expr = NULL } };
+ struct matrix_expr *splits = NULL;
+ struct matrix_expr *factors = NULL;
+
struct msave_command *msave = &cmd->msave;
- if (lex_token (s->lexer) == T_ID)
- msave->varname_ = ss_xstrdup (lex_tokss (s->lexer));
msave->expr = matrix_parse_exp (s);
if (!msave->expr)
- return NULL;
+ goto error;
while (lex_match (s->lexer, T_SLASH))
{
{
lex_match (s->lexer, T_EQUALS);
- fh_unref (common.outfile);
- common.outfile = fh_parse (s->lexer, FH_REF_FILE, NULL);
- if (!common.outfile)
+ fh_unref (common->outfile);
+ common->outfile = fh_parse (s->lexer, FH_REF_FILE, NULL);
+ if (!common->outfile)
goto error;
}
else if (lex_match_id (s->lexer, "VARIABLES"))
{
- if (!parse_var_names (s->lexer, &common.variables))
+ if (!parse_var_names (s->lexer, &common->variables))
goto error;
}
else if (lex_match_id (s->lexer, "FNAMES"))
{
- if (!parse_var_names (s->lexer, &common.fnames))
+ if (!parse_var_names (s->lexer, &common->fnames))
goto error;
}
else if (lex_match_id (s->lexer, "SNAMES"))
{
- if (!parse_var_names (s->lexer, &common.snames))
+ if (!parse_var_names (s->lexer, &common->snames))
goto error;
}
else if (lex_match_id (s->lexer, "SPLIT"))
{
lex_match (s->lexer, T_EQUALS);
- matrix_expr_destroy (msave->splits);
- msave->splits = matrix_parse_exp (s);
- if (!msave->splits)
+ matrix_expr_destroy (splits);
+ splits = matrix_parse_exp (s);
+ if (!splits)
goto error;
}
else if (lex_match_id (s->lexer, "FACTOR"))
{
lex_match (s->lexer, T_EQUALS);
- matrix_expr_destroy (msave->factors);
- msave->factors = matrix_parse_exp (s);
- if (!msave->factors)
+ matrix_expr_destroy (factors);
+ factors = matrix_parse_exp (s);
+ if (!factors)
goto error;
}
else
lex_sbc_missing ("TYPE");
goto error;
}
- common.has_splits = msave->splits || common.snames.n;
- common.has_factors = msave->factors || common.fnames.n;
-
- struct msave_common *c = s->common ? s->common : &common;
- if (c->fnames.n && !msave->factors)
- {
- msg (SE, _("FNAMES requires FACTOR."));
- goto error;
- }
- if (c->snames.n && !msave->splits)
- {
- msg (SE, _("SNAMES requires SPLIT."));
- goto error;
- }
- if (c->has_factors && !common.has_factors)
- {
- msg (SE, _("%s is required because it was present on the first "
- "MSAVE in this MATRIX command."), "FACTOR");
- goto error;
- }
- if (c->has_splits && !common.has_splits)
- {
- msg (SE, _("%s is required because it was present on the first "
- "MSAVE in this MATRIX command."), "SPLIT");
- goto error;
- }
if (!s->common)
{
- if (!common.outfile)
+ if (common->fnames.n && !factors)
+ {
+ msg (SE, _("FNAMES requires FACTOR."));
+ goto error;
+ }
+ if (common->snames.n && !splits)
+ {
+ msg (SE, _("SNAMES requires SPLIT."));
+ goto error;
+ }
+ if (!common->outfile)
{
lex_sbc_missing ("OUTFILE");
goto error;
}
- s->common = xmemdup (&common, sizeof common);
+ s->common = common;
}
else
{
- if (common.outfile)
+ if (common->outfile)
{
- bool same = common.outfile == s->common->outfile;
- fh_unref (common.outfile);
- if (!same)
+ if (!fh_equal (common->outfile, s->common->outfile))
{
msg (SE, _("OUTFILE must name the same file on each MSAVE "
"within a single MATRIX command."));
}
}
if (!compare_variables ("VARIABLES",
- &common.variables, &s->common->variables)
- || !compare_variables ("FNAMES", &common.fnames, &s->common->fnames)
- || !compare_variables ("SNAMES", &common.snames, &s->common->snames))
+ &common->variables, &s->common->variables)
+ || !compare_variables ("FNAMES", &common->fnames, &s->common->fnames)
+ || !compare_variables ("SNAMES", &common->snames, &s->common->snames))
goto error;
- msave_common_uninit (&common);
+ msave_common_destroy (common);
}
msave->common = s->common;
- if (!msave->varname_)
- msave->varname_ = xasprintf ("MAT%zu", ++s->common->n_varnames);
+
+ struct msave_common *c = s->common;
+ if (factors)
+ {
+ if (c->n_factors >= c->allocated_factors)
+ c->factors = x2nrealloc (c->factors, &c->allocated_factors,
+ sizeof *c->factors);
+ c->factors[c->n_factors++] = factors;
+ }
+ if (c->n_factors > 0)
+ msave->factors = c->factors[c->n_factors - 1];
+
+ if (splits)
+ {
+ if (c->n_splits >= c->allocated_splits)
+ c->splits = x2nrealloc (c->splits, &c->allocated_splits,
+ sizeof *c->splits);
+ c->splits[c->n_splits++] = splits;
+ }
+ if (c->n_splits > 0)
+ msave->splits = c->splits[c->n_splits - 1];
+
return cmd;
error:
- msave_common_uninit (&common);
+ matrix_expr_destroy (splits);
+ matrix_expr_destroy (factors);
+ msave_common_destroy (common);
matrix_cmd_destroy (cmd);
return NULL;
}
static gsl_vector *
-matrix_expr_evaluate_vector (struct matrix_expr *e, const char *name)
+matrix_expr_evaluate_vector (const struct matrix_expr *e, const char *name)
{
gsl_matrix *m = matrix_expr_evaluate (e);
if (!m)
const char *dup_split = msave_add_vars (dict, &common->snames);
if (dup_split)
{
- msg (SE, _("Duplicate SPLIT variable name %s."), dup_split);
- goto error;
+ /* Should not be possible because the parser ensures that the names are
+ unique. */
+ NOT_REACHED ();
}
dict_create_var_assert (dict, "ROWTYPE_", 8);
for (size_t i = 0; i < m->size2; i++)
string_array_append_nocopy (&common->variables,
xasprintf ("COL%zu", i + 1));
-
- if (m->size2 != common->variables.n)
+ else if (m->size2 != common->variables.n)
{
- msg (SE, _("Matrix on MSAVE has %zu columns instead of required %zu."),
+ msg (SE,
+ _("Matrix on MSAVE has %zu columns but there are %zu variables."),
m->size2, common->variables.n);
goto error;
}
for (size_t i = 0; i < factors->size; i++)
string_array_append_nocopy (&common->fnames,
xasprintf ("FAC%zu", i + 1));
-
- if (factors->size != common->fnames.n)
+ else if (factors->size != common->fnames.n)
{
msg (SE, _("There are %zu factor variables, "
- "but %zu split values were supplied."),
+ "but %zu factor values were supplied."),
common->fnames.n, factors->size);
goto error;
}
if (!splits)
goto error;
- if (!common->fnames.n)
+ if (!common->snames.n)
for (size_t i = 0; i < splits->size; i++)
- string_array_append_nocopy (&common->fnames,
+ string_array_append_nocopy (&common->snames,
xasprintf ("SPL%zu", i + 1));
-
- if (splits->size != common->fnames.n)
+ else if (splits->size != common->snames.n)
{
msg (SE, _("There are %zu split variables, "
"but %zu split values were supplied."),
- common->fnames.n, splits->size);
+ common->snames.n, splits->size);
goto error;
}
}
common->dict = dict;
}
+ bool matrix = (!strcmp (msave->rowtype, "COV")
+ || !strcmp (msave->rowtype, "CORR"));
for (size_t y = 0; y < m->size1; y++)
{
struct ccase *c = case_create (dict_get_proto (common->dict));
*case_num_rw_idx (c, idx++) = gsl_vector_get (factors, i);
/* VARNAME_. */
+ const char *varname_ = (matrix && y < common->variables.n
+ ? common->variables.strings[y]
+ : "");
buf_copy_str_rpad (CHAR_CAST (char *, case_data_rw_idx (c, idx++)->s), 8,
- msave->varname_, ' ');
+ varname_, ' ');
/* Continuous variables. */
for (size_t x = 0; x < m->size2; x++)
break;
case MCMD_MSAVE:
- free (cmd->msave.varname_);
matrix_expr_destroy (cmd->msave.expr);
- matrix_expr_destroy (cmd->msave.factors);
- matrix_expr_destroy (cmd->msave.splits);
break;
case MCMD_MGET:
free (var);
}
hmap_destroy (&state.vars);
- if (state.common)
- {
- dict_unref (state.common->dict);
- casewriter_destroy (state.common->writer);
- free (state.common);
- }
+ msave_common_destroy (state.common);
fh_unref (state.prev_read_file);
for (size_t i = 0; i < state.n_read_files; i++)
read_file_destroy (state.read_files[i]);
0 0
])
AT_CLEANUP
+
+AT_SETUP([MATRIX - MSAVE])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/VARIABLES=X,Y/OUTFILE='matrix.sav'.
+MSAVE {5, 6; 7, 8; 9, 10}/TYPE=COV/VARIABLES=X,Y.
+MSAVE {11, 12}/TYPE=MEAN.
+MSAVE {13, 14}/TYPE=STDDEV.
+MSAVE {15, 16}/TYPE=N.
+MSAVE {17, 18}/TYPE=COUNT.
+END MATRIX.
+GET 'matrix.sav'.
+LIST.
+])
+AT_CHECK([pspp matrix.sps -O format=csv], [0], [dnl
+Table: Data List
+ROWTYPE_,VARNAME_,X,Y
+CORR,X,1.00,2.00
+CORR,Y,3.00,4.00
+COV,X,5.00,6.00
+COV,Y,7.00,8.00
+COV,,9.00,10.00
+MEAN,,11.00,12.00
+STDDEV,,13.00,14.00
+N,,15.00,16.00
+COUNT,,17.00,18.00
+])
+AT_CLEANUP
+
+AT_SETUP([MATRIX - MSAVE with factor variables])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/FACTOR={1,1}/FNAMES=X,Y/OUTFILE='matrix.sav'.
+MSAVE {5, 6; 7, 8; 9, 10}/TYPE=COV.
+MSAVE {11, 12}/TYPE=MEAN.
+MSAVE {13, 14}/FACTOR={2,1}/TYPE=STDDEV.
+MSAVE {15, 16}/TYPE=N.
+MSAVE {17, 18}/FACTOR={1,2}/TYPE=COUNT.
+END MATRIX.
+GET 'matrix.sav'.
+LIST.
+
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/FACTOR={5,6,7,8}/OUTFILE='matrix2.sav'.
+END MATRIX.
+GET 'matrix2.sav'.
+LIST.
+])
+AT_CHECK([pspp matrix.sps -O format=csv], [0], [dnl
+Table: Data List
+ROWTYPE_,X,Y,VARNAME_,COL1,COL2
+CORR,1.00,1.00,COL1,1.00,2.00
+CORR,1.00,1.00,COL2,3.00,4.00
+COV,1.00,1.00,COL1,5.00,6.00
+COV,1.00,1.00,COL2,7.00,8.00
+COV,1.00,1.00,,9.00,10.00
+MEAN,1.00,1.00,,11.00,12.00
+STDDEV,2.00,1.00,,13.00,14.00
+N,2.00,1.00,,15.00,16.00
+COUNT,1.00,2.00,,17.00,18.00
+
+Table: Data List
+ROWTYPE_,FAC1,FAC2,FAC3,FAC4,VARNAME_,COL1,COL2
+CORR,5.00,6.00,7.00,8.00,COL1,1.00,2.00
+CORR,5.00,6.00,7.00,8.00,COL2,3.00,4.00
+])
+AT_CLEANUP
+
+AT_SETUP([MATRIX - MSAVE with split variables])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/SPLIT={1,1}/SNAMES=X,Y/OUTFILE='matrix.sav'.
+MSAVE {5, 6; 7, 8; 9, 10}/TYPE=COV.
+MSAVE {11, 12}/TYPE=MEAN.
+MSAVE {13, 14}/SPLIT={2,1}/TYPE=STDDEV.
+MSAVE {15, 16}/TYPE=N.
+MSAVE {17, 18}/SPLIT={1,2}/TYPE=COUNT.
+END MATRIX.
+GET 'matrix.sav'.
+LIST.
+
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/SPLIT={5,6,7,8}/OUTFILE='matrix2.sav'.
+END MATRIX.
+GET 'matrix2.sav'.
+LIST.
+])
+AT_CHECK([pspp matrix.sps -O format=csv], [0], [dnl
+Table: Data List
+X,Y,ROWTYPE_,VARNAME_,COL1,COL2
+1.00,1.00,CORR,COL1,1.00,2.00
+1.00,1.00,CORR,COL2,3.00,4.00
+1.00,1.00,COV,COL1,5.00,6.00
+1.00,1.00,COV,COL2,7.00,8.00
+1.00,1.00,COV,,9.00,10.00
+1.00,1.00,MEAN,,11.00,12.00
+2.00,1.00,STDDEV,,13.00,14.00
+2.00,1.00,N,,15.00,16.00
+1.00,2.00,COUNT,,17.00,18.00
+
+Table: Data List
+SPL1,SPL2,SPL3,SPL4,ROWTYPE_,VARNAME_,COL1,COL2
+5.00,6.00,7.00,8.00,CORR,COL1,1.00,2.00
+5.00,6.00,7.00,8.00,CORR,COL2,3.00,4.00
+])
+AT_CLEANUP
+
+AT_SETUP([MATRIX - MSAVE with factor and split variables])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+MSAVE {1, 2; 3, 4}/TYPE=CORR/SPLIT=1/FACTOR=1/OUTFILE='matrix.sav'.
+MSAVE {5, 6; 7, 8; 9, 10}/TYPE=COV.
+MSAVE {11, 12}/FACTOR=2/TYPE=MEAN.
+MSAVE {13, 14}/FACTOR=1/SPLIT=2/TYPE=STDDEV.
+MSAVE {15, 16}/TYPE=N.
+MSAVE {17, 18}/FACTOR=2/TYPE=COUNT.
+END MATRIX.
+GET 'matrix.sav'.
+LIST.
+])
+AT_CHECK([pspp matrix.sps -O format=csv], [0], [dnl
+Table: Data List
+SPL1,ROWTYPE_,FAC1,VARNAME_,COL1,COL2
+1.00,CORR,1.00,COL1,1.00,2.00
+1.00,CORR,1.00,COL2,3.00,4.00
+1.00,COV,1.00,COL1,5.00,6.00
+1.00,COV,1.00,COL2,7.00,8.00
+1.00,COV,1.00,,9.00,10.00
+1.00,MEAN,2.00,,11.00,12.00
+2.00,STDDEV,1.00,,13.00,14.00
+2.00,N,1.00,,15.00,16.00
+2.00,COUNT,2.00,,17.00,18.00
+])
+AT_CLEANUP
+
+AT_SETUP([MATRIX - MSAVE - negative])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+MSAVE !.
+MSAVE 1/TYPE=!.
+MSAVE 1/OUTFILE=!.
+MSAVE 1/VARIABLES=!.
+MSAVE 1/FNAMES=!.
+MSAVE 1/SNAMES=!.
+MSAVE 1/SPLIT=!.
+MSAVE 1/FACTOR=!.
+MSAVE 1/!.
+MSAVE 1.
+MSAVE 1/TYPE=COV/FNAMES=x.
+MSAVE 1/TYPE=COV/SNAMES=x.
+MSAVE 1/TYPE=COV.
+
+MSAVE 1/TYPE=COV/OUTFILE='matrix.sav'
+ /FACTOR=1 /FNAMES=y
+ /SPLIT=2 /SNAMES=z
+ /VARIABLES=w.
+MSAVE 1/TYPE=COV/OUTFILE='matrix2.sav'.
+MSAVE 1/TYPE=COV/VARIABLES=x.
+MSAVE 1/TYPE=COV/FNAMES=x.
+MSAVE 1/TYPE=COV/SNAMES=x.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/VARIABLES=x/OUTFILE='matrix3.sav'/FACTOR=1/SPLIT=2.
+MSAVE {1,2}/TYPE=COV/VARIABLES=x/OUTFILE='matrix3.sav'/FACTOR=1/SPLIT=2.
+MSAVE {1,2;3}/TYPE=COV.
+MSAVE 0/TYPE=COV/FACTOR={1,2}.
+MSAVE 0/TYPE=COV/FACTOR=1/SPLIT={1;2}.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/OUTFILE='matrix4.sav'/SNAMES=x,x/SPLIT=1.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/OUTFILE='matrix5.sav'/SNAMES=x/FNAMES=x/SPLIT=1/FACTOR=1.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/OUTFILE='matrix6.sav'/VARIABLES=x/FNAMES=x/FACTOR=1.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/OUTFILE='matrix6.sav'/VARIABLES=x/SNAMES=x/SPLIT=1.
+END MATRIX.
+
+MATRIX.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/SNAMES=VARNAME_.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/SNAMES=ROWTYPE_.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/FNAMES=VARNAME_.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/FNAMES=ROWTYPE_.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/VARIABLES=VARNAME_.
+MSAVE 1/TYPE=COV/OUTFILE='matrix7.sav'/VARIABLES=ROWTYPE_.
+END MATRIX.
+])
+AT_CHECK([pspp matrix.sps], [1], [dnl
+matrix.sps:2.7: error: MSAVE: Syntax error at `!'.
+
+matrix.sps:3.14: error: MSAVE: Syntax error at `!': expecting COV, CORR, MEAN,
+STDDEV, N, or COUNT.
+
+matrix.sps:4.17: error: MSAVE: Syntax error at `!': expecting a file name or
+handle name.
+
+matrix.sps:5.19: error: MSAVE: Syntax error at `!': expecting variable name.
+
+matrix.sps:6.16: error: MSAVE: Syntax error at `!': expecting variable name.
+
+matrix.sps:7.16: error: MSAVE: Syntax error at `!': expecting variable name.
+
+matrix.sps:8.15: error: MSAVE: Syntax error at `!'.
+
+matrix.sps:9.16: error: MSAVE: Syntax error at `!'.
+
+matrix.sps:10.9: error: MSAVE: Syntax error at `!': expecting TYPE, OUTFILE,
+VARIABLES, FNAMES, SNAMES, SPLIT, or FACTOR.
+
+matrix.sps:11: error: MSAVE: Required subcommand TYPE was not specified.
+
+matrix.sps:12: error: MSAVE: FNAMES requires FACTOR.
+
+matrix.sps:13: error: MSAVE: SNAMES requires SPLIT.
+
+matrix.sps:14: error: MSAVE: Required subcommand OUTFILE was not specified.
+
+matrix.sps:20: error: MSAVE: OUTFILE must name the same file on each MSAVE
+within a single MATRIX command.
+
+matrix.sps:21: error: MSAVE: VARIABLES must specify the same variables each
+time within a given MATRIX.
+
+matrix.sps:22: error: MSAVE: FNAMES must specify the same variables each time
+within a given MATRIX.
+
+matrix.sps:23: error: MSAVE: SNAMES must specify the same variables each time
+within a given MATRIX.
+
+matrix.sps:28: error: MATRIX: Matrix on MSAVE has 2 columns but there are 1
+variables.
+
+matrix.sps:29: error: MATRIX: All rows in a matrix must have the same number of
+columns, but this tries to stack matrices with 2 and 1 columns.
+
+matrix.sps:30: error: MATRIX: There are 1 factor variables, but 2 factor values
+were supplied.
+
+matrix.sps:31: error: MATRIX: There are 1 split variables, but 2 split values
+were supplied.
+
+matrix.sps:35: error: MSAVE: Variable x appears twice in variable list.
+
+matrix.sps:39: error: MATRIX: Duplicate or invalid FACTOR variable name x.
+
+matrix.sps:43: error: MATRIX: Duplicate or invalid variable name x.
+
+matrix.sps:47: error: MATRIX: Duplicate or invalid variable name x.
+
+matrix.sps:51: error: MSAVE: Variable name VARNAME_ is reserved.
+
+matrix.sps:52: error: MSAVE: Variable name ROWTYPE_ is reserved.
+
+matrix.sps:53: error: MSAVE: Variable name VARNAME_ is reserved.
+
+matrix.sps:54: error: MSAVE: Variable name ROWTYPE_ is reserved.
+
+matrix.sps:55: error: MSAVE: Variable name VARNAME_ is reserved.
+
+matrix.sps:56: error: MSAVE: Variable name ROWTYPE_ is reserved.
+])
+AT_CLEANUP
\ No newline at end of file