}
}
- lex_error (lexer, _("Expecting summary function name."));
+ lex_error (lexer, _("Syntax error expecting summary function name."));
return false;
}
else
{
*is_ctables_format = false;
- return (parse_format_specifier (lexer, format)
- && fmt_check_output (format)
- && fmt_check_type_compat (format, VAL_NUMERIC));
+ if (!parse_format_specifier (lexer, format))
+ return false;
+
+ char *error = fmt_check_output__ (format);
+ if (!error)
+ error = fmt_check_type_compat__ (format, NULL, VAL_NUMERIC);
+ if (error)
+ {
+ lex_next_error (lexer, -1, -1, "%s", error);
+ free (error);
+ return false;
+ }
+
+ return true;
}
lex_get (lexer);
};
};
- /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
- CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
+ /* Source location (sometimes NULL). */
struct msg_location *location;
};
/* In struct ctables_section's 'cells' hmap. Indexed by all the values in
all the axes (except the scalar variable, if any). */
struct hmap_node node;
+ struct ctables_section *section;
/* The areas that contain this cell. */
uint32_t omit_areas;
enum pivot_axis_type label_axis[PIVOT_N_AXES];
enum pivot_axis_type clabels_from_axis;
enum pivot_axis_type clabels_to_axis;
+ int clabels_start_ofs, clabels_end_ofs;
const struct variable *clabels_example;
struct hmap clabels_values_map;
struct ctables_value **clabels_values;
return 0;
}
-static int
-ctables_cell_compare_leaf_3way (const void *a_, const void *b_,
- const void *aux UNUSED)
-{
- struct ctables_cell *const *ap = a_;
- struct ctables_cell *const *bp = b_;
- const struct ctables_cell *a = *ap;
- const struct ctables_cell *b = *bp;
-
- for (enum pivot_axis_type axis = 0; axis < PIVOT_N_AXES; axis++)
- {
- int al = a->axes[axis].leaf;
- int bl = b->axes[axis].leaf;
- if (al != bl)
- return al > bl ? 1 : -1;
- }
- return 0;
-}
-
static struct ctables_area *
-ctables_area_insert (struct ctables_section *s, struct ctables_cell *cell,
- enum ctables_area_type area)
+ctables_area_insert (struct ctables_cell *cell, enum ctables_area_type area)
{
+ struct ctables_section *s = cell->section;
size_t hash = 0;
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
}
cell = xmalloc (sizeof *cell);
+ cell->section = s;
cell->hide = false;
cell->sv = sv;
cell->omit_areas = 0;
for (size_t i = 0; i < specs->n; i++)
ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
for (enum ctables_area_type at = 0; at < N_CTATS; at++)
- cell->areas[at] = ctables_area_insert (s, cell, at);
+ cell->areas[at] = ctables_area_insert (cell, at);
hmap_insert (&s->cells, &cell->node, hash);
return cell;
}
};
static struct ctables_value *
-ctables_value_find__ (struct ctables_table *t, const union value *value,
+ctables_value_find__ (const struct ctables_table *t, const union value *value,
int width, unsigned int hash)
{
struct ctables_value *clv;
}
}
-static struct ctables_value *
-ctables_value_find (struct ctables_table *t,
- const union value *value, int width)
+static const struct ctables_value *
+ctables_value_find (const struct ctables_cell *cell)
{
- return ctables_value_find__ (t, value, width,
- value_hash (value, width, 0));
+ const struct ctables_section *s = cell->section;
+ const struct ctables_table *t = s->table;
+ if (!t->clabels_example)
+ return NULL;
+
+ const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
+ const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
+ const union value *value
+ = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
+ int width = var_get_width (var);
+ const struct ctables_value *ctv = ctables_value_find__ (
+ t, value, width, value_hash (value, width, 0));
+ assert (ctv != NULL);
+ return ctv;
}
static int
bool show_totals = false;
char *total_label = NULL;
bool totals_before = false;
+ int key_start_ofs = 0;
+ int key_end_ofs = 0;
while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
{
if (!c->n_cats && lex_match_id (lexer, "ORDER"))
}
else if (!c->n_cats && lex_match_id (lexer, "KEY"))
{
- int start_ofs = lex_ofs (lexer) - 1;
+ key_start_ofs = lex_ofs (lexer) - 1;
lex_match (lexer, T_EQUALS);
if (lex_match_id (lexer, "VALUE"))
cat.type = CCT_VALUE;
bool UNUSED b = lex_force_match (lexer, T_LPAREN);
goto error;
}
+ }
+ key_end_ofs = lex_ofs (lexer) - 1;
- lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
- _("Data-dependent sorting is not implemented."));
+ if (cat.type == CCT_FUNCTION)
+ {
+ lex_ofs_error (lexer, key_start_ofs, key_end_ofs,
+ _("Data-dependent sorting is not implemented."));
goto error;
}
}
if (!c->n_cats)
{
+ if (key_start_ofs)
+ cat.location = lex_ofs_location (lexer, key_start_ofs, key_end_ofs);
+
if (c->n_cats >= allocated_cats)
c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
c->cats[c->n_cats++] = cat;
return true;
}
+static int
+compare_ints_3way (int a, int b)
+{
+ return a < b ? -1 : a > b;
+}
+
+static int
+ctables_cell_compare_leaf_3way (const void *a_, const void *b_,
+ const void *aux UNUSED)
+{
+ struct ctables_cell *const *ap = a_;
+ struct ctables_cell *const *bp = b_;
+ const struct ctables_cell *a = *ap;
+ const struct ctables_cell *b = *bp;
+
+ if (a == b)
+ {
+ assert (a_ == b_);
+ return 0;
+ }
+
+ for (enum pivot_axis_type axis = 0; axis < PIVOT_N_AXES; axis++)
+ {
+ int cmp = compare_ints_3way (a->axes[axis].leaf, b->axes[axis].leaf);
+ if (cmp)
+ return cmp;
+ }
+
+ const struct ctables_value *a_ctv = ctables_value_find (a);
+ const struct ctables_value *b_ctv = ctables_value_find (b);
+ if (a_ctv && b_ctv)
+ {
+ int cmp = compare_ints_3way (a_ctv->leaf, b_ctv->leaf);
+ if (cmp)
+ return cmp;
+ }
+ else
+ assert (!a_ctv && !b_ctv);
+ return 0;
+}
+
static void
ctables_table_output (struct ctables *ct, struct ctables_table *t)
{
if (cell->hide)
continue;
+ const struct ctables_value *ctv = ctables_value_find (cell);
const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
for (size_t j = 0; j < specs->n; j++)
if (summary_dimension)
dindexes[n_dindexes++] = specs->specs[j].axis_idx;
- if (categories_dimension)
- {
- const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
- const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
- const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
- const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
- if (!ctv)
- continue;
- dindexes[n_dindexes++] = ctv->leaf;
- }
+ if (ctv)
+ dindexes[n_dindexes++] = ctv->leaf;
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
if (d[a])
}
static bool
-ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
+ctables_check_label_position (struct ctables_table *t, struct lexer *lexer,
+ enum pivot_axis_type a)
{
enum pivot_axis_type label_pos = t->label_axis[a];
if (label_pos == a)
return true;
- const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
- const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
-
const struct ctables_stack *stack = &t->stacks[a];
if (!stack->n)
return true;
for (size_t i = 0; i < c0->n_cats; i++)
if (c0->cats[i].type == CCT_FUNCTION)
{
- msg (SE, _("%s=%s is not allowed with sorting based "
- "on a summary function."),
- subcommand_name, pos_name);
+ msg (SE, _("Category labels may not be moved to another axis when "
+ "sorting by a summary function."));
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+ _("This syntax moves category labels to another axis."));
+ msg_at (SN, c0->cats[i].location,
+ _("This syntax requests sorting by a summary function."));
return false;
}
- if (n0->n - 1 == n0->scale_idx)
+
+ for (size_t i = 0; i < stack->n; i++)
{
- msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
- "but %s is a scale variable."),
- subcommand_name, pos_name, var_get_name (v0));
- return false;
+ const struct ctables_nest *ni = &stack->nests[i];
+ assert (ni->n > 0);
+ const struct variable *vi = ni->vars[ni->n - 1];
+ if (n0->n - 1 == ni->scale_idx)
+ {
+ msg (SE, _("To move category labels from one axis to another, "
+ "the variables whose labels are to be moved must be "
+ "categorical, but %s is scale."), var_get_name (vi));
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+ _("This syntax moves category labels to another axis."));
+ return false;
+ }
}
for (size_t i = 1; i < stack->n; i++)
const struct variable *vi = ni->vars[ni->n - 1];
struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
- if (ni->n - 1 == ni->scale_idx)
- {
- msg (SE, _("%s=%s requires the variables to be moved to be "
- "categorical, but %s is a scale variable."),
- subcommand_name, pos_name, var_get_name (vi));
- return false;
- }
if (var_get_width (v0) != var_get_width (vi))
{
- msg (SE, _("%s=%s requires the variables to be "
- "moved to have the same width, but %s has "
- "width %d and %s has width %d."),
- subcommand_name, pos_name,
+ msg (SE, _("To move category labels from one axis to another, "
+ "the variables whose labels are to be moved must all "
+ "have the same width, but %s has width %d and %s has "
+ "width %d."),
var_get_name (v0), var_get_width (v0),
var_get_name (vi), var_get_width (vi));
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+ _("This syntax moves category labels to another axis."));
return false;
}
if (!val_labs_equal (var_get_value_labels (v0),
var_get_value_labels (vi)))
{
- msg (SE, _("%s=%s requires the variables to be "
- "moved to have the same value labels, but %s "
- "and %s have different value labels."),
- subcommand_name, pos_name,
+ msg (SE, _("To move category labels from one axis to another, "
+ "the variables whose labels are to be moved must all "
+ "have the same value labels, but %s and %s have "
+ "different value labels."),
var_get_name (v0), var_get_name (vi));
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+ _("This syntax moves category labels to another axis."));
return false;
}
if (!ctables_categories_equal (c0, ci))
{
- msg (SE, _("%s=%s requires the variables to be "
- "moved to have the same category "
- "specifications, but %s and %s have different "
- "category specifications."),
- subcommand_name, pos_name,
+ msg (SE, _("To move category labels from one axis to another, "
+ "the variables whose labels are to be moved must all "
+ "have the same category specifications, but %s and %s "
+ "have different category specifications."),
var_get_name (v0), var_get_name (vi));
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs, t->clabels_end_ofs,
+ _("This syntax moves category labels to another axis."));
return false;
}
}
}
static bool
-ctables_prepare_table (struct ctables_table *t)
+ctables_prepare_table (struct ctables_table *t, struct lexer *lexer)
{
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
if (t->axes[a])
enumerate_sum_vars (t->axes[t->summary_axis],
&t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
- return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
- && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
+ return (ctables_check_label_position (t, lexer, PIVOT_AXIS_ROW)
+ && ctables_check_label_position (t, lexer, PIVOT_AXIS_COLUMN));
}
static void
char *name = ss_xstrdup (lex_tokss (lexer));
lex_get (lexer);
- if (!lex_force_match (lexer, T_EQUALS)
- || !lex_force_match_id (lexer, "EXPR")
- || !lex_force_match (lexer, T_LPAREN))
+ if (!lex_force_match_phrase (lexer, "=EXPR("))
{
free (name);
return false;
= ctables_find_postcompute (ct, lex_tokcstr (lexer));
if (!pc)
{
- msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
+ lex_error (lexer, _("Unknown computed category &%s."),
+ lex_tokcstr (lexer));
goto error;
}
lex_get (lexer);
double widths[2] = { SYSMIS, SYSMIS };
double units_per_inch = 72.0;
+ int start_ofs = lex_ofs (lexer);
while (lex_token (lexer) != T_SLASH)
{
if (lex_match_id (lexer, "MINCOLWIDTH"))
if (widths[0] != SYSMIS && widths[1] != SYSMIS
&& widths[0] > widths[1])
{
- msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
+ lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
+ _("MINCOLWIDTH must not be greater than "
+ "MAXCOLWIDTH."));
goto error;
}
lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
"SMISSING", "PCOMPUTE", "PPROPERTIES",
"WEIGHT", "HIDESMALLCOUNTS", "TABLE");
+ if (lex_match_id (lexer, "SLABELS")
+ || lex_match_id (lexer, "CLABELS")
+ || lex_match_id (lexer, "CRITERIA")
+ || lex_match_id (lexer, "CATEGORIES")
+ || lex_match_id (lexer, "TITLES")
+ || lex_match_id (lexer, "SIGTEST")
+ || lex_match_id (lexer, "COMPARETEST"))
+ lex_next_msg (lexer, SN, -1, -1,
+ _("TABLE must appear before this subcommand."));
goto error;
}
if (lex_token (lexer) == T_ENDCMD)
{
- if (!ctables_prepare_table (t))
+ if (!ctables_prepare_table (t, lexer))
goto error;
break;
}
}
else if (lex_match_id (lexer, "CLABELS"))
{
+ int start_ofs = lex_ofs (lexer) - 1;
if (lex_match_id (lexer, "AUTO"))
{
t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
"COLLABELS");
goto error;
}
+ int end_ofs = lex_ofs (lexer) - 1;
+
+ if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
+ && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
+ {
+ msg (SE, _("ROWLABELS and COLLABELS may not both be "
+ "specified."));
+
+ lex_ofs_msg (lexer, SN, t->clabels_start_ofs,
+ t->clabels_end_ofs,
+ _("This is the first specification."));
+ lex_ofs_msg (lexer, SN, start_ofs, end_ofs,
+ _("This is the second specification."));
+ goto error;
+ }
+
+ t->clabels_start_ofs = start_ofs;
+ t->clabels_end_ofs = end_ofs;
}
else if (lex_match_id (lexer, "CRITERIA"))
{
do
{
char **textp;
- if (lex_match_id (lexer, "CAPTION"))
+ if (lex_match_id (lexer, "CAPTIONS"))
textp = &t->caption;
- else if (lex_match_id (lexer, "CORNER"))
+ else if (lex_match_id (lexer, "CORNERS"))
textp = &t->corner;
- else if (lex_match_id (lexer, "TITLE"))
+ else if (lex_match_id (lexer, "TITLES"))
textp = &t->title;
else
{
}
else if (lex_match_id (lexer, "COMPARETEST"))
{
- int start_ofs = lex_ofs (lexer);
+ int start_ofs = lex_ofs (lexer) - 1;
if (!t->pairwise)
{
t->pairwise = xmalloc (sizeof *t->pairwise);
lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
"CRITERIA", "CATEGORIES", "TITLES",
"SIGTEST", "COMPARETEST");
+ if (lex_match_id (lexer, "FORMAT")
+ || lex_match_id (lexer, "VLABELS")
+ || lex_match_id (lexer, "MRSETS")
+ || lex_match_id (lexer, "SMISSING")
+ || lex_match_id (lexer, "PCOMPUTE")
+ || lex_match_id (lexer, "PPROPERTIES")
+ || lex_match_id (lexer, "WEIGHT")
+ || lex_match_id (lexer, "HIDESMALLCOUNTS"))
+ lex_next_msg (lexer, SN, -1, -1,
+ _("This subcommand must appear before TABLE."));
goto error;
}
}
if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW)
- {
- t->clabels_from_axis = PIVOT_AXIS_ROW;
- if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
- {
- msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
- goto error;
- }
- }
+ t->clabels_from_axis = PIVOT_AXIS_ROW;
else if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
t->clabels_from_axis = PIVOT_AXIS_COLUMN;
t->clabels_to_axis = t->label_axis[t->clabels_from_axis];
- if (!ctables_prepare_table (t))
+ if (!ctables_prepare_table (t, lexer))
goto error;
}
while (lex_token (lexer) != T_ENDCMD);