more CTABLES syntax and tests
[pspp] / src / language / stats / ctables.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2021 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
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"
30
31 #include "gl/minmax.h"
32 #include "gl/xalloc.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36 #define N_(msgid) (msgid)
37
38 enum ctables_vlabel
39   {
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,
44     CTVL_NONE,
45   };
46 static void UNUSED
47 ctables_vlabel_unique (enum ctables_vlabel vlabel)
48 {
49   /* This ensures that all of the values are unique. */
50   switch (vlabel)
51     {
52     case CTVL_DEFAULT:
53     case CTVL_NAME:
54     case CTVL_LABEL:
55     case CTVL_BOTH:
56     case CTVL_NONE:
57       abort ();
58     }
59 }
60
61 /* XXX:
62    - unweighted summaries (U*)
63    - lower confidence limits (*.LCL)
64    - upper confidence limits (*.UCL)
65    - standard error (*.SE)
66  */
67 #define SUMMARIES                                                       \
68     /* All variables. */                                                \
69     S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL)            \
70     S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
71     S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
72     S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
73     S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
74     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
75     S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
76     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
77     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
78     S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
79     S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
80     S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
81     S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
82     S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
83     S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
84     S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
85     S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
86     S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
87     S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
88     S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
89     S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
90     S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
91     S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
92                                                                         \
93     /* Scale variables, totals, and subtotals. */                       \
94     S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE)  \
95     S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE)           \
96     S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE)     \
97     S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE)  \
98     S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE)  \
99     S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE)           \
100     S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE)   \
101     S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE)        \
102     S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
103     S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
104     S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE)              \
105     S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE)      \
106     S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
107     S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE)      \
108     S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
109     S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
110     S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
111     S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
112     S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
113     S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
114     S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
115     S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
116     S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
117                                                                         \
118     /* Multiple response sets. */                                       \
119   S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
120     S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
121     S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
122     S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
123     S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
124     S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
125     S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
126     S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
127     S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
128     S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
129     S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
130     S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
131     S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
132     S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
133     S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
134     S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
135     S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
136     S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
137     S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
138     S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
139     S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
140     S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
141
142 enum ctables_summary_function
143   {
144 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
145     SUMMARIES
146 #undef S
147   };
148
149 enum {
150 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
151   N_CTSF_FUNCTIONS = SUMMARIES
152 #undef S
153 };
154
155 struct ctables
156   {
157     struct pivot_table_look *look;
158
159     /* If this is NULL, zeros are displayed using the normal print format.
160        Otherwise, this string is displayed. */
161     char *zero;
162
163     /* If this is NULL, missing values are displayed using the normal print
164        format.  Otherwise, this string is displayed. */
165     char *missing;
166
167     /* Indexed by variable dictionary index. */
168     enum ctables_vlabel *vlabels;
169
170     bool mrsets_count_duplicates; /* MRSETS. */
171     bool smissing_listwise;       /* SMISSING. */
172     struct variable *base_weight; /* WEIGHT. */
173     int hide_threshold;           /* HIDESMALLCOUNTS. */
174
175     struct ctables_table *tables;
176     size_t n_tables;
177   };
178
179 struct ctables_postcompute
180   {
181     struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
182     const char *name;           /* Name, without leading &. */
183
184     struct ctables_postcompute_expr *expr;
185     char *label;
186     /* XXX FORMAT */
187     bool hide_source_cats;
188   };
189
190 struct ctables_postcompute_expr
191   {
192     enum ctables_postcompute_op
193       {
194         /* Terminals. */
195         CTPO_CAT_NUMBER,
196         CTPO_CAT_STRING,
197         CTPO_CAT_RANGE,
198         CTPO_CAT_MISSING,
199         /* XXX OTHERNM */
200         /* XXX SUBTOTAL and HSUBTOTAL */
201
202         /* Nonterminals. */
203         CTPO_ADD,
204         CTPO_SUB,
205         CTPO_MUL,
206         CTPO_DIV,
207         CTPO_POW,
208       }
209     op;
210
211     union
212       {
213         /* CTPO_CAT_NUMBER, CTPO_NUMBER. */
214         double number;
215
216         /* CTPO_CAT_RANGE.
217
218            XXX what about string ranges? */
219         struct
220           {
221             double low;         /* -DBL_MAX for LO. */
222             double high;        /* DBL_MAX for HIGH. */
223           }
224         range;
225
226         /* CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW. */
227         struct ctables_postcompute_expr *subs[2];
228       };
229   };
230
231 enum ctables_label_position
232   {
233     CTLP_NORMAL,
234     CTLP_OPPOSITE,
235     CTLP_LAYER,
236   };
237
238 struct ctables_table
239   {
240     struct ctables_axis *axes[PIVOT_N_AXES];
241
242     enum pivot_axis_type slabels_position;
243     bool slabels_visible;
244
245     enum ctables_label_position row_labels;
246     enum ctables_label_position col_labels;
247
248     /* Indexed by variable dictionary index. */
249     struct ctables_categories **categories;
250     size_t n_categories;
251
252     double cilevel;
253
254     char *caption;
255     char *corner;
256     char *title;
257
258     struct ctables_chisq *chisq;
259     struct ctables_pairwise *pairwise;
260   };
261
262 struct ctables_var
263   {
264     bool is_mrset;
265     union
266       {
267         struct variable *var;
268         const struct mrset *mrset;
269       };
270   };
271
272 static const struct fmt_spec *
273 ctables_var_get_print_format (const struct ctables_var *var)
274 {
275   return (var->is_mrset
276           ? var_get_print_format (var->mrset->vars[0])
277           : var_get_print_format (var->var));
278 }
279
280 struct ctables_categories
281   {
282     size_t n_refs;
283
284     /* Explicit categories. */
285     struct ctables_cat_value *values;
286     size_t n_values;
287
288     /* Implicit categories. */
289     bool sort_ascending;
290     bool include_missing;
291     enum { CTCS_VALUE, CTCS_LABEL, CTCS_FUNCTION } key;
292     enum ctables_summary_function sort_func;
293     struct variable *sort_func_var;
294     double percentile;
295
296     /* Totals. */
297     bool show_totals;
298     bool totals_before;
299     char *total_label;
300
301     /* Empty categories. */
302     bool show_empty;
303   };
304
305 struct ctables_cat_value
306   {
307     enum ctables_cat_value_type
308       {
309         CCVT_NUMBER,
310         CCVT_STRING,
311         CCVT_RANGE,
312         CCVT_MISSING,
313         CCVT_OTHERNM,
314         CCVT_SUBTOTAL,
315         CCVT_HSUBTOTAL,
316       }
317     type;
318
319     union
320       {
321         double number;          /* CCVT_NUMBER. */
322         char *string;           /* CCVT_STRING. */
323         double range[2];        /* CCVT_RANGE. */
324         char *subtotal_label;   /* CCVT_SUBTOTAL, CCVT_HSUBTOTAL. */
325       };
326   };
327
328 static void
329 ctables_cat_value_uninit (struct ctables_cat_value *cv)
330 {
331   if (!cv)
332     return;
333
334   switch (cv->type)
335     {
336     case CCVT_NUMBER:
337     case CCVT_RANGE:
338     case CCVT_MISSING:
339     case CCVT_OTHERNM:
340       break;
341
342     case CCVT_STRING:
343       free (cv->string);
344       break;
345
346     case CCVT_SUBTOTAL:
347     case CCVT_HSUBTOTAL:
348       free (cv->subtotal_label);
349     }
350 }
351
352 static void
353 ctables_categories_unref (struct ctables_categories *c)
354 {
355   if (!c)
356     return;
357
358   assert (c->n_refs > 0);
359   if (--c->n_refs)
360     return;
361
362   for (size_t i = 0; i < c->n_values; i++)
363     ctables_cat_value_uninit (&c->values[i]);
364   free (c->values);
365   free (c->total_label);
366   free (c);
367 }
368
369 /* Chi-square test (SIGTEST). */
370 struct ctables_chisq
371   {
372     double alpha;
373     bool include_mrsets;
374     bool all_visible;
375   };
376
377 /* Pairwise comparison test (COMPARETEST). */
378 struct ctables_pairwise
379   {
380     enum { PROP, MEAN } type;
381     double alpha[2];
382     bool include_mrsets;
383     bool meansvariance_allcats;
384     bool all_visible;
385     enum { BONFERRONI = 1, BH } adjust;
386     bool merge;
387     bool apa_style;
388     bool show_sig;
389   };
390
391 struct ctables_axis
392   {
393     enum ctables_axis_op
394       {
395         /* Terminals. */
396         CTAO_VAR,
397
398         /* Nonterminals. */
399         CTAO_STACK,             /* + */
400         CTAO_NEST,              /* > */
401       }
402     op;
403
404     union
405       {
406         /* Terminals. */
407         struct
408           {
409             struct ctables_var var;
410             bool scale;
411             struct ctables_summary *summaries;
412             size_t n_summaries;
413           };
414
415         /* Nonterminals. */
416         struct ctables_axis *subs[2];
417       };
418   };
419
420 static void ctables_axis_destroy (struct ctables_axis *);
421
422 enum ctables_format
423   {
424     CTF_COUNT,
425     CTF_PERCENT,
426     CTF_GENERAL
427   };
428
429 enum ctables_function_availability
430   {
431     CTFA_ALL,                /* Any variables. */
432     CTFA_SCALE,              /* Only scale variables, totals, and subtotals. */
433     CTFA_MRSETS,             /* Only multiple-response sets */
434   };
435
436 struct ctables_summary
437   {
438     enum ctables_summary_function function;
439     double percentile;          /* CTSF_PTILE only. */
440     char *label;
441     struct fmt_spec format;     /* XXX extra CTABLES formats */
442   };
443
444 static void
445 ctables_summary_uninit (struct ctables_summary *s)
446 {
447   if (s)
448     free (s->label);
449 }
450
451 static bool
452 parse_col_width (struct lexer *lexer, const char *name, double *width)
453 {
454   lex_match (lexer, T_EQUALS);
455   if (lex_match_id (lexer, "DEFAULT"))
456     *width = SYSMIS;
457   else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
458     {
459       *width = lex_number (lexer);
460       lex_get (lexer);
461     }
462   else
463     return false;
464
465   return true;
466 }
467
468 static bool
469 parse_bool (struct lexer *lexer, bool *b)
470 {
471   if (lex_match_id (lexer, "NO"))
472     *b = false;
473   else if (lex_match_id (lexer, "YES"))
474     *b = true;
475   else
476     {
477       lex_error_expecting (lexer, "YES", "NO");
478       return false;
479     }
480   return true;
481 }
482
483 static enum ctables_function_availability
484 ctables_function_availability (enum ctables_summary_function f)
485 {
486   static enum ctables_function_availability availability[] = {
487 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
488     SUMMARIES
489 #undef S
490   };
491
492   return availability[f];
493 }
494
495 static bool
496 parse_ctables_summary_function (struct lexer *lexer,
497                                 enum ctables_summary_function *f)
498 {
499   struct pair
500     {
501       enum ctables_summary_function function;
502       struct substring name;
503     };
504   static struct pair names[] = {
505 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
506     { ENUM, SS_LITERAL_INITIALIZER (NAME) },
507     SUMMARIES
508
509     /* The .COUNT suffix may be omitted. */
510     S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
511     S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
512     S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
513     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
514     S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
515     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
516     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
517 #undef S
518   };
519
520   if (!lex_force_id (lexer))
521     return false;
522
523   for (size_t i = 0; i < sizeof names / sizeof *names; i++)
524     if (ss_equals_case (names[i].name, lex_tokss (lexer)))
525       {
526         *f = names[i].function;
527         return true;
528       }
529
530   lex_error (lexer, _("Expecting summary function name."));
531   return false;
532 }
533
534 static void
535 ctables_axis_destroy (struct ctables_axis *axis)
536 {
537   if (!axis)
538     return;
539
540   switch (axis->op)
541     {
542     case CTAO_VAR:
543       for (size_t i = 0; i < axis->n_summaries; i++)
544         ctables_summary_uninit (&axis->summaries[i]);
545       free (axis->summaries);
546       break;
547
548     case CTAO_STACK:
549     case CTAO_NEST:
550       ctables_axis_destroy (axis->subs[0]);
551       ctables_axis_destroy (axis->subs[1]);
552       break;
553     }
554   free (axis);
555 }
556
557 static struct ctables_axis *
558 ctables_axis_new_nonterminal (enum ctables_axis_op op,
559                               struct ctables_axis *sub0,
560                               struct ctables_axis *sub1)
561 {
562   struct ctables_axis *axis = xmalloc (sizeof *axis);
563   *axis = (struct ctables_axis) { .op = op, .subs = { sub0, sub1 } };
564   return axis;
565 }
566
567 struct ctables_axis_parse_ctx
568   {
569     struct lexer *lexer;
570     struct dictionary *dict;
571     struct ctables *ct;
572     struct ctables_table *t;
573   };
574
575 static struct ctables_summary *
576 add_summary (struct ctables_axis *axis, enum ctables_summary_function function,
577              double percentile, size_t *allocated_summaries)
578 {
579   if (axis->n_summaries >= *allocated_summaries)
580     axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
581                                   sizeof *axis->summaries);
582
583   static const char *default_labels[] = {
584 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
585     SUMMARIES
586 #undef S
587   };
588   char *label = (function == CTSF_PTILE
589                  ? xasprintf (_("Percentile %.2f"), percentile)
590                  : xstrdup (gettext (default_labels[function])));
591
592   static const enum ctables_format default_formats[] = {
593 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
594     SUMMARIES
595 #undef S
596   };
597   struct fmt_spec format;
598   switch (default_formats[function])
599     {
600     case CTF_COUNT:
601       format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
602       break;
603
604     case CTF_PERCENT:
605       format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
606       break;
607
608     case CTF_GENERAL:
609       format = *ctables_var_get_print_format (&axis->var);
610       break;
611
612     default:
613       NOT_REACHED ();
614     }
615
616   struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
617   *s = (struct ctables_summary) {
618     .function = function,
619     .percentile = percentile,
620     .label = label,
621     .format = format,
622   };
623   return s;
624 }
625
626 static struct ctables_axis *ctables_axis_parse_stack (
627   struct ctables_axis_parse_ctx *);
628
629 static bool
630 ctables_var_parse (struct lexer *lexer, struct dictionary *dict,
631                    struct ctables_var *var)
632 {
633   if (ss_starts_with (lex_tokss (lexer), ss_cstr ("$")))
634     {
635       *var = (struct ctables_var) {
636         .is_mrset = true,
637         .mrset = dict_lookup_mrset (dict, lex_tokcstr (lexer))
638       };
639       if (!var->mrset)
640         {
641           lex_error (lexer, _("'%s' does not name a multiple-response set "
642                               "in the active file dictionary."),
643                      lex_tokcstr (lexer));
644           return false;
645         }
646       lex_get (lexer);
647       return true;
648     }
649   else
650     {
651       *var = (struct ctables_var) {
652         .is_mrset = false,
653         .var = parse_variable (lexer, dict),
654       };
655       return var->var != NULL;
656     }
657 }
658
659 static struct ctables_axis *
660 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
661 {
662   if (lex_match (ctx->lexer, T_LPAREN))
663     {
664       struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
665       if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
666         {
667           ctables_axis_destroy (sub);
668           return NULL;
669         }
670       return sub;
671     }
672
673   if (!lex_force_id (ctx->lexer))
674     return NULL;
675
676   struct ctables_var var;
677   if (!ctables_var_parse (ctx->lexer, ctx->dict, &var))
678     return NULL;
679
680   struct ctables_axis *axis = xmalloc (sizeof *axis);
681   *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
682
683   /* XXX should figure out default measures by reading data */
684   axis->scale = (var.is_mrset ? false
685                  : lex_match_phrase (ctx->lexer, "[S]") ? true
686                  : lex_match_phrase (ctx->lexer, "[C]") ? false
687                  : var_get_measure (var.var) == MEASURE_SCALE);
688
689   size_t allocated_summaries = 0;
690   if (lex_match (ctx->lexer, T_LBRACK))
691     {
692       do
693         {
694           enum ctables_summary_function function;
695           if (!parse_ctables_summary_function (ctx->lexer, &function))
696             goto error;
697
698           double percentile = 0;
699           if (function == CTSF_PTILE)
700             {
701               if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
702                 goto error;
703               percentile = lex_number (ctx->lexer);
704               lex_get (ctx->lexer);
705             }
706
707           struct ctables_summary *s = add_summary (axis, function, percentile,
708                                                    &allocated_summaries);
709           if (lex_is_string (ctx->lexer))
710             {
711               free (s->label);
712               s->label = ss_xstrdup (lex_tokss (ctx->lexer));
713               lex_get (ctx->lexer);
714             }
715           if (lex_token (ctx->lexer) == T_ID)
716             {
717               if (!parse_format_specifier (ctx->lexer, &s->format)
718                   || !fmt_check_output (&s->format)
719                   || !fmt_check_type_compat (&s->format, VAL_NUMERIC))
720                 goto error;
721             }
722           lex_match (ctx->lexer, T_COMMA);
723         }
724       while (!lex_match (ctx->lexer, T_RBRACK));
725     }
726   else
727     add_summary (axis, axis->scale ? CTSF_MEAN : CTSF_COUNT, 0,
728                  &allocated_summaries);
729   return axis;
730
731 error:
732   ctables_axis_destroy (axis);
733   return NULL;
734 }
735
736 static struct ctables_axis *
737 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
738 {
739   struct ctables_axis *lhs = ctables_axis_parse_primary (ctx);
740   if (!lhs)
741     return NULL;
742
743   while (lex_match (ctx->lexer, T_GT))
744     {
745       struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
746       if (!rhs)
747         return NULL;
748
749       lhs = ctables_axis_new_nonterminal (CTAO_NEST, lhs, rhs);
750     }
751
752   return lhs;
753 }
754
755 static struct ctables_axis *
756 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
757 {
758   struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
759   if (!lhs)
760     return NULL;
761
762   while (lex_match (ctx->lexer, T_PLUS))
763     {
764       struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
765       if (!rhs)
766         return NULL;
767
768       lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs);
769     }
770
771   return lhs;
772 }
773
774 static bool
775 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
776                     struct ctables *ct, struct ctables_table *t,
777                     enum pivot_axis_type a)
778 {
779   if (lex_token (lexer) == T_BY
780       || lex_token (lexer) == T_SLASH
781       || lex_token (lexer) == T_ENDCMD)
782     return true;
783
784   struct ctables_axis_parse_ctx ctx = {
785     .lexer = lexer,
786     .dict = dict,
787     .ct = ct,
788     .t = t
789   };
790   t->axes[a] = ctables_axis_parse_stack (&ctx);
791   return t->axes[a] != NULL;
792 }
793
794 static void
795 ctables_chisq_destroy (struct ctables_chisq *chisq)
796 {
797   free (chisq);
798 }
799
800 static void
801 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
802 {
803   free (pairwise);
804 }
805
806 static void
807 ctables_table_uninit (struct ctables_table *t)
808 {
809   if (!t)
810     return;
811
812   for (size_t i = 0; i < t->n_categories; i++)
813     ctables_categories_unref (t->categories[i]);
814   free (t->categories);
815
816   ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
817   ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
818   ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
819   free (t->caption);
820   free (t->corner);
821   free (t->title);
822   ctables_chisq_destroy (t->chisq);
823   ctables_pairwise_destroy (t->pairwise);
824 }
825
826 static void
827 ctables_destroy (struct ctables *ct)
828 {
829   if (!ct)
830     return;
831
832   pivot_table_look_unref (ct->look);
833   free (ct->zero);
834   free (ct->missing);
835   free (ct->vlabels);
836   for (size_t i = 0; i < ct->n_tables; i++)
837     ctables_table_uninit (&ct->tables[i]);
838   free (ct->tables);
839   free (ct);
840 }
841
842 static struct ctables_cat_value
843 ccvt_range (double low, double high)
844 {
845   return (struct ctables_cat_value) {
846     .type = CCVT_RANGE,
847     .range = { low, high }
848   };
849 }
850
851 static bool
852 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
853                                 struct ctables_table *t)
854 {
855   if (!lex_match_id (lexer, "VARIABLES"))
856     return false;
857   lex_match (lexer, T_EQUALS);
858
859   struct variable **vars;
860   size_t n_vars;
861   if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
862     return false;
863
864   struct ctables_categories *c = xmalloc (sizeof *c);
865   *c = (struct ctables_categories) { .n_refs = n_vars };
866   for (size_t i = 0; i < n_vars; i++)
867     {
868       struct ctables_categories **cp
869         = &t->categories[var_get_dict_index (vars[i])];
870       ctables_categories_unref (*cp);
871       *cp = c;
872     }
873   free (vars);
874
875   if (lex_match (lexer, T_LBRACK))
876     {
877       size_t allocated_values = 0;
878       do
879         {
880           if (c->n_values >= allocated_values)
881             c->values = x2nrealloc (c->values, &allocated_values,
882                                     sizeof *c->values);
883
884           struct ctables_cat_value *v = &c->values[c->n_values];
885           if (lex_match_id (lexer, "OTHERNM"))
886             v->type = CCVT_OTHERNM;
887           else if (lex_match_id (lexer, "MISSING"))
888             v->type = CCVT_MISSING;
889           else if (lex_match_id (lexer, "SUBTOTAL"))
890             *v = (struct ctables_cat_value)
891               { .type = CCVT_SUBTOTAL, .subtotal_label = NULL };
892           else if (lex_match_id (lexer, "HSUBTOTAL"))
893             *v = (struct ctables_cat_value)
894               { .type = CCVT_HSUBTOTAL, .subtotal_label = NULL };
895           else if (lex_match_id (lexer, "LO"))
896             {
897               if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
898                 return false;
899               *v = ccvt_range (-DBL_MAX, lex_number (lexer));
900               lex_get (lexer);
901             }
902           else if (lex_is_number (lexer))
903             {
904               double number = lex_number (lexer);
905               lex_get (lexer);
906               if (lex_match_id (lexer, "THRU"))
907                 {
908                   v->type = CCVT_RANGE;
909                   v->range[0] = number;
910                   if (lex_match_id (lexer, "HI"))
911                     *v = ccvt_range (number, DBL_MAX);
912                   else
913                     {
914                       if (!lex_force_num (lexer))
915                         return false;
916                       *v = ccvt_range (number, lex_number (lexer));
917                       lex_get (lexer);
918                     }
919                 }
920               else
921                 *v = (struct ctables_cat_value) {
922                   .type = CCVT_NUMBER,
923                   .number = number
924                 };
925             }
926           else if (lex_is_string (lexer))
927             {
928               *v = (struct ctables_cat_value) {
929                 .type = CCVT_STRING,
930                 .string = ss_xstrdup (lex_tokss (lexer)),
931               };
932               lex_get (lexer);
933             }
934           else
935             {
936               lex_error (lexer, NULL);
937               return false;
938             }
939
940           if ((v->type == CCVT_SUBTOTAL || v->type == CCVT_HSUBTOTAL)
941               && lex_match (lexer, T_EQUALS))
942             {
943               if (!lex_force_string (lexer))
944                 return false;
945
946               v->subtotal_label = ss_xstrdup (lex_tokss (lexer));
947               lex_get (lexer);
948             }
949
950           c->n_values++;
951           lex_match (lexer, T_COMMA);
952         }
953       while (!lex_match (lexer, T_RBRACK));
954     }
955
956   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
957     {
958       if (!c->n_values && lex_match_id (lexer, "ORDER"))
959         {
960           lex_match (lexer, T_EQUALS);
961           if (lex_match_id (lexer, "A"))
962             c->sort_ascending = true;
963           else if (lex_match_id (lexer, "D"))
964             c->sort_ascending = false;
965           else
966             {
967               lex_error_expecting (lexer, "A", "D");
968               return false;
969             }
970         }
971       else if (!c->n_values && lex_match_id (lexer, "KEY"))
972         {
973           lex_match (lexer, T_EQUALS);
974           if (lex_match_id (lexer, "VALUE"))
975             c->key = CTCS_VALUE;
976           else if (lex_match_id (lexer, "LABEL"))
977             c->key = CTCS_LABEL;
978           else
979             {
980               c->key = CTCS_FUNCTION;
981               if (!parse_ctables_summary_function (lexer, &c->sort_func))
982                 return false;
983
984               if (lex_match (lexer, T_LPAREN))
985                 {
986                   c->sort_func_var = parse_variable (lexer, dict);
987                   if (!c->sort_func_var)
988                     return false;
989
990                   if (c->sort_func == CTSF_PTILE)
991                     {
992                       lex_match (lexer, T_COMMA);
993                       if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
994                         return false;
995                       c->percentile = lex_number (lexer);
996                       lex_get (lexer);
997                     }
998
999                   if (!lex_force_match (lexer, T_RPAREN))
1000                     return false;
1001                 }
1002               else if (ctables_function_availability (c->sort_func)
1003                        == CTFA_SCALE)
1004                 {
1005                   bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1006                   return false;
1007                 }
1008             }
1009         }
1010       else if (!c->n_values && lex_match_id (lexer, "MISSING"))
1011         {
1012           lex_match (lexer, T_EQUALS);
1013           if (lex_match_id (lexer, "INCLUDE"))
1014             c->include_missing = true;
1015           else if (lex_match_id (lexer, "EXCLUDE"))
1016             c->include_missing = false;
1017           else
1018             {
1019               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1020               return false;
1021             }
1022         }
1023       else if (lex_match_id (lexer, "TOTAL"))
1024         {
1025           lex_match (lexer, T_EQUALS);
1026           if (!parse_bool (lexer, &c->show_totals))
1027             return false;
1028         }
1029       else if (lex_match_id (lexer, "LABEL"))
1030         {
1031           lex_match (lexer, T_EQUALS);
1032           if (!lex_force_string (lexer))
1033             return false;
1034           free (c->total_label);
1035           c->total_label = ss_xstrdup (lex_tokss (lexer));
1036           lex_get (lexer);
1037         }
1038       else if (lex_match_id (lexer, "POSITION"))
1039         {
1040           lex_match (lexer, T_EQUALS);
1041           if (lex_match_id (lexer, "BEFORE"))
1042             c->totals_before = true;
1043           else if (lex_match_id (lexer, "AFTER"))
1044             c->totals_before = false;
1045           else
1046             {
1047               lex_error_expecting (lexer, "BEFORE", "AFTER");
1048               return false;
1049             }
1050         }
1051       else if (lex_match_id (lexer, "EMPTY"))
1052         {
1053           lex_match (lexer, T_EQUALS);
1054           if (lex_match_id (lexer, "INCLUDE"))
1055             c->show_empty = true;
1056           else if (lex_match_id (lexer, "EXCLUDE"))
1057             c->show_empty = false;
1058           else
1059             {
1060               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1061               return false;
1062             }
1063         }
1064       else
1065         {
1066           if (!c->n_values)
1067             lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
1068                                  "TOTAL", "LABEL", "POSITION", "EMPTY");
1069           else
1070             lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
1071           return false;
1072         }
1073     }
1074   return true;
1075 }
1076
1077 int
1078 cmd_ctables (struct lexer *lexer, struct dataset *ds)
1079 {
1080   size_t n_vars = dict_get_n_vars (dataset_dict (ds));
1081   enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
1082   for (size_t i = 0; i < n_vars; i++)
1083     vlabels[i] = CTVL_DEFAULT;
1084
1085   struct ctables *ct = xmalloc (sizeof *ct);
1086   *ct = (struct ctables) {
1087     .look = pivot_table_look_unshare (pivot_table_look_ref (
1088                                         pivot_table_look_get_default ())),
1089     .vlabels = vlabels,
1090     .hide_threshold = 5,
1091   };
1092
1093   if (!lex_force_match (lexer, T_SLASH))
1094     goto error;
1095
1096   while (!lex_match_id (lexer, "TABLE"))
1097     {
1098       if (lex_match_id (lexer, "FORMAT"))
1099         {
1100           double widths[2] = { SYSMIS, SYSMIS };
1101           double units_per_inch = 72.0;
1102
1103           while (lex_token (lexer) != T_SLASH)
1104             {
1105               if (lex_match_id (lexer, "MINCOLWIDTH"))
1106                 {
1107                   if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
1108                     goto error;
1109                 }
1110               else if (lex_match_id (lexer, "MAXCOLWIDTH"))
1111                 {
1112                   if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
1113                     goto error;
1114                 }
1115               else if (lex_match_id (lexer, "UNITS"))
1116                 {
1117                   lex_match (lexer, T_EQUALS);
1118                   if (lex_match_id (lexer, "POINTS"))
1119                     units_per_inch = 72.0;
1120                   else if (lex_match_id (lexer, "INCHES"))
1121                     units_per_inch = 1.0;
1122                   else if (lex_match_id (lexer, "CM"))
1123                     units_per_inch = 2.54;
1124                   else
1125                     {
1126                       lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
1127                       goto error;
1128                     }
1129                 }
1130               else if (lex_match_id (lexer, "EMPTY"))
1131                 {
1132                   free (ct->zero);
1133                   ct->zero = NULL;
1134
1135                   lex_match (lexer, T_EQUALS);
1136                   if (lex_match_id (lexer, "ZERO"))
1137                     {
1138                       /* Nothing to do. */
1139                     }
1140                   else if (lex_match_id (lexer, "BLANK"))
1141                     ct->zero = xstrdup ("");
1142                   else if (lex_force_string (lexer))
1143                     {
1144                       ct->zero = ss_xstrdup (lex_tokss (lexer));
1145                       lex_get (lexer);
1146                     }
1147                   else
1148                     goto error;
1149                 }
1150               else if (lex_match_id (lexer, "MISSING"))
1151                 {
1152                   lex_match (lexer, T_EQUALS);
1153                   if (!lex_force_string (lexer))
1154                     goto error;
1155
1156                   free (ct->missing);
1157                   ct->missing = (strcmp (lex_tokcstr (lexer), ".")
1158                                  ? ss_xstrdup (lex_tokss (lexer))
1159                                  : NULL);
1160                   lex_get (lexer);
1161                 }
1162               else
1163                 {
1164                   lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
1165                                        "UNITS", "EMPTY", "MISSING");
1166                   goto error;
1167                 }
1168             }
1169
1170           if (widths[0] != SYSMIS && widths[1] != SYSMIS
1171               && widths[0] > widths[1])
1172             {
1173               msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
1174               goto error;
1175             }
1176
1177           for (size_t i = 0; i < 2; i++)
1178             if (widths[i] != SYSMIS)
1179               {
1180                 int *wr = ct->look->width_ranges[TABLE_HORZ];
1181                 wr[i] = widths[i] / units_per_inch * 96.0;
1182                 if (wr[0] > wr[1])
1183                   wr[!i] = wr[i];
1184               }
1185         }
1186       else if (lex_match_id (lexer, "VLABELS"))
1187         {
1188           if (!lex_force_match_id (lexer, "VARIABLES"))
1189             goto error;
1190           lex_match (lexer, T_EQUALS);
1191
1192           struct variable **vars;
1193           size_t n_vars;
1194           if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
1195                                 PV_NO_SCRATCH))
1196             goto error;
1197
1198           if (!lex_force_match_id (lexer, "DISPLAY"))
1199             {
1200               free (vars);
1201               goto error;
1202             }
1203           lex_match (lexer, T_EQUALS);
1204
1205           enum ctables_vlabel vlabel;
1206           if (lex_match_id (lexer, "DEFAULT"))
1207             vlabel = CTVL_DEFAULT;
1208           else if (lex_match_id (lexer, "NAME"))
1209             vlabel = CTVL_NAME;
1210           else if (lex_match_id (lexer, "LABEL"))
1211             vlabel = CTVL_LABEL;
1212           else if (lex_match_id (lexer, "BOTH"))
1213             vlabel = CTVL_BOTH;
1214           else if (lex_match_id (lexer, "NONE"))
1215             vlabel = CTVL_NONE;
1216           else
1217             {
1218               lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
1219                                    "BOTH", "NONE");
1220               free (vars);
1221               goto error;
1222             }
1223
1224           for (size_t i = 0; i < n_vars; i++)
1225             ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
1226           free (vars);
1227         }
1228       else if (lex_match_id (lexer, "MRSETS"))
1229         {
1230           if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
1231             goto error;
1232           lex_match (lexer, T_EQUALS);
1233           if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
1234             goto error;
1235         }
1236       else if (lex_match_id (lexer, "SMISSING"))
1237         {
1238           if (lex_match_id (lexer, "VARIABLE"))
1239             ct->smissing_listwise = false;
1240           else if (lex_match_id (lexer, "LISTWISE"))
1241             ct->smissing_listwise = true;
1242           else
1243             {
1244               lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
1245               goto error;
1246             }
1247         }
1248       /* XXX PCOMPUTE */
1249       else if (lex_match_id (lexer, "WEIGHT"))
1250         {
1251           if (!lex_force_match_id (lexer, "VARIABLE"))
1252             goto error;
1253           lex_match (lexer, T_EQUALS);
1254           ct->base_weight = parse_variable (lexer, dataset_dict (ds));
1255           if (!ct->base_weight)
1256             goto error;
1257         }
1258       else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
1259         {
1260           if (!lex_force_match_id (lexer, "COUNT"))
1261             goto error;
1262           lex_match (lexer, T_EQUALS);
1263           if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
1264             goto error;
1265           ct->hide_threshold = lex_integer (lexer);
1266           lex_get (lexer);
1267         }
1268       else
1269         {
1270           lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
1271                                "SMISSING", "PCOMPUTE", "PPROPERTIES",
1272                                "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
1273           goto error;
1274         }
1275
1276       if (!lex_force_match (lexer, T_SLASH))
1277         goto error;
1278     }
1279
1280   size_t allocated_tables = 0;
1281   do
1282     {
1283       if (ct->n_tables >= allocated_tables)
1284         ct->tables = x2nrealloc (ct->tables, &allocated_tables,
1285                                  sizeof *ct->tables);
1286
1287       struct ctables_table *t = &ct->tables[ct->n_tables++];
1288       *t = (struct ctables_table) {
1289         .slabels_position = PIVOT_AXIS_COLUMN,
1290         .slabels_visible = true,
1291         .row_labels = CTLP_NORMAL,
1292         .col_labels = CTLP_NORMAL,
1293         .categories = xcalloc (dict_get_n_vars (dataset_dict (ds)),
1294                                sizeof *t->categories),
1295         .n_categories = dict_get_n_vars (dataset_dict (ds)),
1296         .cilevel = 95,
1297       };
1298
1299       lex_match (lexer, T_EQUALS);
1300       if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
1301         goto error;
1302
1303       if (lex_match (lexer, T_BY))
1304         {
1305           if (!ctables_axis_parse (lexer, dataset_dict (ds),
1306                                    ct, t, PIVOT_AXIS_COLUMN))
1307             goto error;
1308
1309           if (lex_match (lexer, T_BY))
1310             {
1311               if (!ctables_axis_parse (lexer, dataset_dict (ds),
1312                                        ct, t, PIVOT_AXIS_LAYER))
1313                 goto error;
1314             }
1315         }
1316       if (lex_token (lexer) == T_ENDCMD)
1317         break;
1318       if (!lex_force_match (lexer, T_SLASH))
1319         break;
1320
1321       /* XXX Validate axes. */
1322       while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
1323         {
1324           if (lex_match_id (lexer, "SLABELS"))
1325             {
1326               while (lex_token (lexer) != T_SLASH)
1327                 {
1328                   if (lex_match_id (lexer, "POSITION"))
1329                     {
1330                       lex_match (lexer, T_EQUALS);
1331                       if (lex_match_id (lexer, "COLUMN"))
1332                         t->slabels_position = PIVOT_AXIS_COLUMN;
1333                       else if (lex_match_id (lexer, "ROW"))
1334                         t->slabels_position = PIVOT_AXIS_ROW;
1335                       else if (lex_match_id (lexer, "LAYER"))
1336                         t->slabels_position = PIVOT_AXIS_LAYER;
1337                       else
1338                         {
1339                           lex_error_expecting (lexer, "COLUMN", "ROW",
1340                                                "LAYER");
1341                           goto error;
1342                         }
1343                     }
1344                   else if (lex_match_id (lexer, "VISIBLE"))
1345                     {
1346                       lex_match (lexer, T_EQUALS);
1347                       if (!parse_bool (lexer, &t->slabels_visible))
1348                         goto error;
1349                     }
1350                   else
1351                     {
1352                       lex_error_expecting (lexer, "POSITION", "VISIBLE");
1353                       goto error;
1354                     }
1355                 }
1356             }
1357           else if (lex_match_id (lexer, "CLABELS"))
1358             {
1359               while (lex_token (lexer) != T_SLASH)
1360                 {
1361                   if (lex_match_id (lexer, "AUTO"))
1362                     t->row_labels = t->col_labels = CTLP_NORMAL;
1363                   else if (lex_match_id (lexer, "ROWLABELS"))
1364                     {
1365                       lex_match (lexer, T_EQUALS);
1366                       if (lex_match_id (lexer, "OPPOSITE"))
1367                         t->row_labels = CTLP_OPPOSITE;
1368                       else if (lex_match_id (lexer, "LAYER"))
1369                         t->row_labels = CTLP_LAYER;
1370                       else
1371                         {
1372                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1373                           goto error;
1374                         }
1375                     }
1376                   else if (lex_match_id (lexer, "COLLABELS"))
1377                     {
1378                       lex_match (lexer, T_EQUALS);
1379                       if (lex_match_id (lexer, "OPPOSITE"))
1380                         t->col_labels = CTLP_OPPOSITE;
1381                       else if (lex_match_id (lexer, "LAYER"))
1382                         t->col_labels = CTLP_LAYER;
1383                       else
1384                         {
1385                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1386                           goto error;
1387                         }
1388                     }
1389                   else
1390                     {
1391                       lex_error_expecting (lexer, "AUTO", "ROWLABELS",
1392                                            "COLLABELS");
1393                       goto error;
1394                     }
1395                 }
1396             }
1397           else if (lex_match_id (lexer, "CRITERIA"))
1398             {
1399               if (!lex_force_match_id (lexer, "CILEVEL"))
1400                 goto error;
1401               lex_match (lexer, T_EQUALS);
1402
1403               if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
1404                 goto error;
1405               t->cilevel = lex_number (lexer);
1406               lex_get (lexer);
1407             }
1408           else if (lex_match_id (lexer, "CATEGORIES"))
1409             {
1410               if (!ctables_table_parse_categories (lexer, dataset_dict (ds), t))
1411                 goto error;
1412             }
1413           else if (lex_match_id (lexer, "TITLES"))
1414             {
1415               do
1416                 {
1417                   char **textp;
1418                   if (lex_match_id (lexer, "CAPTION"))
1419                     textp = &t->caption;
1420                   else if (lex_match_id (lexer, "CORNER"))
1421                     textp = &t->corner;
1422                   else if (lex_match_id (lexer, "TITLE"))
1423                     textp = &t->title;
1424                   else
1425                     {
1426                       lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
1427                       goto error;
1428                     }
1429                   lex_match (lexer, T_EQUALS);
1430
1431                   struct string s = DS_EMPTY_INITIALIZER;
1432                   while (lex_is_string (lexer))
1433                     {
1434                       if (!ds_is_empty (&s))
1435                         ds_put_byte (&s, ' ');
1436                       ds_put_substring (&s, lex_tokss (lexer));
1437                       lex_get (lexer);
1438                     }
1439                   free (*textp);
1440                   *textp = ds_steal_cstr (&s);
1441                 }
1442               while (lex_token (lexer) != T_SLASH
1443                      && lex_token (lexer) != T_ENDCMD);
1444             }
1445           else if (lex_match_id (lexer, "SIGTEST"))
1446             {
1447               if (!t->chisq)
1448                 {
1449                   t->chisq = xmalloc (sizeof *t->chisq);
1450                   *t->chisq = (struct ctables_chisq) {
1451                     .alpha = .05,
1452                     .include_mrsets = true,
1453                     .all_visible = true,
1454                   };
1455                 }
1456
1457               do
1458                 {
1459                   if (lex_match_id (lexer, "TYPE"))
1460                     {
1461                       lex_match (lexer, T_EQUALS);
1462                       if (!lex_force_match_id (lexer, "CHISQUARE"))
1463                         goto error;
1464                     }
1465                   else if (lex_match_id (lexer, "ALPHA"))
1466                     {
1467                       lex_match (lexer, T_EQUALS);
1468                       if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
1469                         goto error;
1470                       t->chisq->alpha = lex_number (lexer);
1471                       lex_get (lexer);
1472                     }
1473                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1474                     {
1475                       lex_match (lexer, T_EQUALS);
1476                       if (parse_bool (lexer, &t->chisq->include_mrsets))
1477                         goto error;
1478                     }
1479                   else if (lex_match_id (lexer, "CATEGORIES"))
1480                     {
1481                       lex_match (lexer, T_EQUALS);
1482                       if (lex_match_id (lexer, "ALLVISIBLE"))
1483                         t->chisq->all_visible = true;
1484                       else if (lex_match_id (lexer, "SUBTOTALS"))
1485                         t->chisq->all_visible = false;
1486                       else
1487                         {
1488                           lex_error_expecting (lexer,
1489                                                "ALLVISIBLE", "SUBTOTALS");
1490                           goto error;
1491                         }
1492                     }
1493                   else
1494                     {
1495                       lex_error_expecting (lexer, "TYPE", "ALPHA",
1496                                            "INCLUDEMRSETS", "CATEGORIES");
1497                       goto error;
1498                     }
1499                 }
1500               while (lex_token (lexer) != T_SLASH
1501                      && lex_token (lexer) != T_ENDCMD);
1502             }
1503           else if (lex_match_id (lexer, "COMPARETEST"))
1504             {
1505               if (!t->pairwise)
1506                 {
1507                   t->pairwise = xmalloc (sizeof *t->pairwise);
1508                   *t->pairwise = (struct ctables_pairwise) {
1509                     .type = PROP,
1510                     .alpha = { .05, .05 },
1511                     .adjust = BONFERRONI,
1512                     .include_mrsets = true,
1513                     .meansvariance_allcats = true,
1514                     .all_visible = true,
1515                     .merge = false,
1516                     .apa_style = true,
1517                     .show_sig = false,
1518                   };
1519                 }
1520
1521               do
1522                 {
1523                   if (lex_match_id (lexer, "TYPE"))
1524                     {
1525                       lex_match (lexer, T_EQUALS);
1526                       if (lex_match_id (lexer, "PROP"))
1527                         t->pairwise->type = PROP;
1528                       else if (lex_match_id (lexer, "MEAN"))
1529                         t->pairwise->type = MEAN;
1530                       else
1531                         {
1532                           lex_error_expecting (lexer, "PROP", "MEAN");
1533                           goto error;
1534                         }
1535                     }
1536                   else if (lex_match_id (lexer, "ALPHA"))
1537                     {
1538                       lex_match (lexer, T_EQUALS);
1539
1540                       if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1541                         goto error;
1542                       double a0 = lex_number (lexer);
1543                       lex_get (lexer);
1544
1545                       lex_match (lexer, T_COMMA);
1546                       if (lex_is_number (lexer))
1547                         {
1548                           if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1549                             goto error;
1550                           double a1 = lex_number (lexer);
1551                           lex_get (lexer);
1552
1553                           t->pairwise->alpha[0] = MIN (a0, a1);
1554                           t->pairwise->alpha[1] = MAX (a0, a1);
1555                         }
1556                       else
1557                         t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1558                     }
1559                   else if (lex_match_id (lexer, "ADJUST"))
1560                     {
1561                       lex_match (lexer, T_EQUALS);
1562                       if (lex_match_id (lexer, "BONFERRONI"))
1563                         t->pairwise->adjust = BONFERRONI;
1564                       else if (lex_match_id (lexer, "BH"))
1565                         t->pairwise->adjust = BH;
1566                       else if (lex_match_id (lexer, "NONE"))
1567                         t->pairwise->adjust = 0;
1568                       else
1569                         {
1570                           lex_error_expecting (lexer, "BONFERRONI", "BH",
1571                                                "NONE");
1572                           goto error;
1573                         }
1574                     }
1575                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1576                     {
1577                       lex_match (lexer, T_EQUALS);
1578                       if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1579                         goto error;
1580                     }
1581                   else if (lex_match_id (lexer, "MEANSVARIANCE"))
1582                     {
1583                       lex_match (lexer, T_EQUALS);
1584                       if (lex_match_id (lexer, "ALLCATS"))
1585                         t->pairwise->meansvariance_allcats = true;
1586                       else if (lex_match_id (lexer, "TESTEDCATS"))
1587                         t->pairwise->meansvariance_allcats = false;
1588                       else
1589                         {
1590                           lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1591                           goto error;
1592                         }
1593                     }
1594                   else if (lex_match_id (lexer, "CATEGORIES"))
1595                     {
1596                       lex_match (lexer, T_EQUALS);
1597                       if (lex_match_id (lexer, "ALLVISIBLE"))
1598                         t->pairwise->all_visible = true;
1599                       else if (lex_match_id (lexer, "SUBTOTALS"))
1600                         t->pairwise->all_visible = false;
1601                       else
1602                         {
1603                           lex_error_expecting (lexer, "ALLVISIBLE",
1604                                                "SUBTOTALS");
1605                           goto error;
1606                         }
1607                     }
1608                   else if (lex_match_id (lexer, "MERGE"))
1609                     {
1610                       lex_match (lexer, T_EQUALS);
1611                       if (!parse_bool (lexer, &t->pairwise->merge))
1612                         goto error;
1613                     }
1614                   else if (lex_match_id (lexer, "STYLE"))
1615                     {
1616                       lex_match (lexer, T_EQUALS);
1617                       if (lex_match_id (lexer, "APA"))
1618                         t->pairwise->apa_style = true;
1619                       else if (lex_match_id (lexer, "SIMPLE"))
1620                         t->pairwise->apa_style = false;
1621                       else
1622                         {
1623                           lex_error_expecting (lexer, "APA", "SIMPLE");
1624                           goto error;
1625                         }
1626                     }
1627                   else if (lex_match_id (lexer, "SHOWSIG"))
1628                     {
1629                       lex_match (lexer, T_EQUALS);
1630                       if (!parse_bool (lexer, &t->pairwise->show_sig))
1631                         goto error;
1632                     }
1633                   else
1634                     {
1635                       lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1636                                            "INCLUDEMRSETS", "MEANSVARIANCE",
1637                                            "CATEGORIES", "MERGE", "STYLE",
1638                                            "SHOWSIG");
1639                       goto error;
1640                     }
1641                 }
1642               while (lex_token (lexer) != T_SLASH
1643                      && lex_token (lexer) != T_ENDCMD);
1644             }
1645           else
1646             {
1647               lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1648                                    "CRITERIA", "CATEGORIES", "TITLES",
1649                                    "SIGTEST", "COMPARETEST");
1650               goto error;
1651             }
1652         }
1653     }
1654   while (lex_token (lexer) != T_ENDCMD);
1655   ctables_destroy (ct);
1656   return CMD_SUCCESS;
1657
1658 error:
1659   ctables_destroy (ct);
1660   return CMD_FAILURE;
1661 }
1662