From: Ben Pfaff Date: Mon, 8 Nov 2021 01:27:19 +0000 (-0800) Subject: MATRIX SAVE positive tests. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed6bba5123261509fc0987660b309e82912d779e;p=pspp MATRIX SAVE positive tests. --- diff --git a/src/language/stats/matrix.c b/src/language/stats/matrix.c index 5214ee3d8b..bc9ae3363b 100644 --- a/src/language/stats/matrix.c +++ b/src/language/stats/matrix.c @@ -105,6 +105,20 @@ struct write_file struct u8_line *held; }; +struct save_file + { + struct file_handle *file; + + /* Parameters from parsing the first SAVE command for 'file'. */ + struct string_array variables; + struct matrix_expr *names; + struct stringi_set strings; + + /* Results from the first attempt to open this save_file. */ + bool error; + struct casewriter *writer; + }; + struct matrix_state { struct dataset *dataset; @@ -112,7 +126,6 @@ struct matrix_state struct lexer *lexer; struct hmap vars; bool in_loop; - struct file_handle *prev_save_outfile; struct msave_common *common; struct file_handle *prev_read_file; @@ -122,6 +135,10 @@ struct matrix_state struct file_handle *prev_write_file; struct write_file **write_files; size_t n_write_files; + + struct file_handle *prev_save_file; + struct save_file **save_files; + size_t n_save_files; }; static struct matrix_var * @@ -3391,10 +3408,7 @@ struct matrix_cmd struct save_command { struct matrix_expr *expression; - struct file_handle *outfile; - struct string_array *variables; - struct matrix_expr *names; - struct stringi_set strings; + struct save_file *sf; } save; @@ -3406,7 +3420,6 @@ struct matrix_cmd int c1, c2; enum fmt_type format; int w; - //int d; bool symmetric; bool reread; } @@ -4346,18 +4359,178 @@ matrix_cmd_execute_release (struct release_command *cmd) } } +static struct save_file * +save_file_create (struct matrix_state *s, struct file_handle *fh, + struct string_array *variables, + struct matrix_expr *names, + struct stringi_set *strings) +{ + for (size_t i = 0; i < s->n_save_files; i++) + { + struct save_file *sf = s->save_files[i]; + if (sf->file == fh) + { + fh_unref (fh); + + string_array_destroy (variables); + matrix_expr_destroy (names); + stringi_set_destroy (strings); + + return sf; + } + } + + struct save_file *sf = xmalloc (sizeof *sf); + *sf = (struct save_file) { + .file = fh, + .variables = *variables, + .names = names, + .strings = STRINGI_SET_INITIALIZER (sf->strings), + }; + + stringi_set_swap (&sf->strings, strings); + stringi_set_destroy (strings); + + s->save_files = xrealloc (s->save_files, + (s->n_save_files + 1) * sizeof *s->save_files); + s->save_files[s->n_save_files++] = sf; + return sf; +} + +static struct casewriter * +save_file_open (struct save_file *sf, gsl_matrix *m) +{ + if (sf->writer || sf->error) + { + if (sf->writer) + { + size_t n_variables = caseproto_get_n_widths ( + casewriter_get_proto (sf->writer)); + if (m->size2 != n_variables) + { + msg (ME, _("The first SAVE to %s within this matrix program " + "had %zu columns, so a %zu×%zu matrix cannot be " + "saved to it."), + fh_get_name (sf->file), n_variables, m->size1, m->size2); + return NULL; + } + } + return sf->writer; + } + + bool ok = true; + struct dictionary *dict = dict_create (get_default_encoding ()); + + /* Fill 'names' with user-specified names if there were any, either from + sf->variables or sf->names. */ + struct string_array names = { .n = 0 }; + if (sf->variables.n) + string_array_clone (&names, &sf->variables); + else if (sf->names) + { + gsl_matrix *nm = matrix_expr_evaluate (sf->names); + if (nm && is_vector (nm)) + { + gsl_vector nv = to_vector (nm); + for (size_t i = 0; i < nv.size; i++) + { + char *name = trimmed_string (gsl_vector_get (&nv, i)); + if (dict_id_is_valid (dict, name, true)) + string_array_append_nocopy (&names, name); + else + ok = false; + } + } + gsl_matrix_free (nm); + } + + struct stringi_set strings; + stringi_set_clone (&strings, &sf->strings); + + for (size_t i = 0; dict_get_var_cnt (dict) < m->size2; i++) + { + char tmp_name[64]; + const char *name; + if (i < names.n) + name = names.strings[i]; + else + { + snprintf (tmp_name, sizeof tmp_name, "COL%zu", i + 1); + name = tmp_name; + } + + int width = stringi_set_delete (&strings, name) ? 8 : 0; + struct variable *var = dict_create_var (dict, name, width); + if (!var) + { + msg (ME, _("Duplicate variable name %s in SAVE statement."), + name); + ok = false; + } + } + + if (!stringi_set_is_empty (&strings)) + { + const char *example = stringi_set_node_get_string ( + stringi_set_first (&strings)); + msg (ME, ngettext ("STRINGS specified a variable %s, but no variable " + "with that name was found on SAVE.", + "STRINGS specified %2$zu variables, including %1$s, " + "whose names were not found on SAVE.", + stringi_set_count (&strings)), + example, stringi_set_count (&strings)); + ok = false; + } + stringi_set_destroy (&strings); + string_array_destroy (&names); + + if (!ok) + { + dict_unref (dict); + sf->error = true; + return NULL; + } + + sf->writer = any_writer_open (sf->file, dict); + dict_unref (dict); + if (!sf->writer) + { + sf->error = true; + return NULL; + } + return sf->writer; +} + +static void +save_file_destroy (struct save_file *sf) +{ + if (sf) + { + fh_unref (sf->file); + string_array_destroy (&sf->variables); + matrix_expr_destroy (sf->names); + stringi_set_destroy (&sf->strings); + casewriter_destroy (sf->writer); + free (sf); + } +} + static struct matrix_cmd * matrix_parse_save (struct matrix_state *s) { struct matrix_cmd *cmd = xmalloc (sizeof *cmd); *cmd = (struct matrix_cmd) { .type = MCMD_SAVE, - .save = { - .strings = STRINGI_SET_INITIALIZER (cmd->save.strings) - } + .save = { .expression = NULL }, }; + struct file_handle *fh = NULL; struct save_command *save = &cmd->save; + + struct string_array variables = STRING_ARRAY_INITIALIZER; + struct matrix_expr *names = NULL; + struct stringi_set strings = STRINGI_SET_INITIALIZER (strings); + save->expression = matrix_parse_exp (s); if (!save->expression) goto error; @@ -4368,11 +4541,11 @@ matrix_parse_save (struct matrix_state *s) { lex_match (s->lexer, T_EQUALS); - fh_unref (save->outfile); - save->outfile = (lex_match (s->lexer, T_ASTERISK) - ? fh_inline_file () - : fh_parse (s->lexer, FH_REF_FILE, s->session)); - if (!save->outfile) + fh_unref (fh); + fh = (lex_match (s->lexer, T_ASTERISK) + ? fh_inline_file () + : fh_parse (s->lexer, FH_REF_FILE, s->session)); + if (!fh) goto error; } else if (lex_match_id (s->lexer, "VARIABLES")) @@ -4388,10 +4561,8 @@ matrix_parse_save (struct matrix_state *s) if (!ok) goto error; - string_array_destroy (save->variables); - if (!save->variables) - save->variables = xmalloc (sizeof *save->variables); - *save->variables = (struct string_array) { + string_array_clear (&variables); + variables = (struct string_array) { .strings = names, .n = n, .allocated = n, @@ -4400,9 +4571,9 @@ matrix_parse_save (struct matrix_state *s) else if (lex_match_id (s->lexer, "NAMES")) { lex_match (s->lexer, T_EQUALS); - matrix_expr_destroy (save->names); - save->names = matrix_parse_exp (s); - if (!save->names) + matrix_expr_destroy (names); + names = matrix_parse_exp (s); + if (!names) goto error; } else if (lex_match_id (s->lexer, "STRINGS")) @@ -4410,7 +4581,7 @@ matrix_parse_save (struct matrix_state *s) lex_match (s->lexer, T_EQUALS); while (lex_token (s->lexer) == T_ID) { - stringi_set_insert (&save->strings, lex_tokcstr (s->lexer)); + stringi_set_insert (&strings, lex_tokcstr (s->lexer)); lex_get (s->lexer); if (!lex_match (s->lexer, T_COMMA)) break; @@ -4424,29 +4595,36 @@ matrix_parse_save (struct matrix_state *s) } } - if (!save->outfile) + if (!fh) { - if (s->prev_save_outfile) - save->outfile = fh_ref (s->prev_save_outfile); + if (s->prev_save_file) + fh = fh_ref (s->prev_save_file); else { lex_sbc_missing ("OUTFILE"); goto error; } } - fh_unref (s->prev_save_outfile); - s->prev_save_outfile = fh_ref (save->outfile); + fh_unref (s->prev_save_file); + s->prev_save_file = fh_ref (fh); - if (save->variables && save->names) + if (variables.n && names) { msg (SW, _("VARIABLES and NAMES both specified; ignoring NAMES.")); - matrix_expr_destroy (save->names); - save->names = NULL; + matrix_expr_destroy (names); + names = NULL; } + save->sf = save_file_create (s, fh, &variables, names, &strings); + fh = NULL; + return cmd; error: + string_array_destroy (&variables); + matrix_expr_destroy (names); + stringi_set_destroy (&strings); + fh_unref (fh); matrix_cmd_destroy (cmd); return NULL; } @@ -4454,90 +4632,18 @@ error: static void matrix_cmd_execute_save (const struct save_command *save) { - assert (save->outfile != fh_inline_file ()); /* XXX not yet implemented */ - gsl_matrix *m = matrix_expr_evaluate (save->expression); if (!m) return; - bool ok = true; - struct dictionary *dict = dict_create (get_default_encoding ()); - struct string_array names = { .n = 0 }; - if (save->variables) - string_array_clone (&names, save->variables); - else if (save->names) - { - gsl_matrix *nm = matrix_expr_evaluate (save->names); - if (nm && is_vector (nm)) - { - gsl_vector nv = to_vector (nm); - for (size_t i = 0; i < nv.size; i++) - { - char *name = trimmed_string (gsl_vector_get (&nv, i)); - if (dict_id_is_valid (dict, name, true)) - string_array_append_nocopy (&names, name); - else - ok = false; - } - } - } - - struct stringi_set strings; - stringi_set_clone (&strings, &save->strings); - - for (size_t i = 0; dict_get_var_cnt (dict) < m->size2; i++) - { - char tmp_name[64]; - const char *name; - if (i < names.n) - name = names.strings[i]; - else - { - snprintf (tmp_name, sizeof tmp_name, "COL%zu", i + 1); - name = tmp_name; - } - - int width = stringi_set_delete (&strings, name) ? 8 : 0; - struct variable *var = dict_create_var (dict, name, width); - if (!var) - { - msg (SE, _("Duplicate variable name %s in SAVE statement."), - name); - ok = false; - } - } - - if (!stringi_set_is_empty (&strings)) - { - const char *example = stringi_set_node_get_string ( - stringi_set_first (&strings)); - msg (SE, ngettext ("STRINGS specified a variable %s, but no variable " - "with that name was found on SAVE.", - "STRINGS specified %2$zu variables, including %1$s, " - "whose names were not found on SAVE.", - stringi_set_count (&strings)), - example, stringi_set_count (&strings)); - ok = false; - } - stringi_set_destroy (&strings); - string_array_destroy (&names); - - if (!ok) - { - gsl_matrix_free (m); - dict_unref (dict); - return; - } - - struct casewriter *writer = any_writer_open (save->outfile, dict); + struct casewriter *writer = save_file_open (save->sf, m); if (!writer) { gsl_matrix_free (m); - dict_unref (dict); return; } - const struct caseproto *proto = dict_get_proto (dict); + const struct caseproto *proto = casewriter_get_proto (writer); for (size_t y = 0; y < m->size1; y++) { struct ccase *c = case_create (proto); @@ -4553,10 +4659,7 @@ matrix_cmd_execute_save (const struct save_command *save) } casewriter_write (writer, c); } - casewriter_destroy (writer); - gsl_matrix_free (m); - dict_unref (dict); } static struct matrix_cmd * @@ -6628,10 +6731,6 @@ matrix_cmd_destroy (struct matrix_cmd *cmd) case MCMD_SAVE: matrix_expr_destroy (cmd->save.expression); - fh_unref (cmd->save.outfile); - string_array_destroy (cmd->save.variables); - matrix_expr_destroy (cmd->save.names); - stringi_set_destroy (&cmd->save.strings); break; case MCMD_READ: @@ -6785,7 +6884,6 @@ cmd_matrix (struct lexer *lexer, struct dataset *ds) free (var); } hmap_destroy (&state.vars); - fh_unref (state.prev_save_outfile); if (state.common) { dict_unref (state.common->dict); @@ -6800,6 +6898,10 @@ cmd_matrix (struct lexer *lexer, struct dataset *ds) for (size_t i = 0; i < state.n_write_files; i++) write_file_destroy (state.write_files[i]); free (state.write_files); + fh_unref (state.prev_save_file); + for (size_t i = 0; i < state.n_save_files; i++) + save_file_destroy (state.save_files[i]); + free (state.save_files); return CMD_SUCCESS; } diff --git a/tests/language/stats/matrix.at b/tests/language/stats/matrix.at index 306669e271..d7966f415b 100644 --- a/tests/language/stats/matrix.at +++ b/tests/language/stats/matrix.at @@ -3096,3 +3096,35 @@ matrix.sps:25: error: MATRIX: GET: Variable d is not numeric. error: GET cannot read empty active file. ]) AT_CLEANUP + +AT_SETUP([MATRIX - SAVE]) +AT_DATA([matrix.sps], [dnl +MATRIX. +SAVE {1,2,3; 4,5,6}/OUTFILE='matrix.sav'. +SAVE {7,8,9}/VARIABLES=a b c d. + +SAVE {1,2,3}/OUTFILE='matrix2.sav'/VARIABLES=v01 TO v03. +SAVE {4,5,6}/NAMES={'x', 'y', 'z', 'w'}. + +SAVE {1,'abcd',3}/OUTFILE='matrix3.sav'/NAMES={'a', 'b', 'c'}/STRINGS=b. +SAVE {4,'xyzw',6}/STRINGS=a, b. +END MATRIX. +]) +AT_CHECK([pspp matrix.sps]) +AT_CHECK([pspp-convert matrix.sav matrix.csv && cat matrix.csv], [0], [dnl +COL1,COL2,COL3 +1,2,3 +4,5,6 +7,8,9 +]) +AT_CHECK([pspp-convert matrix2.sav matrix2.csv && cat matrix2.csv], [0], [dnl +v01,v02,v03 +1,2,3 +4,5,6 +]) +AT_CHECK([pspp-convert matrix3.sav matrix3.csv && cat matrix3.csv], [0], [dnl +a,b,c +1,abcd,3 +4,xyzw,6 +]) +AT_CLEANUP \ No newline at end of file