const struct ctables_summary_spec *src)
{
*dst = *src;
- dst->label = xstrdup (src->label);
+ dst->label = xstrdup_if_nonnull (src->label);
}
static void
}
static struct pivot_value *
-ctables_category_create_label (const struct ctables_category *cat,
- const struct variable *var,
- const union value *value)
+ctables_category_create_label__ (const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
{
return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
- : cat->type == CCT_POSTCOMPUTE && cat->pc->label
- ? pivot_value_new_user_text (cat->pc->label, SIZE_MAX)
: pivot_value_new_var_value (var, value));
}
+static struct pivot_value *
+ctables_postcompute_label (const struct ctables_categories *cats,
+ const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
+{
+ struct substring in = ss_cstr (cat->pc->label);
+ struct substring target = ss_cstr (")LABEL[");
+
+ struct string out = DS_EMPTY_INITIALIZER;
+ for (;;)
+ {
+ size_t chunk = ss_find_substring (in, target);
+ if (chunk == SIZE_MAX)
+ {
+ if (ds_is_empty (&out))
+ return pivot_value_new_user_text (in.string, in.length);
+ else
+ {
+ ds_put_substring (&out, in);
+ return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
+ }
+ }
+
+ ds_put_substring (&out, ss_head (in, chunk));
+ ss_advance (&in, chunk + target.length);
+
+ struct substring idx_s;
+ if (!ss_get_until (&in, ']', &idx_s))
+ goto error;
+ char *tail;
+ long int idx = strtol (idx_s.string, &tail, 10);
+ if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
+ goto error;
+
+ struct ctables_category *cat2 = &cats->cats[idx - 1];
+ struct pivot_value *label2
+ = ctables_category_create_label__ (cat2, var, value);
+ char *label2_s = pivot_value_to_string_defaults (label2);
+ ds_put_cstr (&out, label2_s);
+ free (label2_s);
+ pivot_value_destroy (label2);
+ }
+
+error:
+ ds_destroy (&out);
+ return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
+}
+
+static struct pivot_value *
+ctables_category_create_label (const struct ctables_categories *cats,
+ const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
+{
+ return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
+ ? ctables_postcompute_label (cats, cat, var, value)
+ : ctables_category_create_label__ (cat, var, value));
+}
+
static struct ctables_value *
ctables_value_find__ (struct ctables_table *t, const union value *value,
int width, unsigned int hash)
const struct ctables_categories *cats;
enum pivot_axis_type pc_a;
size_t pc_a_idx;
+ size_t summary_idx;
};
static double ctables_pcexpr_evaluate (
const struct ctables_table *t = s->table;
const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
- size_t j = 0 /* XXX */;
- return ctables_summary_value (tc, &tc->summaries[j], &specs->specs[j]);
+ return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
+ &specs->specs[ctx->summary_idx]);
}
static double
NOT_REACHED ();
}
+/* XXX what if there is a postcompute in more than one dimension?? */
+static const struct ctables_postcompute *
+ctables_cell_postcompute (const struct ctables_section *s,
+ const struct ctables_cell *cell,
+ enum pivot_axis_type *pc_a_p,
+ size_t *pc_a_idx_p)
+{
+ assert (cell->postcompute);
+ for (enum pivot_axis_type pc_a = 0; ; pc_a++)
+ {
+ assert (pc_a < PIVOT_N_AXES);
+ for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+ {
+ const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
+ if (cv->category->type == CCT_POSTCOMPUTE)
+ {
+ if (pc_a_p)
+ *pc_a_p = pc_a;
+ if (pc_a_idx_p)
+ *pc_a_idx_p = pc_a_idx;
+ return cv->category->pc;
+ }
+ }
+ }
+
+ NOT_REACHED ();
+}
+
static double
ctables_cell_calculate_postcompute (const struct ctables_section *s,
- const struct ctables_cell *cell)
+ const struct ctables_cell *cell,
+ const struct ctables_summary_spec *ss,
+ struct fmt_spec *format,
+ bool *is_ctables_format,
+ size_t summary_idx)
{
enum pivot_axis_type pc_a;
size_t pc_a_idx;
- const struct ctables_postcompute *pc;
- for (pc_a = 0; ; pc_a++)
+ const struct ctables_postcompute *pc = ctables_cell_postcompute (
+ s, cell, &pc_a, &pc_a_idx);
+
+ if (pc->specs)
{
- assert (pc_a < PIVOT_N_AXES);
- for (pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+ for (size_t i = 0; i < pc->specs->n; i++)
{
- const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
- if (cv->category->type == CCT_POSTCOMPUTE)
+ const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
+ if (ss->function == ss2->function
+ && ss->percentile == ss2->percentile)
{
- pc = cv->category->pc;
- goto found;
+ *format = ss2->format;
+ *is_ctables_format = ss2->is_ctables_format;
+ break;
}
}
}
-found: ;
const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
const struct ctables_categories *cats = s->table->categories[
.cats = cats,
.pc_a = pc_a,
.pc_a_idx = pc_a_idx,
+ .summary_idx = summary_idx,
};
return ctables_pcexpr_evaluate (&ctx, pc->expr);
}
const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
assert (cat != NULL);
pivot_category_create_leaf (d->root, ctables_category_create_label (
- cat, t->clabels_example, &value->value));
+ c, cat, t->clabels_example,
+ &value->value));
}
}
else if (level->type == CTL_CATEGORY)
{
const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
- label = ctables_category_create_label (cv->category,
- var, &cv->value);
+ label = ctables_category_create_label (
+ t->categories[var_get_dict_index (var)],
+ cv->category, var, &cv->value);
}
else
NOT_REACHED ();
const struct ctables_summary_spec *ss = &specs->specs[j];
+ struct fmt_spec format = specs->specs[j].format;
+ bool is_ctables_format = ss->is_ctables_format;
double d = (cell->postcompute
- ? ctables_cell_calculate_postcompute (s, cell)
- : ctables_summary_value (cell, &cell->summaries[j], ss));
+ ? ctables_cell_calculate_postcompute (
+ s, cell, ss, &format, &is_ctables_format, j)
+ : ctables_summary_value (cell, &cell->summaries[j],
+ ss));
+
struct pivot_value *value;
if (ct->hide_threshold != 0
&& d < ct->hide_threshold
- && (cell->postcompute
- ? false /* XXX */
- : ctables_summary_function_is_count (ss->function)))
+ && ctables_summary_function_is_count (ss->function))
{
value = pivot_value_new_user_text_nocopy (
xasprintf ("<%d", ct->hide_threshold));
value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
else if (d == SYSMIS && ct->missing)
value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
- else if (specs->specs[j].is_ctables_format)
+ else if (is_ctables_format)
{
char *s = data_out_stretchy (&(union value) { .f = d },
- "UTF-8",
- &specs->specs[j].format,
+ "UTF-8", &format,
&ct->ctables_formats, NULL);
value = pivot_value_new_user_text_nocopy (s);
}
else
{
value = pivot_value_new_number (d);
- value->numeric.format = specs->specs[j].format;
+ value->numeric.format = format;
}
pivot_table_put (pt, dindexes, n_dindexes, value);
}
};
static const struct operator *
-match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops)
+ctable_pcexpr_match_operator (struct lexer *lexer,
+ const struct operator ops[], size_t n_ops)
{
for (const struct operator *op = ops; op < ops + n_ops; op++)
if (lex_token (lexer) == op->token)
}
static struct ctables_pcexpr *
-parse_binary_operators__ (struct lexer *lexer, struct dictionary *dict,
- const struct operator ops[], size_t n_ops,
- parse_recursively_func *parse_next_level,
- const char *chain_warning,
- struct ctables_pcexpr *lhs)
+ctable_pcexpr_parse_binary_operators__ (
+ struct lexer *lexer, struct dictionary *dict,
+ const struct operator ops[], size_t n_ops,
+ parse_recursively_func *parse_next_level,
+ const char *chain_warning, struct ctables_pcexpr *lhs)
{
for (int op_count = 0; ; op_count++)
{
- const struct operator *op = match_operator (lexer, ops, n_ops);
+ const struct operator *op
+ = ctable_pcexpr_match_operator (lexer, ops, n_ops);
if (!op)
{
if (op_count > 1 && chain_warning)
}
static struct ctables_pcexpr *
-parse_binary_operators (struct lexer *lexer, struct dictionary *dict,
- const struct operator ops[], size_t n_ops,
- parse_recursively_func *parse_next_level,
- const char *chain_warning)
+ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
+ struct dictionary *dict,
+ const struct operator ops[], size_t n_ops,
+ parse_recursively_func *parse_next_level,
+ const char *chain_warning)
{
struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
if (!lhs)
return NULL;
- return parse_binary_operators__ (lexer, dict, ops, n_ops, parse_next_level,
- chain_warning, lhs);
+ return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
+ parse_next_level,
+ chain_warning, lhs);
}
-static struct ctables_pcexpr *parse_add (struct lexer *, struct dictionary *);
+static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
+ struct dictionary *);
static struct ctables_pcexpr
ctpo_cat_range (double low, double high)
}
static struct ctables_pcexpr *
-parse_primary (struct lexer *lexer, struct dictionary *dict)
+ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
{
int start_ofs = lex_ofs (lexer);
struct ctables_pcexpr e;
}
else if (lex_match (lexer, T_LPAREN))
{
- struct ctables_pcexpr *ep = parse_add (lexer, dict);
+ struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
if (!ep)
return NULL;
if (!lex_force_match (lexer, T_RPAREN))
}
static struct ctables_pcexpr *
-parse_exp (struct lexer *lexer, struct dictionary *dict)
+ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator op = { T_EXP, CTPO_POW };
"To disable this warning, insert parentheses.");
if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
- return parse_binary_operators (lexer, dict, &op, 1,
- parse_primary, chain_warning);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
+ ctable_pcexpr_parse_primary,
+ chain_warning);
/* Special case for situations like "-5**6", which must be parsed as
-(5**6). */
};
lex_get (lexer);
- struct ctables_pcexpr *node = parse_binary_operators__ (
- lexer, dict, &op, 1, parse_primary, chain_warning, lhs);
+ struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
+ lexer, dict, &op, 1,
+ ctable_pcexpr_parse_primary, chain_warning, lhs);
if (!node)
return NULL;
/* Parses the unary minus level. */
static struct ctables_pcexpr *
-parse_neg (struct lexer *lexer, struct dictionary *dict)
+ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
{
int start_ofs = lex_ofs (lexer);
if (!lex_match (lexer, T_DASH))
- return parse_exp (lexer, dict);
+ return ctable_pcexpr_parse_exp (lexer, dict);
- struct ctables_pcexpr *inner = parse_neg (lexer, dict);
+ struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
if (!inner)
return NULL;
/* Parses the multiplication and division level. */
static struct ctables_pcexpr *
-parse_mul (struct lexer *lexer, struct dictionary *dict)
+ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator ops[] =
{
{ T_SLASH, CTPO_DIV },
};
- return parse_binary_operators (lexer, dict, ops, sizeof ops / sizeof *ops,
- parse_neg, NULL);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
+ sizeof ops / sizeof *ops,
+ ctable_pcexpr_parse_neg, NULL);
}
/* Parses the addition and subtraction level. */
static struct ctables_pcexpr *
-parse_add (struct lexer *lexer, struct dictionary *dict)
+ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator ops[] =
{
{ T_NEG_NUM, CTPO_ADD },
};
- return parse_binary_operators (lexer, dict, ops, sizeof ops / sizeof *ops,
- parse_mul, NULL);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict,
+ ops, sizeof ops / sizeof *ops,
+ ctable_pcexpr_parse_mul, NULL);
}
static struct ctables_postcompute *
}
int expr_start = lex_ofs (lexer);
- struct ctables_pcexpr *expr = parse_add (lexer, dict);
+ struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
int expr_end = lex_ofs (lexer) - 1;
if (!expr || !lex_force_match (lexer, T_RPAREN))
{
/* Parse format. */
struct fmt_spec format;
- if (!parse_format_specifier (lexer, &format)
- || !fmt_check_output (&format)
- || !fmt_check_type_compat (&format, VAL_NUMERIC))
+ bool is_ctables_format;
+ if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
goto error;
if (sss->n >= sss->allocated)
.function = function,
.percentile = percentile,
.format = format,
+ .is_ctables_format = is_ctables_format,
};
}
return true;