1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/dataset.h"
20 #include "data/dictionary.h"
21 #include "data/mrset.h"
22 #include "language/command.h"
23 #include "language/lexer/format-parser.h"
24 #include "language/lexer/lexer.h"
25 #include "language/lexer/variable-parser.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/hmap.h"
28 #include "libpspp/message.h"
29 #include "output/pivot-table.h"
31 #include "gl/minmax.h"
32 #include "gl/xalloc.h"
35 #define _(msgid) gettext (msgid)
36 #define N_(msgid) (msgid)
40 CTVL_DEFAULT = SETTINGS_VALUE_SHOW_DEFAULT,
41 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
42 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
43 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
47 ctables_vlabel_unique (enum ctables_vlabel vlabel)
49 /* This ensures that all of the values are unique. */
63 struct pivot_table_look *look;
65 /* If this is NULL, zeros are displayed using the normal print format.
66 Otherwise, this string is displayed. */
69 /* If this is NULL, missing values are displayed using the normal print
70 format. Otherwise, this string is displayed. */
73 /* Indexed by variable dictionary index. */
74 enum ctables_vlabel *vlabels;
76 bool mrsets_count_duplicates; /* MRSETS. */
77 bool smissing_listwise; /* SMISSING. */
78 struct variable *base_weight; /* WEIGHT. */
79 int hide_threshold; /* HIDESMALLCOUNTS. */
81 struct ctables_table *tables;
85 struct ctables_postcompute
87 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
88 const char *name; /* Name, without leading &. */
90 struct ctables_postcompute_expr *expr;
93 bool hide_source_cats;
96 struct ctables_postcompute_expr
98 enum ctables_postcompute_op
106 /* XXX SUBTOTAL and HSUBTOTAL */
119 /* CTPO_CAT_NUMBER, CTPO_NUMBER. */
124 XXX what about string ranges? */
127 double low; /* -DBL_MAX for LO. */
128 double high; /* DBL_MAX for HIGH. */
132 /* CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW. */
133 struct ctables_postcompute_expr *subs[2];
137 enum ctables_label_position
146 struct ctables_axis *axes[PIVOT_N_AXES];
148 enum pivot_axis_type slabels_position;
149 bool slabels_visible;
151 enum ctables_label_position row_labels;
152 enum ctables_label_position col_labels;
162 struct ctables_chisq *chisq;
163 struct ctables_pairwise *pairwise;
166 /* Chi-square test (SIGTEST). */
174 /* Pairwise comparison test (COMPARETEST). */
175 struct ctables_pairwise
177 enum { PROP, MEAN } type;
180 bool meansvariance_allcats;
182 enum { BONFERRONI = 1, BH } adjust;
209 struct variable *var;
210 const struct mrset *mrset;
214 struct ctables_summary *summaries;
219 struct ctables_axis *subs[2];
223 static void ctables_axis_destroy (struct ctables_axis *);
233 /* All variables. */ \
234 S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT) \
235 S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT) \
236 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT) \
237 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT) \
238 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT) \
239 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT) \
240 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT) \
241 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT) \
242 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT) \
243 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT) \
244 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT) \
245 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT) \
246 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT) \
247 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT) \
248 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT) \
249 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT) \
250 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT) \
251 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT) \
252 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT) \
253 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT) \
254 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT) \
255 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT) \
256 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT) \
258 /* Scale variables, totals, and subtotals. */ \
259 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL) \
260 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL) \
261 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL) \
262 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL) \
263 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL) \
264 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL) \
265 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL) \
266 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL) \
267 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL) \
268 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL) \
269 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL) \
270 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT) \
271 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT) \
272 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT) \
273 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT) \
274 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL) \
275 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT) \
276 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT) \
277 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT) \
278 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT) \
279 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT) \
280 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT) \
281 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT) \
283 /* Multiple response sets. */ \
284 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT) \
285 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT) \
286 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT) \
287 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT) \
288 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT) \
289 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT) \
290 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT) \
291 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT) \
292 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT) \
293 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT) \
294 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT) \
295 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT) \
296 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT) \
297 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT) \
298 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT) \
299 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT) \
300 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT) \
301 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT) \
302 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT) \
303 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT) \
304 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT) \
305 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT)
307 enum ctables_summary_function
309 #define S(ENUM, NAME, LABEL, FORMAT) ENUM,
315 #define S(ENUM, NAME, LABEL, FORMAT) +1
316 N_CTSF_FUNCTIONS = SUMMARIES
320 struct ctables_summary
322 enum ctables_summary_function function;
323 double percentile; /* CTSF_PTILE only. */
325 struct fmt_spec format; /* XXX extra CTABLES formats */
329 ctables_summary_uninit (struct ctables_summary *s)
336 parse_col_width (struct lexer *lexer, const char *name, double *width)
338 lex_match (lexer, T_EQUALS);
339 if (lex_match_id (lexer, "DEFAULT"))
341 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
343 *width = lex_number (lexer);
353 parse_bool (struct lexer *lexer, bool *b)
355 if (lex_match_id (lexer, "NO"))
357 else if (lex_match_id (lexer, "YES"))
361 lex_error_expecting (lexer, "YES", "NO");
368 parse_ctables_summary_function (struct lexer *lexer,
369 enum ctables_summary_function *f)
373 enum ctables_summary_function function;
374 struct substring name;
376 static struct pair names[] = {
377 #define S(ENUM, NAME, LABEL, FORMAT) { ENUM, SS_LITERAL_INITIALIZER (NAME) },
380 /* The .COUNT suffix may be omitted. */
381 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _)
382 S(CTSF_COLPCT_COUNT, "COLPCT", _, _)
383 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _)
384 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _)
385 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _)
386 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _)
387 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _)
391 if (!lex_force_id (lexer))
394 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
395 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
397 *f = names[i].function;
401 lex_error (lexer, _("Expecting summary function name."));
406 ctables_axis_destroy (struct ctables_axis *axis)
415 for (size_t i = 0; i < axis->n_summaries; i++)
416 ctables_summary_uninit (&axis->summaries[i]);
417 free (axis->summaries);
422 ctables_axis_destroy (axis->subs[0]);
423 ctables_axis_destroy (axis->subs[1]);
429 static struct ctables_axis *
430 ctables_axis_new_nonterminal (enum ctables_axis_op op,
431 struct ctables_axis *sub0,
432 struct ctables_axis *sub1)
434 struct ctables_axis *axis = xmalloc (sizeof *axis);
435 *axis = (struct ctables_axis) { .op = op, .subs = { sub0, sub1 } };
439 struct ctables_axis_parse_ctx
442 struct dictionary *dict;
444 struct ctables_table *t;
447 static struct ctables_summary *
448 add_summary (struct ctables_axis *axis, enum ctables_summary_function function,
449 double percentile, size_t *allocated_summaries)
451 if (axis->n_summaries >= *allocated_summaries)
452 axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
453 sizeof *axis->summaries);
455 static const char *default_labels[] = {
456 #define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = LABEL,
460 char *label = (function == CTSF_PTILE
461 ? xasprintf (_("Percentile %.2f"), percentile)
462 : xstrdup (gettext (default_labels[function])));
464 static const enum ctables_format default_formats[] = {
465 #define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = FORMAT,
469 struct fmt_spec format;
470 switch (default_formats[function])
473 format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
477 format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
481 format = *(axis->op == CTAO_VAR
482 ? var_get_print_format (axis->var)
483 : var_get_print_format (axis->mrset->vars[0]));
490 struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
491 *s = (struct ctables_summary) {
492 .function = function,
493 .percentile = percentile,
500 static struct ctables_axis *ctables_axis_parse_stack (
501 struct ctables_axis_parse_ctx *);
503 static struct ctables_axis *
504 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
506 if (lex_match (ctx->lexer, T_LPAREN))
508 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
509 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
511 ctables_axis_destroy (sub);
517 if (!lex_force_id (ctx->lexer))
520 const struct mrset *mrset = NULL;
521 struct variable *var = NULL;
522 if (ss_starts_with (lex_tokss (ctx->lexer), ss_cstr ("$")))
524 mrset = dict_lookup_mrset (ctx->dict, lex_tokcstr (ctx->lexer));
527 lex_error (ctx->lexer, _("'%s' is not the name of a "
528 "multiple-response set in the active file "
530 lex_tokcstr (ctx->lexer));
533 lex_get (ctx->lexer);
537 var = parse_variable (ctx->lexer, ctx->dict);
542 struct ctables_axis *axis = xmalloc (sizeof *axis);
544 *axis = (struct ctables_axis) { .op = CTAO_MRSET, .mrset = mrset };
546 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
548 /* XXX should figure out default measures by reading data */
549 axis->scale = (mrset ? false
550 : lex_match_phrase (ctx->lexer, "[S]") ? true
551 : lex_match_phrase (ctx->lexer, "[C]") ? false
552 : var_get_measure (var) == MEASURE_SCALE);
554 size_t allocated_summaries = 0;
555 if (lex_match (ctx->lexer, T_LBRACK))
559 enum ctables_summary_function function;
560 if (!parse_ctables_summary_function (ctx->lexer, &function))
563 double percentile = 0;
564 if (function == CTSF_PTILE)
566 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
568 percentile = lex_number (ctx->lexer);
569 lex_get (ctx->lexer);
572 struct ctables_summary *s = add_summary (axis, function, percentile,
573 &allocated_summaries);
574 if (lex_is_string (ctx->lexer))
577 s->label = ss_xstrdup (lex_tokss (ctx->lexer));
578 lex_get (ctx->lexer);
580 if (lex_token (ctx->lexer) == T_ID)
582 if (!parse_format_specifier (ctx->lexer, &s->format)
583 || !fmt_check_output (&s->format)
584 || !fmt_check_type_compat (&s->format, VAL_NUMERIC))
587 lex_match (ctx->lexer, T_COMMA);
589 while (!lex_match (ctx->lexer, T_RBRACK));
592 add_summary (axis, axis->scale ? CTSF_MEAN : CTSF_COUNT, 0,
593 &allocated_summaries);
597 ctables_axis_destroy (axis);
601 static struct ctables_axis *
602 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
604 struct ctables_axis *lhs = ctables_axis_parse_primary (ctx);
608 while (lex_match (ctx->lexer, T_GT))
610 struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
614 lhs = ctables_axis_new_nonterminal (CTAO_NEST, lhs, rhs);
620 static struct ctables_axis *
621 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
623 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
627 while (lex_match (ctx->lexer, T_PLUS))
629 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
633 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs);
640 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
641 struct ctables *ct, struct ctables_table *t,
642 enum pivot_axis_type a)
644 if (lex_token (lexer) == T_BY
645 || lex_token (lexer) == T_SLASH
646 || lex_token (lexer) == T_ENDCMD)
649 struct ctables_axis_parse_ctx ctx = {
655 t->axes[a] = ctables_axis_parse_stack (&ctx);
656 return t->axes[a] != NULL;
660 ctables_chisq_destroy (struct ctables_chisq *chisq)
666 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
672 ctables_table_uninit (struct ctables_table *t)
677 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
678 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
679 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
683 ctables_chisq_destroy (t->chisq);
684 ctables_pairwise_destroy (t->pairwise);
688 ctables_destroy (struct ctables *ct)
693 pivot_table_look_unref (ct->look);
697 for (size_t i = 0; i < ct->n_tables; i++)
698 ctables_table_uninit (&ct->tables[i]);
704 cmd_ctables (struct lexer *lexer, struct dataset *ds)
706 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
707 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
708 for (size_t i = 0; i < n_vars; i++)
709 vlabels[i] = CTVL_DEFAULT;
711 struct ctables *ct = xmalloc (sizeof *ct);
712 *ct = (struct ctables) {
713 .look = pivot_table_look_unshare (pivot_table_look_ref (
714 pivot_table_look_get_default ())),
719 if (!lex_force_match (lexer, T_SLASH))
722 while (!lex_match_id (lexer, "TABLE"))
724 if (lex_match_id (lexer, "FORMAT"))
726 double widths[2] = { SYSMIS, SYSMIS };
727 double units_per_inch = 72.0;
729 while (lex_token (lexer) != T_SLASH)
731 if (lex_match_id (lexer, "MINCOLWIDTH"))
733 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
736 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
738 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
741 else if (lex_match_id (lexer, "UNITS"))
743 lex_match (lexer, T_EQUALS);
744 if (lex_match_id (lexer, "POINTS"))
745 units_per_inch = 72.0;
746 else if (lex_match_id (lexer, "INCHES"))
747 units_per_inch = 1.0;
748 else if (lex_match_id (lexer, "CM"))
749 units_per_inch = 2.54;
752 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
756 else if (lex_match_id (lexer, "EMPTY"))
761 lex_match (lexer, T_EQUALS);
762 if (lex_match_id (lexer, "ZERO"))
766 else if (lex_match_id (lexer, "BLANK"))
767 ct->zero = xstrdup ("");
768 else if (lex_force_string (lexer))
770 ct->zero = ss_xstrdup (lex_tokss (lexer));
776 else if (lex_match_id (lexer, "MISSING"))
778 lex_match (lexer, T_EQUALS);
779 if (!lex_force_string (lexer))
783 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
784 ? ss_xstrdup (lex_tokss (lexer))
790 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
791 "UNITS", "EMPTY", "MISSING");
796 if (widths[0] != SYSMIS && widths[1] != SYSMIS
797 && widths[0] > widths[1])
799 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
803 for (size_t i = 0; i < 2; i++)
804 if (widths[i] != SYSMIS)
806 int *wr = ct->look->width_ranges[TABLE_HORZ];
807 wr[i] = widths[i] / units_per_inch * 96.0;
812 else if (lex_match_id (lexer, "VLABELS"))
814 if (!lex_force_match_id (lexer, "VARIABLES"))
816 lex_match (lexer, T_EQUALS);
818 struct variable **vars;
820 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
824 if (!lex_force_match_id (lexer, "DISPLAY"))
829 lex_match (lexer, T_EQUALS);
831 enum ctables_vlabel vlabel;
832 if (lex_match_id (lexer, "DEFAULT"))
833 vlabel = CTVL_DEFAULT;
834 else if (lex_match_id (lexer, "NAME"))
836 else if (lex_match_id (lexer, "LABEL"))
838 else if (lex_match_id (lexer, "BOTH"))
840 else if (lex_match_id (lexer, "NONE"))
844 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
850 for (size_t i = 0; i < n_vars; i++)
851 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
854 else if (lex_match_id (lexer, "MRSETS"))
856 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
858 lex_match (lexer, T_EQUALS);
859 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
862 else if (lex_match_id (lexer, "SMISSING"))
864 if (lex_match_id (lexer, "VARIABLE"))
865 ct->smissing_listwise = false;
866 else if (lex_match_id (lexer, "LISTWISE"))
867 ct->smissing_listwise = true;
870 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
875 else if (lex_match_id (lexer, "WEIGHT"))
877 if (!lex_force_match_id (lexer, "VARIABLE"))
879 lex_match (lexer, T_EQUALS);
880 ct->base_weight = parse_variable (lexer, dataset_dict (ds));
881 if (!ct->base_weight)
884 else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
886 if (!lex_force_match_id (lexer, "COUNT"))
888 lex_match (lexer, T_EQUALS);
889 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
891 ct->hide_threshold = lex_integer (lexer);
896 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
897 "SMISSING", "PCOMPUTE", "PPROPERTIES",
898 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
902 if (!lex_force_match (lexer, T_SLASH))
906 size_t allocated_tables = 0;
909 if (ct->n_tables >= allocated_tables)
910 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
913 struct ctables_table *t = &ct->tables[ct->n_tables++];
914 *t = (struct ctables_table) {
915 .slabels_position = PIVOT_AXIS_COLUMN,
916 .slabels_visible = true,
917 .row_labels = CTLP_NORMAL,
918 .col_labels = CTLP_NORMAL,
922 lex_match (lexer, T_EQUALS);
923 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
926 if (lex_match (lexer, T_BY))
928 if (!ctables_axis_parse (lexer, dataset_dict (ds),
929 ct, t, PIVOT_AXIS_COLUMN))
932 if (lex_match (lexer, T_BY))
934 if (!ctables_axis_parse (lexer, dataset_dict (ds),
935 ct, t, PIVOT_AXIS_LAYER))
939 if (lex_token (lexer) == T_ENDCMD)
941 if (!lex_force_match (lexer, T_SLASH))
944 /* XXX Validate axes. */
945 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
947 if (lex_match_id (lexer, "SLABELS"))
949 while (lex_token (lexer) != T_SLASH)
951 if (lex_match_id (lexer, "POSITION"))
953 lex_match (lexer, T_EQUALS);
954 if (lex_match_id (lexer, "COLUMN"))
955 t->slabels_position = PIVOT_AXIS_COLUMN;
956 else if (lex_match_id (lexer, "ROW"))
957 t->slabels_position = PIVOT_AXIS_ROW;
958 else if (lex_match_id (lexer, "LAYER"))
959 t->slabels_position = PIVOT_AXIS_LAYER;
962 lex_error_expecting (lexer, "COLUMN", "ROW",
967 else if (lex_match_id (lexer, "VISIBLE"))
969 lex_match (lexer, T_EQUALS);
970 if (!parse_bool (lexer, &t->slabels_visible))
975 lex_error_expecting (lexer, "POSITION", "VISIBLE");
980 else if (lex_match_id (lexer, "CLABELS"))
982 while (lex_token (lexer) != T_SLASH)
984 if (lex_match_id (lexer, "AUTO"))
985 t->row_labels = t->col_labels = CTLP_NORMAL;
986 else if (lex_match_id (lexer, "ROWLABELS"))
988 lex_match (lexer, T_EQUALS);
989 if (lex_match_id (lexer, "OPPOSITE"))
990 t->row_labels = CTLP_OPPOSITE;
991 else if (lex_match_id (lexer, "LAYER"))
992 t->row_labels = CTLP_LAYER;
995 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
999 else if (lex_match_id (lexer, "COLLABELS"))
1001 lex_match (lexer, T_EQUALS);
1002 if (lex_match_id (lexer, "OPPOSITE"))
1003 t->col_labels = CTLP_OPPOSITE;
1004 else if (lex_match_id (lexer, "LAYER"))
1005 t->col_labels = CTLP_LAYER;
1008 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1014 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
1020 else if (lex_match_id (lexer, "CRITERIA"))
1022 if (!lex_force_match_id (lexer, "CILEVEL"))
1024 lex_match (lexer, T_EQUALS);
1026 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
1028 t->cilevel = lex_number (lexer);
1031 else if (lex_match_id (lexer, "TITLES"))
1036 if (lex_match_id (lexer, "CAPTION"))
1037 textp = &t->caption;
1038 else if (lex_match_id (lexer, "CORNER"))
1040 else if (lex_match_id (lexer, "TITLE"))
1044 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
1047 lex_match (lexer, T_EQUALS);
1049 struct string s = DS_EMPTY_INITIALIZER;
1050 while (lex_is_string (lexer))
1052 if (!ds_is_empty (&s))
1053 ds_put_byte (&s, ' ');
1054 ds_put_substring (&s, lex_tokss (lexer));
1058 *textp = ds_steal_cstr (&s);
1060 while (lex_token (lexer) != T_SLASH
1061 && lex_token (lexer) != T_ENDCMD);
1063 else if (lex_match_id (lexer, "SIGTEST"))
1067 t->chisq = xmalloc (sizeof *t->chisq);
1068 *t->chisq = (struct ctables_chisq) {
1070 .include_mrsets = true,
1071 .all_visible = true,
1077 if (lex_match_id (lexer, "TYPE"))
1079 lex_match (lexer, T_EQUALS);
1080 if (!lex_force_match_id (lexer, "CHISQUARE"))
1083 else if (lex_match_id (lexer, "ALPHA"))
1085 lex_match (lexer, T_EQUALS);
1086 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
1088 t->chisq->alpha = lex_number (lexer);
1091 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1093 lex_match (lexer, T_EQUALS);
1094 if (parse_bool (lexer, &t->chisq->include_mrsets))
1097 else if (lex_match_id (lexer, "CATEGORIES"))
1099 lex_match (lexer, T_EQUALS);
1100 if (lex_match_id (lexer, "ALLVISIBLE"))
1101 t->chisq->all_visible = true;
1102 else if (lex_match_id (lexer, "SUBTOTALS"))
1103 t->chisq->all_visible = false;
1106 lex_error_expecting (lexer,
1107 "ALLVISIBLE", "SUBTOTALS");
1113 lex_error_expecting (lexer, "TYPE", "ALPHA",
1114 "INCLUDEMRSETS", "CATEGORIES");
1118 while (lex_token (lexer) != T_SLASH
1119 && lex_token (lexer) != T_ENDCMD);
1121 else if (lex_match_id (lexer, "COMPARETEST"))
1125 t->pairwise = xmalloc (sizeof *t->pairwise);
1126 *t->pairwise = (struct ctables_pairwise) {
1128 .alpha = { .05, .05 },
1129 .adjust = BONFERRONI,
1130 .include_mrsets = true,
1131 .meansvariance_allcats = true,
1132 .all_visible = true,
1141 if (lex_match_id (lexer, "TYPE"))
1143 lex_match (lexer, T_EQUALS);
1144 if (lex_match_id (lexer, "PROP"))
1145 t->pairwise->type = PROP;
1146 else if (lex_match_id (lexer, "MEAN"))
1147 t->pairwise->type = MEAN;
1150 lex_error_expecting (lexer, "PROP", "MEAN");
1154 else if (lex_match_id (lexer, "ALPHA"))
1156 lex_match (lexer, T_EQUALS);
1158 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1160 double a0 = lex_number (lexer);
1163 lex_match (lexer, T_COMMA);
1164 if (lex_is_number (lexer))
1166 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1168 double a1 = lex_number (lexer);
1171 t->pairwise->alpha[0] = MIN (a0, a1);
1172 t->pairwise->alpha[1] = MAX (a0, a1);
1175 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1177 else if (lex_match_id (lexer, "ADJUST"))
1179 lex_match (lexer, T_EQUALS);
1180 if (lex_match_id (lexer, "BONFERRONI"))
1181 t->pairwise->adjust = BONFERRONI;
1182 else if (lex_match_id (lexer, "BH"))
1183 t->pairwise->adjust = BH;
1184 else if (lex_match_id (lexer, "NONE"))
1185 t->pairwise->adjust = 0;
1188 lex_error_expecting (lexer, "BONFERRONI", "BH",
1193 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1195 lex_match (lexer, T_EQUALS);
1196 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1199 else if (lex_match_id (lexer, "MEANSVARIANCE"))
1201 lex_match (lexer, T_EQUALS);
1202 if (lex_match_id (lexer, "ALLCATS"))
1203 t->pairwise->meansvariance_allcats = true;
1204 else if (lex_match_id (lexer, "TESTEDCATS"))
1205 t->pairwise->meansvariance_allcats = false;
1208 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1212 else if (lex_match_id (lexer, "CATEGORIES"))
1214 lex_match (lexer, T_EQUALS);
1215 if (lex_match_id (lexer, "ALLVISIBLE"))
1216 t->pairwise->all_visible = true;
1217 else if (lex_match_id (lexer, "SUBTOTALS"))
1218 t->pairwise->all_visible = false;
1221 lex_error_expecting (lexer, "ALLVISIBLE",
1226 else if (lex_match_id (lexer, "MERGE"))
1228 lex_match (lexer, T_EQUALS);
1229 if (!parse_bool (lexer, &t->pairwise->merge))
1232 else if (lex_match_id (lexer, "STYLE"))
1234 lex_match (lexer, T_EQUALS);
1235 if (lex_match_id (lexer, "APA"))
1236 t->pairwise->apa_style = true;
1237 else if (lex_match_id (lexer, "SIMPLE"))
1238 t->pairwise->apa_style = false;
1241 lex_error_expecting (lexer, "APA", "SIMPLE");
1245 else if (lex_match_id (lexer, "SHOWSIG"))
1247 lex_match (lexer, T_EQUALS);
1248 if (!parse_bool (lexer, &t->pairwise->show_sig))
1253 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1254 "INCLUDEMRSETS", "MEANSVARIANCE",
1255 "CATEGORIES", "MERGE", "STYLE",
1260 while (lex_token (lexer) != T_SLASH
1261 && lex_token (lexer) != T_ENDCMD);
1265 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1266 "CRITERIA", "CATEGORIES", "TITLES",
1267 "SIGTEST", "COMPARETEST");
1272 while (lex_token (lexer) != T_ENDCMD);
1273 ctables_destroy (ct);
1277 ctables_destroy (ct);