From 07cbfcefba94a3e525a757f0809c20ffd2705c63 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 27 Dec 2021 18:43:03 -0800 Subject: [PATCH] more validation --- src/language/stats/ctables.c | 217 +++++++++++++++++++++++++---------- 1 file changed, 155 insertions(+), 62 deletions(-) diff --git a/src/language/stats/ctables.c b/src/language/stats/ctables.c index 7b289ed2c7..1b8ba198f8 100644 --- a/src/language/stats/ctables.c +++ b/src/language/stats/ctables.c @@ -277,6 +277,12 @@ ctables_var_get_print_format (const struct ctables_var *var) : var_get_print_format (var->var)); } +static const char * +ctables_var_name (const struct ctables_var *var) +{ + return var->is_mrset ? var->mrset->name : var_get_name (var->var); +} + struct ctables_categories { size_t n_refs; @@ -410,6 +416,7 @@ struct ctables_axis bool scale; struct ctables_summary *summaries; size_t n_summaries; + size_t allocated_summaries; }; /* Nonterminals. */ @@ -581,55 +588,104 @@ struct ctables_axis_parse_ctx struct ctables_table *t; }; -static struct ctables_summary * -add_summary (struct ctables_axis *axis, enum ctables_summary_function function, - double percentile, size_t *allocated_summaries) +static struct fmt_spec +ctables_summary_default_format (enum ctables_summary_function function, + const struct ctables_var *var) { - 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, AVAILABILITY) [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, AVAILABILITY) [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; + return (struct fmt_spec) { .type = FMT_F, .w = 40 }; case CTF_PERCENT: - format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 }; - break; + return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 }; case CTF_GENERAL: - format = *ctables_var_get_print_format (&axis->var); - break; + return *ctables_var_get_print_format (var); default: NOT_REACHED (); } +} - struct ctables_summary *s = &axis->summaries[axis->n_summaries++]; - *s = (struct ctables_summary) { - .function = function, - .percentile = percentile, - .label = label, - .format = format, +static const char * +ctables_summary_function_name (enum ctables_summary_function function) +{ + static const char *names[] = { +#define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME, + SUMMARIES +#undef S }; - return s; + return names[function]; +} + +static bool +add_summary (struct ctables_axis *axis, + enum ctables_summary_function function, double percentile, + const char *label, const struct fmt_spec *format, + const struct msg_location *loc) +{ + if (axis->op == CTAO_VAR) + { + if (axis->n_summaries >= axis->allocated_summaries) + axis->summaries = x2nrealloc (axis->summaries, + &axis->allocated_summaries, + sizeof *axis->summaries); + + const char *function_name = ctables_summary_function_name (function); + const char *var_name = ctables_var_name (&axis->var); + switch (ctables_function_availability (function)) + { + case CTFA_MRSETS: + if (!axis->var.is_mrset) + { + msg_at (SE, loc, _("Summary function %s applies only to multiple " + "response sets."), function_name); + msg_at (SN, axis->loc, _("'%s' is not a multiple response set."), + var_name); + return false; + } + break; + + case CTFA_SCALE: + if (!axis->scale) + { + msg_at (SE, loc, + _("Summary function %s applies only to scale variables."), + function_name); + msg_at (SN, axis->loc, _("'%s' is not a scale variable."), + var_name); + return false; + } + break; + + case CTFA_ALL: + break; + } + + struct ctables_summary *dst = &axis->summaries[axis->n_summaries++]; + *dst = (struct ctables_summary) { + .function = function, + .percentile = percentile, + .label = xstrdup (label), + .format = (format ? *format + : ctables_summary_default_format (function, &axis->var)), + }; + return true; + } + else + { + for (size_t i = 0; i < 2; i++) + if (!add_summary (axis->subs[i], function, percentile, label, format, + loc)) + return false; + return true; + } } static struct ctables_axis *ctables_axis_parse_stack ( @@ -697,48 +753,85 @@ ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx) : var_get_measure (var.var) == MEASURE_SCALE); axis->loc = lex_ofs_location (ctx->lexer, start_ofs, lex_ofs (ctx->lexer) - 1); + return axis; +} - if (lex_match (ctx->lexer, T_LBRACK)) +static struct ctables_axis * +ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx) +{ + struct ctables_axis *sub = ctables_axis_parse_primary (ctx); + if (!sub || !lex_match (ctx->lexer, T_LBRACK)) + return sub; + + do { - size_t allocated_summaries = 0; - do + int start_ofs = lex_ofs (ctx->lexer); + + /* Parse function. */ + enum ctables_summary_function function; + if (!parse_ctables_summary_function (ctx->lexer, &function)) + goto error; + + /* Parse percentile. */ + double percentile = 0; + if (function == CTSF_PTILE) { - enum ctables_summary_function function; - if (!parse_ctables_summary_function (ctx->lexer, &function)) + if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100)) goto error; + percentile = lex_number (ctx->lexer); + lex_get (ctx->lexer); + } - 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); - } + /* Parse label. */ + char *label; + if (lex_is_string (ctx->lexer)) + { + label = ss_xstrdup (lex_tokss (ctx->lexer)); + lex_get (ctx->lexer); + } + else if (function == CTSF_PTILE) + label = xasprintf (_("Percentile %.2f"), percentile); + else + { + static const char *default_labels[] = { +#define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL, + SUMMARIES +#undef S + }; + label = xstrdup (gettext (default_labels[function])); + } - 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); - } - if (lex_token (ctx->lexer) == T_ID) + /* Parse format. */ + struct fmt_spec format; + const struct fmt_spec *formatp; + if (lex_token (ctx->lexer) == T_ID) + { + if (!parse_format_specifier (ctx->lexer, &format) + || !fmt_check_output (&format) + || !fmt_check_type_compat (&format, VAL_NUMERIC)) { - if (!parse_format_specifier (ctx->lexer, &s->format) - || !fmt_check_output (&s->format) - || !fmt_check_type_compat (&s->format, VAL_NUMERIC)) - goto error; + free (label); + goto error; } - lex_match (ctx->lexer, T_COMMA); + formatp = &format; } - while (!lex_match (ctx->lexer, T_RBRACK)); + else + formatp = NULL; + + struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs, + lex_ofs (ctx->lexer) - 1); + add_summary (sub, function, percentile, label, formatp, loc); + free (label); + msg_location_destroy (loc); + + lex_match (ctx->lexer, T_COMMA); } - return axis; + while (!lex_match (ctx->lexer, T_RBRACK)); + + return sub; error: - ctables_axis_destroy (axis); + ctables_axis_destroy (sub); return NULL; } @@ -793,13 +886,13 @@ static struct ctables_axis * ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx) { int start_ofs = lex_ofs (ctx->lexer); - struct ctables_axis *lhs = ctables_axis_parse_primary (ctx); + struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx); if (!lhs) return NULL; while (lex_match (ctx->lexer, T_GT)) { - struct ctables_axis *rhs = ctables_axis_parse_primary (ctx); + struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx); if (!rhs) return NULL; -- 2.30.2