From: Ben Pfaff Date: Mon, 27 Dec 2021 20:36:52 +0000 (-0800) Subject: basic parsing works X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=09385bf64ab49b52292eb9c81da34b4e654c4547 basic parsing works --- diff --git a/src/language/stats/ctables.c b/src/language/stats/ctables.c index deb98beb9c..585753664a 100644 --- a/src/language/stats/ctables.c +++ b/src/language/stats/ctables.c @@ -18,10 +18,12 @@ #include "data/dataset.h" #include "data/dictionary.h" +#include "data/mrset.h" #include "language/command.h" #include "language/lexer/format-parser.h" #include "language/lexer/lexer.h" #include "language/lexer/variable-parser.h" +#include "libpspp/assertion.h" #include "libpspp/hmap.h" #include "libpspp/message.h" #include "output/pivot-table.h" @@ -31,6 +33,7 @@ #include "gettext.h" #define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) enum ctables_vlabel { @@ -67,6 +70,7 @@ struct ctables format. Otherwise, this string is displayed. */ char *missing; + /* Indexed by variable dictionary index. */ enum ctables_vlabel *vlabels; bool mrsets_count_duplicates; /* MRSETS. */ @@ -218,89 +222,97 @@ struct ctables_axis static void ctables_axis_destroy (struct ctables_axis *); +enum ctables_format + { + CTF_COUNT, + CTF_PERCENT, + CTF_GENERAL + }; + #define SUMMARIES \ /* All variables. */ \ - S(CTSF_COUNT, "COUNT") \ - S(CTSF_ECOUNT, "ECOUNT") \ - S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT") \ - S(CTSF_COLPCT_COUNT, "COLPCT.COUNT") \ - S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT") \ - S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT") \ - S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT") \ - S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT") \ - S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT") \ - S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN") \ - S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN") \ - S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN") \ - S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN") \ - S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN") \ - S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN") \ - S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN") \ - S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN") \ - S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN") \ - S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN") \ - S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN") \ - S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN") \ - S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN") \ - S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN") \ + S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT) \ + S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT) \ + S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT) \ + S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT) \ + S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT) \ + S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT) \ + S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT) \ + S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT) \ \ /* Scale variables, totals, and subtotals. */ \ - S(CTSF_MAXIMUM, "!MAXIMUM") \ - S(CTSF_MEAN, "!MEAN") \ - S(CTSF_MEDIAN, "!MEDIAN") \ - S(CTSF_MINIMUM, "!MINIMUM") \ - S(CTSF_MISSING, "!MISSING") \ - S(CTSF_MODE, "!MODE") \ - S(CTSF_PTILE, "!PTILE") \ - S(CTSF_RANGE, "!RANGE") \ - S(CTSF_SEMAN, "!SEMAN") \ - S(CTSF_STDDEV, "!STDDEV") \ - S(CTSF_SUM, "!SUM") \ - S(CSTF_TOTALN, "!TOTALN") \ - S(CTSF_ETOTALN, "!ETOTALN") \ - S(CTSF_VALIDN, "!VALIDN") \ - S(CTSF_EVALIDN, "!EVALIDN") \ - S(CTSF_VARIANCE, "!VARIANCE") \ - S(CTSF_ROWPCT_SUM, "ROWPCT.SUM") \ - S(CTSF_COLPCT_SUM, "COLPCT.SUM") \ - S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM") \ - S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM") \ - S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM") \ - S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM") \ - S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM") \ + S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL) \ + S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL) \ + S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL) \ + S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL) \ + S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL) \ + S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL) \ + S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL) \ + S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL) \ + S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL) \ + S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL) \ + S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL) \ + S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT) \ + S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT) \ + S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT) \ + S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT) \ + S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL) \ + S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT) \ + S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT) \ \ /* Multiple response sets. */ \ - S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES") \ - S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES") \ - S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES") \ - S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES") \ - S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES") \ - S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES") \ - S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES") \ - S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT") \ - S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT") \ - S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT") \ - S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT") \ - S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT") \ - S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT") \ - S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT") \ - S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES") \ - S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES") \ - S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES") \ - S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES") \ - S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES") \ - S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES") \ - S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.COUNT.RESPONSES") + S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT) \ + S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT) \ + S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT) \ + S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT) \ + S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT) \ + S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT) enum ctables_summary_function { -#define S(ENUM, NAME) ENUM, +#define S(ENUM, NAME, LABEL, FORMAT) ENUM, SUMMARIES #undef S }; enum { -#define S(ENUM, NAME) +1 +#define S(ENUM, NAME, LABEL, FORMAT) +1 N_CTSF_FUNCTIONS = SUMMARIES #undef S }; @@ -308,6 +320,7 @@ enum { struct ctables_summary { enum ctables_summary_function function; + double percentile; /* CTSF_PTILE only. */ char *label; struct fmt_spec format; /* XXX extra CTABLES formats */ }; @@ -361,17 +374,17 @@ parse_ctables_summary_function (struct lexer *lexer, struct substring name; }; static struct pair names[] = { -#define S(ENUM, NAME) { ENUM, SS_LITERAL_INITIALIZER (NAME) }, +#define S(ENUM, NAME, LABEL, FORMAT) { ENUM, SS_LITERAL_INITIALIZER (NAME) }, SUMMARIES /* The .COUNT suffix may be omitted. */ - S(CTSF_ROWPCT_COUNT, "ROWPCT") - S(CTSF_COLPCT_COUNT, "COLPCT") - S(CTSF_TABLEPCT_COUNT, "TABLEPCT") - S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT") - S(CTSF_LAYERPCT_COUNT, "LAYERPCT") - S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT") - S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT") + S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _) + S(CTSF_COLPCT_COUNT, "COLPCT", _, _) + S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _) + S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _) + S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _) + S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _) + S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _) #undef S }; @@ -432,14 +445,55 @@ struct ctables_axis_parse_ctx }; static struct ctables_summary * -add_summary (struct ctables_axis *axis, size_t *allocated_summaries) +add_summary (struct ctables_axis *axis, enum ctables_summary_function function, + double percentile, size_t *allocated_summaries) { if (axis->n_summaries >= *allocated_summaries) axis->summaries = x2nrealloc (axis->summaries, allocated_summaries, sizeof *axis->summaries); + static const char *default_labels[] = { +#define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = LABEL, + SUMMARIES +#undef S + }; + char *label = (function == CTSF_PTILE + ? xasprintf (_("Percentile %.2f"), percentile) + : xstrdup (gettext (default_labels[function]))); + + static const enum ctables_format default_formats[] = { +#define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = FORMAT, + SUMMARIES +#undef S + }; + struct fmt_spec format; + switch (default_formats[function]) + { + case CTF_COUNT: + format = (struct fmt_spec) { .type = FMT_F, .w = 40 }; + break; + + case CTF_PERCENT: + format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 }; + break; + + case CTF_GENERAL: + format = *(axis->op == CTAO_VAR + ? var_get_print_format (axis->var) + : var_get_print_format (axis->mrset->vars[0])); + break; + + default: + NOT_REACHED (); + } + struct ctables_summary *s = &axis->summaries[axis->n_summaries++]; - *s = (struct ctables_summary) { .function = CTSF_COUNT }; + *s = (struct ctables_summary) { + .function = function, + .percentile = percentile, + .label = label, + .format = format, + }; return s; } @@ -502,11 +556,24 @@ ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx) { do { - struct ctables_summary *s = add_summary (axis, &allocated_summaries); - if (!parse_ctables_summary_function (ctx->lexer, &s->function)) + enum ctables_summary_function function; + if (!parse_ctables_summary_function (ctx->lexer, &function)) goto error; + + double percentile = 0; + if (function == CTSF_PTILE) + { + if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100)) + goto error; + percentile = lex_number (ctx->lexer); + lex_get (ctx->lexer); + } + + struct ctables_summary *s = add_summary (axis, function, percentile, + &allocated_summaries); if (lex_is_string (ctx->lexer)) { + free (s->label); s->label = ss_xstrdup (lex_tokss (ctx->lexer)); lex_get (ctx->lexer); } @@ -522,12 +589,8 @@ ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx) while (!lex_match (ctx->lexer, T_RBRACK)); } else - { - struct ctables_summary *s = add_summary (axis, &allocated_summaries); - s->function = axis->scale ? CTSF_MEAN : CTSF_COUNT; - s->label = xstrdup (axis->scale ? _("Mean") : _("Count")); - s->format = (struct fmt_spec) { .type = FMT_F, .w = 40 }; - } + add_summary (axis, axis->scale ? CTSF_MEAN : CTSF_COUNT, 0, + &allocated_summaries); return axis; error: @@ -542,7 +605,7 @@ ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx) if (!lhs) return NULL; - while (lex_match (ctx->lexer, T_PLUS)) + while (lex_match (ctx->lexer, T_GT)) { struct ctables_axis *rhs = ctables_axis_parse_primary (ctx); if (!rhs) @@ -593,12 +656,56 @@ ctables_axis_parse (struct lexer *lexer, struct dictionary *dict, return t->axes[a] != NULL; } +static void +ctables_chisq_destroy (struct ctables_chisq *chisq) +{ + free (chisq); +} + +static void +ctables_pairwise_destroy (struct ctables_pairwise *pairwise) +{ + free (pairwise); +} + +static void +ctables_table_uninit (struct ctables_table *t) +{ + if (!t) + return; + + ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]); + ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]); + ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]); + free (t->caption); + free (t->corner); + free (t->title); + ctables_chisq_destroy (t->chisq); + ctables_pairwise_destroy (t->pairwise); +} + +static void +ctables_destroy (struct ctables *ct) +{ + if (!ct) + return; + + pivot_table_look_unref (ct->look); + free (ct->zero); + free (ct->missing); + free (ct->vlabels); + for (size_t i = 0; i < ct->n_tables; i++) + ctables_table_uninit (&ct->tables[i]); + free (ct->tables); + free (ct); +} + int cmd_ctables (struct lexer *lexer, struct dataset *ds) { size_t n_vars = dict_get_n_vars (dataset_dict (ds)); enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels); - for (size_t i = 0; n_vars; i++) + for (size_t i = 0; i < n_vars; i++) vlabels[i] = CTVL_DEFAULT; struct ctables *ct = xmalloc (sizeof *ct); @@ -829,8 +936,10 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds) goto error; } } + if (lex_token (lexer) == T_ENDCMD) + break; if (!lex_force_match (lexer, T_SLASH)) - goto error; + break; /* XXX Validate axes. */ while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD) @@ -1161,11 +1270,11 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds) } } while (lex_token (lexer) != T_ENDCMD); - + ctables_destroy (ct); return CMD_SUCCESS; error: - /* XXX free */ + ctables_destroy (ct); return CMD_FAILURE; } diff --git a/tests/automake.mk b/tests/automake.mk index 7b5f9286be..e5d3819c47 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -390,6 +390,7 @@ TESTSUITE_AT = \ tests/language/stats/autorecode.at \ tests/language/stats/correlations.at \ tests/language/stats/crosstabs.at \ + tests/language/stats/ctables.at \ tests/language/stats/descriptives.at \ tests/language/stats/examine.at \ tests/language/stats/graph.at \