- }
- }
-
-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);
-
- free (t->sum_vars);
- free (t->caption);
- free (t->corner);
- free (t->title);
- ctables_chisq_destroy (t->chisq);
- ctables_pairwise_destroy (t->pairwise);
- free (t);
-}
-
-static void
-ctables_destroy (struct ctables *ct)
-{
- if (!ct)
- return;
-
- struct ctables_postcompute *pc, *next_pc;
- HMAP_FOR_EACH_SAFE (pc, next_pc, struct ctables_postcompute, hmap_node,
- &ct->postcomputes)
- {
- free (pc->name);
- msg_location_destroy (pc->location);
- ctables_pcexpr_destroy (pc->expr);
- free (pc->label);
- if (pc->specs)
- {
- ctables_summary_spec_set_uninit (pc->specs);
- free (pc->specs);
- }
- hmap_delete (&ct->postcomputes, &pc->hmap_node);
- free (pc);
- }
- hmap_destroy (&ct->postcomputes);
-
- fmt_settings_uninit (&ct->ctables_formats);
- 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_destroy (ct->tables[i]);
- free (ct->tables);
- free (ct);
-}
-
-static struct ctables_category
-cct_nrange (double low, double high)
-{
- return (struct ctables_category) {
- .type = CCT_NRANGE,
- .nrange = { low, high }
- };
-}