- lex_error (lexer, NULL);
- return false;
- }
-
- return true;
-}
-
-static bool
-parse_category_string (struct msg_location *location,
- struct substring s, const struct dictionary *dict,
- enum fmt_type format, double *n)
-{
- union value v;
- char *error = data_in (s, dict_get_encoding (dict), format,
- settings_get_fmt_settings (), &v, 0, NULL);
- if (error)
- {
- msg_at (SE, location,
- _("Failed to parse category specification as format %s: %s."),
- fmt_name (format), error);
- free (error);
- return false;
- }
-
- *n = v.f;
- return true;
-}
-
-static struct ctables_category *
-ctables_find_category_for_postcompute__ (const struct ctables_categories *cats,
- const struct ctables_pcexpr *e)
-{
- struct ctables_category *best = NULL;
- size_t n_subtotals = 0;
- for (size_t i = 0; i < cats->n_cats; i++)
- {
- struct ctables_category *cat = &cats->cats[i];
- switch (e->op)
- {
- case CTPO_CAT_NUMBER:
- if (cat->type == CCT_NUMBER && cat->number == e->number)
- best = cat;
- break;
-
- case CTPO_CAT_STRING:
- if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
- best = cat;
- break;
-
- case CTPO_CAT_NRANGE:
- if (cat->type == CCT_NRANGE
- && cat->nrange[0] == e->nrange[0]
- && cat->nrange[1] == e->nrange[1])
- best = cat;
- break;
-
- case CTPO_CAT_SRANGE:
- if (cat->type == CCT_SRANGE
- && nullable_substring_equal (&cat->srange[0], &e->srange[0])
- && nullable_substring_equal (&cat->srange[1], &e->srange[1]))
- best = cat;
- break;
-
- case CTPO_CAT_MISSING:
- if (cat->type == CCT_MISSING)
- best = cat;
- break;
-
- case CTPO_CAT_OTHERNM:
- if (cat->type == CCT_OTHERNM)
- best = cat;
- break;
-
- case CTPO_CAT_SUBTOTAL:
- if (cat->type == CCT_SUBTOTAL)
- {
- n_subtotals++;
- if (e->subtotal_index == n_subtotals)
- return cat;
- else if (e->subtotal_index == 0)
- best = cat;
- }
- break;
-
- case CTPO_CAT_TOTAL:
- if (cat->type == CCT_TOTAL)
- return cat;
- break;
-
- case CTPO_CONSTANT:
- case CTPO_ADD:
- case CTPO_SUB:
- case CTPO_MUL:
- case CTPO_DIV:
- case CTPO_POW:
- case CTPO_NEG:
- NOT_REACHED ();
- }
- }
- if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
- return NULL;
- return best;
-}
-
-static struct ctables_category *
-ctables_find_category_for_postcompute (const struct dictionary *dict,
- const struct ctables_categories *cats,
- enum fmt_type parse_format,
- const struct ctables_pcexpr *e)
-{
- if (parse_format != FMT_F)
- {
- if (e->op == CTPO_CAT_STRING)
- {
- double number;
- if (!parse_category_string (e->location, e->string, dict,
- parse_format, &number))
- return NULL;
-
- struct ctables_pcexpr e2 = {
- .op = CTPO_CAT_NUMBER,
- .number = number,
- .location = e->location,
- };
- return ctables_find_category_for_postcompute__ (cats, &e2);
- }
- else if (e->op == CTPO_CAT_SRANGE)
- {
- double nrange[2];
- if (!e->srange[0].string)
- nrange[0] = -DBL_MAX;
- else if (!parse_category_string (e->location, e->srange[0], dict,
- parse_format, &nrange[0]))
- return NULL;
-
- if (!e->srange[1].string)
- nrange[1] = DBL_MAX;
- else if (!parse_category_string (e->location, e->srange[1], dict,
- parse_format, &nrange[1]))
- return NULL;
-
- struct ctables_pcexpr e2 = {
- .op = CTPO_CAT_NRANGE,
- .nrange = { nrange[0], nrange[1] },
- .location = e->location,
- };
- return ctables_find_category_for_postcompute__ (cats, &e2);
- }
- }
- return ctables_find_category_for_postcompute__ (cats, e);
-}
-
-static bool
-ctables_recursive_check_postcompute (struct dictionary *dict,
- const struct ctables_pcexpr *e,
- struct ctables_category *pc_cat,
- const struct ctables_categories *cats,
- const struct msg_location *cats_location)
-{
- switch (e->op)
- {
- case CTPO_CAT_NUMBER:
- case CTPO_CAT_STRING:
- case CTPO_CAT_NRANGE:
- case CTPO_CAT_SRANGE:
- case CTPO_CAT_MISSING:
- case CTPO_CAT_OTHERNM:
- case CTPO_CAT_SUBTOTAL:
- case CTPO_CAT_TOTAL:
- {
- struct ctables_category *cat = ctables_find_category_for_postcompute (
- dict, cats, pc_cat->parse_format, e);
- if (!cat)
- {
- if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
- {
- size_t n_subtotals = 0;
- for (size_t i = 0; i < cats->n_cats; i++)
- n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
- if (n_subtotals > 1)
- {
- msg_at (SE, cats_location,
- ngettext ("These categories include %zu instance "
- "of SUBTOTAL or HSUBTOTAL, so references "
- "from computed categories must refer to "
- "subtotals by position, "
- "e.g. SUBTOTAL[1].",
- "These categories include %zu instances "
- "of SUBTOTAL or HSUBTOTAL, so references "
- "from computed categories must refer to "
- "subtotals by position, "
- "e.g. SUBTOTAL[1].",
- n_subtotals),
- n_subtotals);
- msg_at (SN, e->location,
- _("This is the reference that lacks a position."));
- return NULL;
- }
- }
-
- msg_at (SE, pc_cat->location,
- _("Computed category &%s references a category not included "
- "in the category list."),
- pc_cat->pc->name);
- msg_at (SN, e->location, _("This is the missing category."));
- if (e->op == CTPO_CAT_SUBTOTAL)
- msg_at (SN, cats_location,
- _("To fix the problem, add subtotals to the "
- "list of categories here."));
- else if (e->op == CTPO_CAT_TOTAL)
- msg (SN, _("To fix the problem, add TOTAL=YES to the variable's "
- "CATEGORIES specification."));
- else
- msg_at (SN, cats_location,
- _("To fix the problem, add the missing category to the "
- "list of categories here."));
- return false;
- }
- if (pc_cat->pc->hide_source_cats)
- cat->hide = true;
- return true;
- }
-
- case CTPO_CONSTANT:
- return true;
-
- case CTPO_ADD:
- case CTPO_SUB:
- case CTPO_MUL:
- case CTPO_DIV:
- case CTPO_POW:
- case CTPO_NEG:
- for (size_t i = 0; i < 2; i++)
- if (e->subs[i] && !ctables_recursive_check_postcompute (
- dict, e->subs[i], pc_cat, cats, cats_location))
- return false;
- return true;
- }
-
- NOT_REACHED ();
-}
-
-static bool
-all_strings (struct variable **vars, size_t n_vars,
- const struct ctables_category *cat)
-{
- for (size_t j = 0; j < n_vars; j++)
- if (var_is_numeric (vars[j]))
- {
- msg_at (SE, cat->location,
- _("This category specification may be applied only to string "
- "variables, but this subcommand tries to apply it to "
- "numeric variable %s."),
- var_get_name (vars[j]));
- return false;
- }