- 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 }
- };
-}
-
-static struct ctables_category
-cct_srange (struct substring low, struct substring high)
-{
- return (struct ctables_category) {
- .type = CCT_SRANGE,
- .srange = { low, high }
- };
-}
-
-static bool
-ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
- struct ctables_category *cat)
-{
- char *total_label;
- if (lex_match (lexer, T_EQUALS))
- {
- if (!lex_force_string (lexer))
- return false;
-
- total_label = ss_xstrdup (lex_tokss (lexer));
- lex_get (lexer);
- }
- else
- total_label = xstrdup (_("Subtotal"));
-
- *cat = (struct ctables_category) {
- .type = CCT_SUBTOTAL,
- .hide_subcategories = hide_subcategories,
- .total_label = total_label
- };
- return true;
-}
-
-static struct substring
-parse_substring (struct lexer *lexer, struct dictionary *dict)
-{
- struct substring s = recode_substring_pool (
- dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
- ss_rtrim (&s, ss_cstr (" "));
- lex_get (lexer);
- return s;
-}
-
-static bool
-ctables_table_parse_explicit_category (struct lexer *lexer,
- struct dictionary *dict,
- struct ctables *ct,
- struct ctables_category *cat)
-{
- if (lex_match_id (lexer, "OTHERNM"))
- *cat = (struct ctables_category) { .type = CCT_OTHERNM };
- else if (lex_match_id (lexer, "MISSING"))
- *cat = (struct ctables_category) { .type = CCT_MISSING };
- else if (lex_match_id (lexer, "SUBTOTAL"))
- return ctables_table_parse_subtotal (lexer, false, cat);
- else if (lex_match_id (lexer, "HSUBTOTAL"))
- return ctables_table_parse_subtotal (lexer, true, cat);
- else if (lex_match_id (lexer, "LO"))
- {
- if (!lex_force_match_id (lexer, "THRU"))
- return false;
- if (lex_is_string (lexer))
- {
- struct substring sr0 = { .string = NULL };
- struct substring sr1 = parse_substring (lexer, dict);
- *cat = cct_srange (sr0, sr1);
- }
- else if (lex_force_num (lexer))
- {
- *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
- lex_get (lexer);
- }
- else
- return false;
- }
- else if (lex_is_number (lexer))
- {
- double number = lex_number (lexer);
- lex_get (lexer);
- if (lex_match_id (lexer, "THRU"))
- {
- if (lex_match_id (lexer, "HI"))
- *cat = cct_nrange (number, DBL_MAX);
- else
- {
- if (!lex_force_num (lexer))
- return false;
- *cat = cct_nrange (number, lex_number (lexer));
- lex_get (lexer);
- }
- }
- else
- *cat = (struct ctables_category) {
- .type = CCT_NUMBER,
- .number = number
- };
- }
- else if (lex_is_string (lexer))
- {
- struct substring s = parse_substring (lexer, dict);
- if (lex_match_id (lexer, "THRU"))
- {
- if (lex_match_id (lexer, "HI"))
- {
- struct substring sr1 = { .string = NULL };
- *cat = cct_srange (s, sr1);
- }
- else
- {
- if (!lex_force_string (lexer))
- {
- ss_dealloc (&s);
- return false;
- }
- struct substring sr1 = parse_substring (lexer, dict);
- *cat = cct_srange (s, sr1);
- }
- }
- else
- *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
- }
- else if (lex_match (lexer, T_AND))
- {
- if (!lex_force_id (lexer))
- return false;
- struct ctables_postcompute *pc = ctables_find_postcompute (
- ct, lex_tokcstr (lexer));
- if (!pc)