{
CTFA_ALL, /* Any variables. */
CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
- CTFA_MRSETS, /* Only multiple-response sets */
+ //CTFA_MRSETS, /* Only multiple-response sets */
};
struct ctables_summary_spec
const char *var_name = var_get_name (axis->var);
switch (ctables_function_availability (function))
{
+#if 0
case CTFA_MRSETS:
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;
+#endif
case CTFA_SCALE:
-#if 0
- if (!axis->scale)
+ if (!axis->scale && sv != CSV_TOTAL)
{
msg_at (SE, loc,
_("Summary function %s applies only to scale variables."),
var_name);
return false;
}
-#endif
break;
case CTFA_ALL:
struct ctables_axis *axis = xmalloc (sizeof *axis);
*axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
- /* XXX should figure out default measures by reading data */
axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
: lex_match_phrase (ctx->lexer, "[C]") ? false
: var_get_measure (var) == MEASURE_SCALE);
&& fmt_check_type_compat (format, VAL_NUMERIC));
}
+ lex_get (lexer);
if (format->w < 2)
{
- msg (SE, _("Output format %s requires width 2 or greater."), type);
+ lex_next_error (lexer, -1, -1,
+ _("Output format %s requires width 2 or greater."), type);
return false;
}
else if (format->d > format->w - 1)
{
- msg (SE, _("Output format %s requires width greater than decimals."),
- type);
+ lex_next_error (lexer, -1, -1, _("Output format %s requires width "
+ "greater than decimals."), type);
return false;
}
else
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:
ngettext ("These categories include %zu instance "
"of SUBTOTAL or HSUBTOTAL, so references "
"from computed categories must refer to "
- "subtotals by position.",
+ "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.",
+ "subtotals by position, "
+ "e.g. SUBTOTAL[1].",
n_subtotals),
n_subtotals);
msg_at (SN, e->location,
"in the category list."),
pc_cat->pc->name);
msg_at (SN, e->location, _("This is the missing category."));
- msg_at (SN, cats_location,
- _("To fix the problem, add the missing category to the "
- "list of categories here."));
+ 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)
dict, e->subs[i], pc_cat, cats, cats_location))
return false;
return true;
-
- default:
- NOT_REACHED ();
}
+
+ NOT_REACHED ();
}
static bool
}
size_t allocated_cats = 0;
+ int cats_start_ofs = -1;
+ int cats_end_ofs = -1;
if (lex_match (lexer, T_LBRACK))
{
- int cats_start_ofs = lex_ofs (lexer);
+ cats_start_ofs = lex_ofs (lexer);
do
{
if (c->n_cats >= allocated_cats)
lex_match (lexer, T_COMMA);
}
while (!lex_match (lexer, T_RBRACK));
-
- struct msg_location *cats_location
- = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
- for (size_t i = 0; i < c->n_cats; i++)
- {
- struct ctables_category *cat = &c->cats[i];
- switch (cat->type)
- {
- case CCT_POSTCOMPUTE:
- cat->parse_format = parse_strings ? common_format->type : FMT_F;
- if (!ctables_recursive_check_postcompute (dict, cat->pc->expr,
- cat, c, cats_location))
- return false;
- break;
-
- case CCT_NUMBER:
- case CCT_NRANGE:
- for (size_t j = 0; j < n_vars; j++)
- if (var_is_alpha (vars[j]))
- {
- msg_at (SE, cat->location,
- _("This category specification may be applied "
- "only to numeric variables, but this "
- "subcommand tries to apply it to string "
- "variable %s."),
- var_get_name (vars[j]));
- return false;
- }
- break;
-
- case CCT_STRING:
- if (parse_strings)
- {
- double n;
- if (!parse_category_string (cat->location, cat->string, dict,
- common_format->type, &n))
- return false;
-
- ss_dealloc (&cat->string);
-
- cat->type = CCT_NUMBER;
- cat->number = n;
- }
- else if (!all_strings (vars, n_vars, cat))
- return false;
- break;
-
- case CCT_SRANGE:
- if (parse_strings)
- {
- double n[2];
-
- if (!cat->srange[0].string)
- n[0] = -DBL_MAX;
- else if (!parse_category_string (cat->location,
- cat->srange[0], dict,
- common_format->type, &n[0]))
- return false;
-
- if (!cat->srange[1].string)
- n[1] = DBL_MAX;
- else if (!parse_category_string (cat->location,
- cat->srange[1], dict,
- common_format->type, &n[1]))
- return false;
-
- ss_dealloc (&cat->srange[0]);
- ss_dealloc (&cat->srange[1]);
-
- cat->type = CCT_NRANGE;
- cat->nrange[0] = n[0];
- cat->nrange[1] = n[1];
- }
- else if (!all_strings (vars, n_vars, cat))
- return false;
- break;
-
- case CCT_MISSING:
- case CCT_OTHERNM:
- case CCT_SUBTOTAL:
- case CCT_TOTAL:
- case CCT_VALUE:
- case CCT_LABEL:
- case CCT_FUNCTION:
- case CCT_EXCLUDED_MISSING:
- break;
- }
- }
+ cats_end_ofs = lex_ofs (lexer) - 1;
}
struct ctables_category cat = {
}
}
+ if (cats_start_ofs != -1)
+ {
+ struct msg_location *cats_location
+ = lex_ofs_location (lexer, cats_start_ofs, cats_end_ofs);
+ for (size_t i = 0; i < c->n_cats; i++)
+ {
+ struct ctables_category *cat = &c->cats[i];
+ switch (cat->type)
+ {
+ case CCT_POSTCOMPUTE:
+ cat->parse_format = parse_strings ? common_format->type : FMT_F;
+ if (!ctables_recursive_check_postcompute (dict, cat->pc->expr,
+ cat, c, cats_location))
+ return false;
+ break;
+
+ case CCT_NUMBER:
+ case CCT_NRANGE:
+ for (size_t j = 0; j < n_vars; j++)
+ if (var_is_alpha (vars[j]))
+ {
+ msg_at (SE, cat->location,
+ _("This category specification may be applied "
+ "only to numeric variables, but this "
+ "subcommand tries to apply it to string "
+ "variable %s."),
+ var_get_name (vars[j]));
+ return false;
+ }
+ break;
+
+ case CCT_STRING:
+ if (parse_strings)
+ {
+ double n;
+ if (!parse_category_string (cat->location, cat->string, dict,
+ common_format->type, &n))
+ return false;
+
+ ss_dealloc (&cat->string);
+
+ cat->type = CCT_NUMBER;
+ cat->number = n;
+ }
+ else if (!all_strings (vars, n_vars, cat))
+ return false;
+ break;
+
+ case CCT_SRANGE:
+ if (parse_strings)
+ {
+ double n[2];
+
+ if (!cat->srange[0].string)
+ n[0] = -DBL_MAX;
+ else if (!parse_category_string (cat->location,
+ cat->srange[0], dict,
+ common_format->type, &n[0]))
+ return false;
+
+ if (!cat->srange[1].string)
+ n[1] = DBL_MAX;
+ else if (!parse_category_string (cat->location,
+ cat->srange[1], dict,
+ common_format->type, &n[1]))
+ return false;
+
+ ss_dealloc (&cat->srange[0]);
+ ss_dealloc (&cat->srange[1]);
+
+ cat->type = CCT_NRANGE;
+ cat->nrange[0] = n[0];
+ cat->nrange[1] = n[1];
+ }
+ else if (!all_strings (vars, n_vars, cat))
+ return false;
+ break;
+
+ case CCT_MISSING:
+ case CCT_OTHERNM:
+ case CCT_SUBTOTAL:
+ case CCT_TOTAL:
+ case CCT_VALUE:
+ case CCT_LABEL:
+ case CCT_FUNCTION:
+ case CCT_EXCLUDED_MISSING:
+ break;
+ }
+ }
+ }
+
return true;
}
}
case CTPO_CAT_NUMBER:
- case CTPO_CAT_STRING:
case CTPO_CAT_MISSING:
case CTPO_CAT_OTHERNM:
case CTPO_CAT_SUBTOTAL:
return ctables_pcexpr_evaluate_category (ctx, &cv);
}
+ case CTPO_CAT_STRING:
+ {
+ int width = var_get_width (ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx]);
+ char *s = NULL;
+ if (width > e->string.length)
+ {
+ s = xmalloc (width);
+ buf_copy_rpad (s, width, e->string.string, e->string.length, ' ');
+ }
+ struct ctables_cell_value cv = {
+ .category = ctables_find_category_for_postcompute (ctx->section->table->ctables->dict, ctx->cats, ctx->parse_format, e),
+ .value = { .s = CHAR_CAST (uint8_t *, s ? s : e->string.string) },
+ };
+ assert (cv.category != NULL);
+ double retval = ctables_pcexpr_evaluate_category (ctx, &cv);
+ free (s);
+ return retval;
+ }
+
case CTPO_ADD:
return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
return true;
const struct ctables_nest *n0 = &stack->nests[0];
- assert (n0->n > 0);
+ if (n0->n == 0)
+ {
+ assert (stack->n == 1);
+ return true;
+ }
+
const struct variable *v0 = n0->vars[n0->n - 1];
struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
t->clabels_example = v0;
struct ctables_nest *nest = xmalloc (sizeof *nest);
*nest = (struct ctables_nest) { .n = 0 };
t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
+
+ /* There's no point in moving labels away from an axis that has no
+ labels, so avoid dealing with the special cases around that. */
+ t->label_axis[a] = a;
}
struct ctables_stack *stack = &t->stacks[t->summary_axis];
};
}
+static struct ctables_pcexpr
+ctpo_cat_srange (struct substring low, struct substring high)
+{
+ return (struct ctables_pcexpr) {
+ .op = CTPO_CAT_SRANGE,
+ .srange = { low, high },
+ };
+}
+
static struct ctables_pcexpr *
ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
{
{
if (lex_match_id (lexer, "LO"))
{
- if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
+ if (!lex_force_match_id (lexer, "THRU"))
return false;
- e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
- lex_get (lexer);
+
+ if (lex_is_string (lexer))
+ {
+ struct substring low = { .string = NULL };
+ struct substring high = parse_substring (lexer, dict);
+ e = ctpo_cat_srange (low, high);
+ }
+ else
+ {
+ if (lex_force_num (lexer))
+ return false;
+ e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
+ lex_get (lexer);
+ }
}
else if (lex_is_number (lexer))
{
}
else if (lex_is_string (lexer))
{
- struct substring s = recode_substring_pool (
- dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
- ss_rtrim (&s, ss_cstr (" "));
+ struct substring s = parse_substring (lexer, dict);
- e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
- lex_get (lexer);
+ if (lex_match_id (lexer, "THRU"))
+ {
+ struct substring high;
+
+ if (lex_match_id (lexer, "HI"))
+ high = (struct substring) { .string = NULL };
+ else
+ {
+ if (!lex_force_string (lexer))
+ {
+ ss_dealloc (&s);
+ return false;
+ }
+ high = parse_substring (lexer, dict);
+ }
+
+ e = ctpo_cat_srange (s, high);
+ }
+ else
+ e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
}
else
{
{
if (e.op == CTPO_CAT_STRING)
ss_dealloc (&e.string);
+ else if (e.op == CTPO_CAT_SRANGE)
+ {
+ ss_dealloc (&e.srange[0]);
+ ss_dealloc (&e.srange[1]);
+ }
return NULL;
}
}
{
int pcompute_start = lex_ofs (lexer) - 1;
- if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
+ if (!lex_match (lexer, T_AND))
+ {
+ lex_error_expecting (lexer, "&");
+ return false;
+ }
+ if (!lex_force_id (lexer))
return false;
char *name = ss_xstrdup (lex_tokss (lexer));
if (!ct->e_weight)
goto error;
}
- else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
+ else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
{
if (lex_match_id (lexer, "COUNT"))
{
break;
}
if (!lex_force_match (lexer, T_SLASH))
- break;
+ goto error;
while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
{
}
else if (lex_match_id (lexer, "CLABELS"))
{
- while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
+ if (lex_match_id (lexer, "AUTO"))
{
- if (lex_match_id (lexer, "AUTO"))
- {
- t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
- t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
- }
- else if (lex_match_id (lexer, "ROWLABELS"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "OPPOSITE"))
- t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
- else if (lex_match_id (lexer, "LAYER"))
- t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
- else
- {
- lex_error_expecting (lexer, "OPPOSITE", "LAYER");
- goto error;
- }
- }
- else if (lex_match_id (lexer, "COLLABELS"))
+ t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
+ t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
+ }
+ else if (lex_match_id (lexer, "ROWLABELS"))
+ {
+ lex_match (lexer, T_EQUALS);
+ if (lex_match_id (lexer, "OPPOSITE"))
+ t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
+ else if (lex_match_id (lexer, "LAYER"))
+ t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
+ else
{
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "OPPOSITE"))
- t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
- else if (lex_match_id (lexer, "LAYER"))
- t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
- else
- {
- lex_error_expecting (lexer, "OPPOSITE", "LAYER");
- goto error;
- }
+ lex_error_expecting (lexer, "OPPOSITE", "LAYER");
+ goto error;
}
+ }
+ else if (lex_match_id (lexer, "COLLABELS"))
+ {
+ lex_match (lexer, T_EQUALS);
+ if (lex_match_id (lexer, "OPPOSITE"))
+ t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
+ else if (lex_match_id (lexer, "LAYER"))
+ t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
else
{
- lex_error_expecting (lexer, "AUTO", "ROWLABELS",
- "COLLABELS");
+ lex_error_expecting (lexer, "OPPOSITE", "LAYER");
goto error;
}
}
+ else
+ {
+ lex_error_expecting (lexer, "AUTO", "ROWLABELS",
+ "COLLABELS");
+ goto error;
+ }
}
else if (lex_match_id (lexer, "CRITERIA"))
{
else if (lex_match_id (lexer, "INCLUDEMRSETS"))
{
lex_match (lexer, T_EQUALS);
- if (parse_bool (lexer, &t->chisq->include_mrsets))
+ if (!parse_bool (lexer, &t->chisq->include_mrsets))
goto error;
}
else if (lex_match_id (lexer, "CATEGORIES"))