- /* Parse label. */
- char *label = NULL;
- if (lex_is_string (ctx->lexer))
- {
- label = ss_xstrdup (lex_tokss (ctx->lexer));
- lex_get (ctx->lexer);
- }
-
- /* Parse format. */
- struct fmt_spec format;
- const struct fmt_spec *formatp;
- bool is_ctables_format = false;
- if (lex_token (ctx->lexer) == T_ID
- && has_digit (lex_tokcstr (ctx->lexer)))
- {
- if (!parse_ctables_format_specifier (ctx->lexer, &format,
- &is_ctables_format))
- {
- free (label);
- goto error;
- }
- formatp = &format;
- }
- else
- formatp = NULL;
-
- struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
- lex_ofs (ctx->lexer) - 1);
- add_summary_spec (sub, function, weighted, area, percentile, label,
- formatp, is_ctables_format, loc, sv);
- free (label);
- msg_location_destroy (loc);
-
- lex_match (ctx->lexer, T_COMMA);
- if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
- {
- if (!lex_force_match (ctx->lexer, T_LBRACK))
- goto error;
- sv = CSV_TOTAL;
- }
- else if (lex_match (ctx->lexer, T_RBRACK))
- {
- if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
- goto error;
- return sub;
- }
- }
-
-error:
- ctables_axis_destroy (sub);
- return NULL;
-}
-
-static const struct ctables_axis *
-find_scale (const struct ctables_axis *axis)
-{
- if (!axis)
- return NULL;
- else if (axis->op == CTAO_VAR)
- return axis->scale ? axis : NULL;
- else
- {
- for (size_t i = 0; i < 2; i++)
- {
- const struct ctables_axis *scale = find_scale (axis->subs[i]);
- if (scale)
- return scale;
- }
- return NULL;
- }
-}
-
-static const struct ctables_axis *
-find_categorical_summary_spec (const struct ctables_axis *axis)
-{
- if (!axis)
- return NULL;
- else if (axis->op == CTAO_VAR)
- return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
- else
- {
- for (size_t i = 0; i < 2; i++)
- {
- const struct ctables_axis *sum
- = find_categorical_summary_spec (axis->subs[i]);
- if (sum)
- return sum;
- }
- return NULL;
- }
-}
-
-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_postfix (ctx);
- if (!lhs)
- return NULL;
-
- while (lex_match (ctx->lexer, T_GT))
- {
- struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
- if (!rhs)
- {
- ctables_axis_destroy (lhs);
- return NULL;
- }
-
- struct ctables_axis *nest = ctables_axis_new_nonterminal (
- CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
-
- const struct ctables_axis *outer_scale = find_scale (lhs);
- const struct ctables_axis *inner_scale = find_scale (rhs);
- if (outer_scale && inner_scale)
- {
- msg_at (SE, nest->loc, _("Cannot nest scale variables."));
- msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
- msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
- ctables_axis_destroy (nest);
- return NULL;
- }
-
- const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
- if (outer_sum)
- {
- msg_at (SE, nest->loc,
- _("Summaries may only be requested for categorical variables "
- "at the innermost nesting level."));
- msg_at (SN, outer_sum->loc,
- _("This outer categorical variable has a summary."));
- ctables_axis_destroy (nest);
- return NULL;
- }
-
- lhs = nest;
- }
-
- return lhs;
-}
-
-static struct ctables_axis *
-ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
-{
- int start_ofs = lex_ofs (ctx->lexer);
- struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
- if (!lhs)
- return NULL;
-
- while (lex_match (ctx->lexer, T_PLUS))
- {
- struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
- if (!rhs)
- {
- ctables_axis_destroy (lhs);
- return NULL;
- }
-
- lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
- ctx->lexer, start_ofs);
- }
-
- return lhs;
-}
-
-static bool
-ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
- struct ctables *ct, struct ctables_table *t,
- enum pivot_axis_type a)
-{
- if (lex_token (lexer) == T_BY
- || lex_token (lexer) == T_SLASH
- || lex_token (lexer) == T_ENDCMD)
- return true;
-
- struct ctables_axis_parse_ctx ctx = {
- .lexer = lexer,
- .dict = dict,
- .ct = ct,
- .t = t
- };
- t->axes[a] = ctables_axis_parse_stack (&ctx);
- 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_destroy (struct ctables_table *t)
-{
- if (!t)
- return;
-
- for (size_t i = 0; i < t->n_sections; i++)
- ctables_section_uninit (&t->sections[i]);
- free (t->sections);
-
- for (size_t i = 0; i < t->n_categories; i++)
- ctables_categories_unref (t->categories[i]);
- free (t->categories);
-
- for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
- {
- ctables_axis_destroy (t->axes[a]);
- ctables_stack_uninit (&t->stacks[a]);
- }
- free (t->summary_specs.specs);
-
- struct ctables_value *ctv, *next_ctv;
- HMAP_FOR_EACH_SAFE (ctv, next_ctv, struct ctables_value, node,
- &t->clabels_values_map)
- {
- value_destroy (&ctv->value, var_get_width (t->clabels_example));
- hmap_delete (&t->clabels_values_map, &ctv->node);
- free (ctv);
- }
- hmap_destroy (&t->clabels_values_map);
- free (t->clabels_values);