start validating
[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     struct msg_location *loc;
420   };
421
422 static void ctables_axis_destroy (struct ctables_axis *);
423
424 enum ctables_format
425   {
426     CTF_COUNT,
427     CTF_PERCENT,
428     CTF_GENERAL
429   };
430
431 enum ctables_function_availability
432   {
433     CTFA_ALL,                /* Any variables. */
434     CTFA_SCALE,              /* Only scale variables, totals, and subtotals. */
435     CTFA_MRSETS,             /* Only multiple-response sets */
436   };
437
438 struct ctables_summary
439   {
440     enum ctables_summary_function function;
441     double percentile;          /* CTSF_PTILE only. */
442     char *label;
443     struct fmt_spec format;     /* XXX extra CTABLES formats */
444   };
445
446 static void
447 ctables_summary_uninit (struct ctables_summary *s)
448 {
449   if (s)
450     free (s->label);
451 }
452
453 static bool
454 parse_col_width (struct lexer *lexer, const char *name, double *width)
455 {
456   lex_match (lexer, T_EQUALS);
457   if (lex_match_id (lexer, "DEFAULT"))
458     *width = SYSMIS;
459   else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
460     {
461       *width = lex_number (lexer);
462       lex_get (lexer);
463     }
464   else
465     return false;
466
467   return true;
468 }
469
470 static bool
471 parse_bool (struct lexer *lexer, bool *b)
472 {
473   if (lex_match_id (lexer, "NO"))
474     *b = false;
475   else if (lex_match_id (lexer, "YES"))
476     *b = true;
477   else
478     {
479       lex_error_expecting (lexer, "YES", "NO");
480       return false;
481     }
482   return true;
483 }
484
485 static enum ctables_function_availability
486 ctables_function_availability (enum ctables_summary_function f)
487 {
488   static enum ctables_function_availability availability[] = {
489 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
490     SUMMARIES
491 #undef S
492   };
493
494   return availability[f];
495 }
496
497 static bool
498 parse_ctables_summary_function (struct lexer *lexer,
499                                 enum ctables_summary_function *f)
500 {
501   struct pair
502     {
503       enum ctables_summary_function function;
504       struct substring name;
505     };
506   static struct pair names[] = {
507 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
508     { ENUM, SS_LITERAL_INITIALIZER (NAME) },
509     SUMMARIES
510
511     /* The .COUNT suffix may be omitted. */
512     S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
513     S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
514     S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
515     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
516     S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
517     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
518     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
519 #undef S
520   };
521
522   if (!lex_force_id (lexer))
523     return false;
524
525   for (size_t i = 0; i < sizeof names / sizeof *names; i++)
526     if (ss_equals_case (names[i].name, lex_tokss (lexer)))
527       {
528         *f = names[i].function;
529         lex_get (lexer);
530         return true;
531       }
532
533   lex_error (lexer, _("Expecting summary function name."));
534   return false;
535 }
536
537 static void
538 ctables_axis_destroy (struct ctables_axis *axis)
539 {
540   if (!axis)
541     return;
542
543   switch (axis->op)
544     {
545     case CTAO_VAR:
546       for (size_t i = 0; i < axis->n_summaries; i++)
547         ctables_summary_uninit (&axis->summaries[i]);
548       free (axis->summaries);
549       break;
550
551     case CTAO_STACK:
552     case CTAO_NEST:
553       ctables_axis_destroy (axis->subs[0]);
554       ctables_axis_destroy (axis->subs[1]);
555       break;
556     }
557   msg_location_destroy (axis->loc);
558   free (axis);
559 }
560
561 static struct ctables_axis *
562 ctables_axis_new_nonterminal (enum ctables_axis_op op,
563                               struct ctables_axis *sub0,
564                               struct ctables_axis *sub1,
565                               struct lexer *lexer, int start_ofs)
566 {
567   struct ctables_axis *axis = xmalloc (sizeof *axis);
568   *axis = (struct ctables_axis) {
569     .op = op,
570     .subs = { sub0, sub1 },
571     .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
572   };
573   return axis;
574 }
575
576 struct ctables_axis_parse_ctx
577   {
578     struct lexer *lexer;
579     struct dictionary *dict;
580     struct ctables *ct;
581     struct ctables_table *t;
582   };
583
584 static struct ctables_summary *
585 add_summary (struct ctables_axis *axis, enum ctables_summary_function function,
586              double percentile, size_t *allocated_summaries)
587 {
588   if (axis->n_summaries >= *allocated_summaries)
589     axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
590                                   sizeof *axis->summaries);
591
592   static const char *default_labels[] = {
593 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
594     SUMMARIES
595 #undef S
596   };
597   char *label = (function == CTSF_PTILE
598                  ? xasprintf (_("Percentile %.2f"), percentile)
599                  : xstrdup (gettext (default_labels[function])));
600
601   static const enum ctables_format default_formats[] = {
602 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
603     SUMMARIES
604 #undef S
605   };
606   struct fmt_spec format;
607   switch (default_formats[function])
608     {
609     case CTF_COUNT:
610       format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
611       break;
612
613     case CTF_PERCENT:
614       format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
615       break;
616
617     case CTF_GENERAL:
618       format = *ctables_var_get_print_format (&axis->var);
619       break;
620
621     default:
622       NOT_REACHED ();
623     }
624
625   struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
626   *s = (struct ctables_summary) {
627     .function = function,
628     .percentile = percentile,
629     .label = label,
630     .format = format,
631   };
632   return s;
633 }
634
635 static struct ctables_axis *ctables_axis_parse_stack (
636   struct ctables_axis_parse_ctx *);
637
638 static bool
639 ctables_var_parse (struct lexer *lexer, struct dictionary *dict,
640                    struct ctables_var *var)
641 {
642   if (ss_starts_with (lex_tokss (lexer), ss_cstr ("$")))
643     {
644       *var = (struct ctables_var) {
645         .is_mrset = true,
646         .mrset = dict_lookup_mrset (dict, lex_tokcstr (lexer))
647       };
648       if (!var->mrset)
649         {
650           lex_error (lexer, _("'%s' does not name a multiple-response set "
651                               "in the active file dictionary."),
652                      lex_tokcstr (lexer));
653           return false;
654         }
655       lex_get (lexer);
656       return true;
657     }
658   else
659     {
660       *var = (struct ctables_var) {
661         .is_mrset = false,
662         .var = parse_variable (lexer, dict),
663       };
664       return var->var != NULL;
665     }
666 }
667
668 static struct ctables_axis *
669 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
670 {
671   if (lex_match (ctx->lexer, T_LPAREN))
672     {
673       struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
674       if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
675         {
676           ctables_axis_destroy (sub);
677           return NULL;
678         }
679       return sub;
680     }
681
682   if (!lex_force_id (ctx->lexer))
683     return NULL;
684
685   int start_ofs = lex_ofs (ctx->lexer);
686   struct ctables_var var;
687   if (!ctables_var_parse (ctx->lexer, ctx->dict, &var))
688     return NULL;
689
690   struct ctables_axis *axis = xmalloc (sizeof *axis);
691   *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
692
693   /* XXX should figure out default measures by reading data */
694   axis->scale = (var.is_mrset ? false
695                  : lex_match_phrase (ctx->lexer, "[S]") ? true
696                  : lex_match_phrase (ctx->lexer, "[C]") ? false
697                  : var_get_measure (var.var) == MEASURE_SCALE);
698   axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
699                                 lex_ofs (ctx->lexer) - 1);
700
701   if (lex_match (ctx->lexer, T_LBRACK))
702     {
703       size_t allocated_summaries = 0;
704       do
705         {
706           enum ctables_summary_function function;
707           if (!parse_ctables_summary_function (ctx->lexer, &function))
708             goto error;
709
710           double percentile = 0;
711           if (function == CTSF_PTILE)
712             {
713               if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
714                 goto error;
715               percentile = lex_number (ctx->lexer);
716               lex_get (ctx->lexer);
717             }
718
719           struct ctables_summary *s = add_summary (axis, function, percentile,
720                                                    &allocated_summaries);
721           if (lex_is_string (ctx->lexer))
722             {
723               free (s->label);
724               s->label = ss_xstrdup (lex_tokss (ctx->lexer));
725               lex_get (ctx->lexer);
726             }
727           if (lex_token (ctx->lexer) == T_ID)
728             {
729               if (!parse_format_specifier (ctx->lexer, &s->format)
730                   || !fmt_check_output (&s->format)
731                   || !fmt_check_type_compat (&s->format, VAL_NUMERIC))
732                 goto error;
733             }
734           lex_match (ctx->lexer, T_COMMA);
735         }
736       while (!lex_match (ctx->lexer, T_RBRACK));
737     }
738   return axis;
739
740 error:
741   ctables_axis_destroy (axis);
742   return NULL;
743 }
744
745 static const struct ctables_axis *
746 find_scale (const struct ctables_axis *axis)
747 {
748   if (!axis)
749     return NULL;
750   else if (axis->op == CTAO_VAR)
751     {
752       if (axis->scale)
753         {
754           assert (!axis->var.is_mrset);
755           return axis;
756         }
757       else
758         return NULL;
759     }
760   else
761     {
762       for (size_t i = 0; i < 2; i++)
763         {
764           const struct ctables_axis *scale = find_scale (axis->subs[i]);
765           if (scale)
766             return scale;
767         }
768       return NULL;
769     }
770 }
771
772 static const struct ctables_axis *
773 find_categorical_summary (const struct ctables_axis *axis)
774 {
775   if (!axis)
776     return NULL;
777   else if (axis->op == CTAO_VAR)
778     return !axis->scale && axis->n_summaries ? axis : NULL;
779   else
780     {
781       for (size_t i = 0; i < 2; i++)
782         {
783           const struct ctables_axis *sum
784             = find_categorical_summary (axis->subs[i]);
785           if (sum)
786             return sum;
787         }
788       return NULL;
789     }
790 }
791
792 static struct ctables_axis *
793 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
794 {
795   int start_ofs = lex_ofs (ctx->lexer);
796   struct ctables_axis *lhs = ctables_axis_parse_primary (ctx);
797   if (!lhs)
798     return NULL;
799
800   while (lex_match (ctx->lexer, T_GT))
801     {
802       struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
803       if (!rhs)
804         return NULL;
805
806       struct ctables_axis *nest = ctables_axis_new_nonterminal (
807         CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
808
809       const struct ctables_axis *outer_scale = find_scale (lhs);
810       const struct ctables_axis *inner_scale = find_scale (rhs);
811       if (outer_scale && inner_scale)
812         {
813           msg_at (SE, nest->loc, _("Cannot nest scale variables."));
814           msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
815           msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
816           ctables_axis_destroy (nest);
817           return NULL;
818         }
819
820       const struct ctables_axis *outer_sum = find_categorical_summary (lhs);
821       if (outer_sum)
822         {
823           msg_at (SE, nest->loc,
824                   _("Summaries may only be requested for categorical variables "
825                     "at the innermost nesting level."));
826           msg_at (SN, outer_sum->loc,
827                   _("This outer categorical variable has a summary."));
828           ctables_axis_destroy (nest);
829           return NULL;
830         }
831
832       lhs = nest;
833     }
834
835   return lhs;
836 }
837
838 static struct ctables_axis *
839 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
840 {
841   int start_ofs = lex_ofs (ctx->lexer);
842   struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
843   if (!lhs)
844     return NULL;
845
846   while (lex_match (ctx->lexer, T_PLUS))
847     {
848       struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
849       if (!rhs)
850         return NULL;
851
852       lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
853                                           ctx->lexer, start_ofs);
854     }
855
856   return lhs;
857 }
858
859 static bool
860 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
861                     struct ctables *ct, struct ctables_table *t,
862                     enum pivot_axis_type a)
863 {
864   if (lex_token (lexer) == T_BY
865       || lex_token (lexer) == T_SLASH
866       || lex_token (lexer) == T_ENDCMD)
867     return true;
868
869   struct ctables_axis_parse_ctx ctx = {
870     .lexer = lexer,
871     .dict = dict,
872     .ct = ct,
873     .t = t
874   };
875   t->axes[a] = ctables_axis_parse_stack (&ctx);
876   return t->axes[a] != NULL;
877 }
878
879 static void
880 ctables_chisq_destroy (struct ctables_chisq *chisq)
881 {
882   free (chisq);
883 }
884
885 static void
886 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
887 {
888   free (pairwise);
889 }
890
891 static void
892 ctables_table_uninit (struct ctables_table *t)
893 {
894   if (!t)
895     return;
896
897   for (size_t i = 0; i < t->n_categories; i++)
898     ctables_categories_unref (t->categories[i]);
899   free (t->categories);
900
901   ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
902   ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
903   ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
904   free (t->caption);
905   free (t->corner);
906   free (t->title);
907   ctables_chisq_destroy (t->chisq);
908   ctables_pairwise_destroy (t->pairwise);
909 }
910
911 static void
912 ctables_destroy (struct ctables *ct)
913 {
914   if (!ct)
915     return;
916
917   pivot_table_look_unref (ct->look);
918   free (ct->zero);
919   free (ct->missing);
920   free (ct->vlabels);
921   for (size_t i = 0; i < ct->n_tables; i++)
922     ctables_table_uninit (&ct->tables[i]);
923   free (ct->tables);
924   free (ct);
925 }
926
927 static struct ctables_cat_value
928 ccvt_range (double low, double high)
929 {
930   return (struct ctables_cat_value) {
931     .type = CCVT_RANGE,
932     .range = { low, high }
933   };
934 }
935
936 static bool
937 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
938                                 struct ctables_table *t)
939 {
940   if (!lex_match_id (lexer, "VARIABLES"))
941     return false;
942   lex_match (lexer, T_EQUALS);
943
944   struct variable **vars;
945   size_t n_vars;
946   if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
947     return false;
948
949   struct ctables_categories *c = xmalloc (sizeof *c);
950   *c = (struct ctables_categories) { .n_refs = n_vars };
951   for (size_t i = 0; i < n_vars; i++)
952     {
953       struct ctables_categories **cp
954         = &t->categories[var_get_dict_index (vars[i])];
955       ctables_categories_unref (*cp);
956       *cp = c;
957     }
958   free (vars);
959
960   if (lex_match (lexer, T_LBRACK))
961     {
962       size_t allocated_values = 0;
963       do
964         {
965           if (c->n_values >= allocated_values)
966             c->values = x2nrealloc (c->values, &allocated_values,
967                                     sizeof *c->values);
968
969           struct ctables_cat_value *v = &c->values[c->n_values];
970           if (lex_match_id (lexer, "OTHERNM"))
971             v->type = CCVT_OTHERNM;
972           else if (lex_match_id (lexer, "MISSING"))
973             v->type = CCVT_MISSING;
974           else if (lex_match_id (lexer, "SUBTOTAL"))
975             *v = (struct ctables_cat_value)
976               { .type = CCVT_SUBTOTAL, .subtotal_label = NULL };
977           else if (lex_match_id (lexer, "HSUBTOTAL"))
978             *v = (struct ctables_cat_value)
979               { .type = CCVT_HSUBTOTAL, .subtotal_label = NULL };
980           else if (lex_match_id (lexer, "LO"))
981             {
982               if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
983                 return false;
984               *v = ccvt_range (-DBL_MAX, lex_number (lexer));
985               lex_get (lexer);
986             }
987           else if (lex_is_number (lexer))
988             {
989               double number = lex_number (lexer);
990               lex_get (lexer);
991               if (lex_match_id (lexer, "THRU"))
992                 {
993                   v->type = CCVT_RANGE;
994                   v->range[0] = number;
995                   if (lex_match_id (lexer, "HI"))
996                     *v = ccvt_range (number, DBL_MAX);
997                   else
998                     {
999                       if (!lex_force_num (lexer))
1000                         return false;
1001                       *v = ccvt_range (number, lex_number (lexer));
1002                       lex_get (lexer);
1003                     }
1004                 }
1005               else
1006                 *v = (struct ctables_cat_value) {
1007                   .type = CCVT_NUMBER,
1008                   .number = number
1009                 };
1010             }
1011           else if (lex_is_string (lexer))
1012             {
1013               *v = (struct ctables_cat_value) {
1014                 .type = CCVT_STRING,
1015                 .string = ss_xstrdup (lex_tokss (lexer)),
1016               };
1017               lex_get (lexer);
1018             }
1019           else
1020             {
1021               lex_error (lexer, NULL);
1022               return false;
1023             }
1024
1025           if ((v->type == CCVT_SUBTOTAL || v->type == CCVT_HSUBTOTAL)
1026               && lex_match (lexer, T_EQUALS))
1027             {
1028               if (!lex_force_string (lexer))
1029                 return false;
1030
1031               v->subtotal_label = ss_xstrdup (lex_tokss (lexer));
1032               lex_get (lexer);
1033             }
1034
1035           c->n_values++;
1036           lex_match (lexer, T_COMMA);
1037         }
1038       while (!lex_match (lexer, T_RBRACK));
1039     }
1040
1041   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
1042     {
1043       if (!c->n_values && lex_match_id (lexer, "ORDER"))
1044         {
1045           lex_match (lexer, T_EQUALS);
1046           if (lex_match_id (lexer, "A"))
1047             c->sort_ascending = true;
1048           else if (lex_match_id (lexer, "D"))
1049             c->sort_ascending = false;
1050           else
1051             {
1052               lex_error_expecting (lexer, "A", "D");
1053               return false;
1054             }
1055         }
1056       else if (!c->n_values && lex_match_id (lexer, "KEY"))
1057         {
1058           lex_match (lexer, T_EQUALS);
1059           if (lex_match_id (lexer, "VALUE"))
1060             c->key = CTCS_VALUE;
1061           else if (lex_match_id (lexer, "LABEL"))
1062             c->key = CTCS_LABEL;
1063           else
1064             {
1065               c->key = CTCS_FUNCTION;
1066               if (!parse_ctables_summary_function (lexer, &c->sort_func))
1067                 return false;
1068
1069               if (lex_match (lexer, T_LPAREN))
1070                 {
1071                   c->sort_func_var = parse_variable (lexer, dict);
1072                   if (!c->sort_func_var)
1073                     return false;
1074
1075                   if (c->sort_func == CTSF_PTILE)
1076                     {
1077                       lex_match (lexer, T_COMMA);
1078                       if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
1079                         return false;
1080                       c->percentile = lex_number (lexer);
1081                       lex_get (lexer);
1082                     }
1083
1084                   if (!lex_force_match (lexer, T_RPAREN))
1085                     return false;
1086                 }
1087               else if (ctables_function_availability (c->sort_func)
1088                        == CTFA_SCALE)
1089                 {
1090                   bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1091                   return false;
1092                 }
1093             }
1094         }
1095       else if (!c->n_values && lex_match_id (lexer, "MISSING"))
1096         {
1097           lex_match (lexer, T_EQUALS);
1098           if (lex_match_id (lexer, "INCLUDE"))
1099             c->include_missing = true;
1100           else if (lex_match_id (lexer, "EXCLUDE"))
1101             c->include_missing = false;
1102           else
1103             {
1104               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1105               return false;
1106             }
1107         }
1108       else if (lex_match_id (lexer, "TOTAL"))
1109         {
1110           lex_match (lexer, T_EQUALS);
1111           if (!parse_bool (lexer, &c->show_totals))
1112             return false;
1113         }
1114       else if (lex_match_id (lexer, "LABEL"))
1115         {
1116           lex_match (lexer, T_EQUALS);
1117           if (!lex_force_string (lexer))
1118             return false;
1119           free (c->total_label);
1120           c->total_label = ss_xstrdup (lex_tokss (lexer));
1121           lex_get (lexer);
1122         }
1123       else if (lex_match_id (lexer, "POSITION"))
1124         {
1125           lex_match (lexer, T_EQUALS);
1126           if (lex_match_id (lexer, "BEFORE"))
1127             c->totals_before = true;
1128           else if (lex_match_id (lexer, "AFTER"))
1129             c->totals_before = false;
1130           else
1131             {
1132               lex_error_expecting (lexer, "BEFORE", "AFTER");
1133               return false;
1134             }
1135         }
1136       else if (lex_match_id (lexer, "EMPTY"))
1137         {
1138           lex_match (lexer, T_EQUALS);
1139           if (lex_match_id (lexer, "INCLUDE"))
1140             c->show_empty = true;
1141           else if (lex_match_id (lexer, "EXCLUDE"))
1142             c->show_empty = false;
1143           else
1144             {
1145               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1146               return false;
1147             }
1148         }
1149       else
1150         {
1151           if (!c->n_values)
1152             lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
1153                                  "TOTAL", "LABEL", "POSITION", "EMPTY");
1154           else
1155             lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
1156           return false;
1157         }
1158     }
1159   return true;
1160 }
1161
1162 int
1163 cmd_ctables (struct lexer *lexer, struct dataset *ds)
1164 {
1165   size_t n_vars = dict_get_n_vars (dataset_dict (ds));
1166   enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
1167   for (size_t i = 0; i < n_vars; i++)
1168     vlabels[i] = CTVL_DEFAULT;
1169
1170   struct ctables *ct = xmalloc (sizeof *ct);
1171   *ct = (struct ctables) {
1172     .look = pivot_table_look_unshare (pivot_table_look_ref (
1173                                         pivot_table_look_get_default ())),
1174     .vlabels = vlabels,
1175     .hide_threshold = 5,
1176   };
1177
1178   if (!lex_force_match (lexer, T_SLASH))
1179     goto error;
1180
1181   while (!lex_match_id (lexer, "TABLE"))
1182     {
1183       if (lex_match_id (lexer, "FORMAT"))
1184         {
1185           double widths[2] = { SYSMIS, SYSMIS };
1186           double units_per_inch = 72.0;
1187
1188           while (lex_token (lexer) != T_SLASH)
1189             {
1190               if (lex_match_id (lexer, "MINCOLWIDTH"))
1191                 {
1192                   if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
1193                     goto error;
1194                 }
1195               else if (lex_match_id (lexer, "MAXCOLWIDTH"))
1196                 {
1197                   if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
1198                     goto error;
1199                 }
1200               else if (lex_match_id (lexer, "UNITS"))
1201                 {
1202                   lex_match (lexer, T_EQUALS);
1203                   if (lex_match_id (lexer, "POINTS"))
1204                     units_per_inch = 72.0;
1205                   else if (lex_match_id (lexer, "INCHES"))
1206                     units_per_inch = 1.0;
1207                   else if (lex_match_id (lexer, "CM"))
1208                     units_per_inch = 2.54;
1209                   else
1210                     {
1211                       lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
1212                       goto error;
1213                     }
1214                 }
1215               else if (lex_match_id (lexer, "EMPTY"))
1216                 {
1217                   free (ct->zero);
1218                   ct->zero = NULL;
1219
1220                   lex_match (lexer, T_EQUALS);
1221                   if (lex_match_id (lexer, "ZERO"))
1222                     {
1223                       /* Nothing to do. */
1224                     }
1225                   else if (lex_match_id (lexer, "BLANK"))
1226                     ct->zero = xstrdup ("");
1227                   else if (lex_force_string (lexer))
1228                     {
1229                       ct->zero = ss_xstrdup (lex_tokss (lexer));
1230                       lex_get (lexer);
1231                     }
1232                   else
1233                     goto error;
1234                 }
1235               else if (lex_match_id (lexer, "MISSING"))
1236                 {
1237                   lex_match (lexer, T_EQUALS);
1238                   if (!lex_force_string (lexer))
1239                     goto error;
1240
1241                   free (ct->missing);
1242                   ct->missing = (strcmp (lex_tokcstr (lexer), ".")
1243                                  ? ss_xstrdup (lex_tokss (lexer))
1244                                  : NULL);
1245                   lex_get (lexer);
1246                 }
1247               else
1248                 {
1249                   lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
1250                                        "UNITS", "EMPTY", "MISSING");
1251                   goto error;
1252                 }
1253             }
1254
1255           if (widths[0] != SYSMIS && widths[1] != SYSMIS
1256               && widths[0] > widths[1])
1257             {
1258               msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
1259               goto error;
1260             }
1261
1262           for (size_t i = 0; i < 2; i++)
1263             if (widths[i] != SYSMIS)
1264               {
1265                 int *wr = ct->look->width_ranges[TABLE_HORZ];
1266                 wr[i] = widths[i] / units_per_inch * 96.0;
1267                 if (wr[0] > wr[1])
1268                   wr[!i] = wr[i];
1269               }
1270         }
1271       else if (lex_match_id (lexer, "VLABELS"))
1272         {
1273           if (!lex_force_match_id (lexer, "VARIABLES"))
1274             goto error;
1275           lex_match (lexer, T_EQUALS);
1276
1277           struct variable **vars;
1278           size_t n_vars;
1279           if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
1280                                 PV_NO_SCRATCH))
1281             goto error;
1282
1283           if (!lex_force_match_id (lexer, "DISPLAY"))
1284             {
1285               free (vars);
1286               goto error;
1287             }
1288           lex_match (lexer, T_EQUALS);
1289
1290           enum ctables_vlabel vlabel;
1291           if (lex_match_id (lexer, "DEFAULT"))
1292             vlabel = CTVL_DEFAULT;
1293           else if (lex_match_id (lexer, "NAME"))
1294             vlabel = CTVL_NAME;
1295           else if (lex_match_id (lexer, "LABEL"))
1296             vlabel = CTVL_LABEL;
1297           else if (lex_match_id (lexer, "BOTH"))
1298             vlabel = CTVL_BOTH;
1299           else if (lex_match_id (lexer, "NONE"))
1300             vlabel = CTVL_NONE;
1301           else
1302             {
1303               lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
1304                                    "BOTH", "NONE");
1305               free (vars);
1306               goto error;
1307             }
1308
1309           for (size_t i = 0; i < n_vars; i++)
1310             ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
1311           free (vars);
1312         }
1313       else if (lex_match_id (lexer, "MRSETS"))
1314         {
1315           if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
1316             goto error;
1317           lex_match (lexer, T_EQUALS);
1318           if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
1319             goto error;
1320         }
1321       else if (lex_match_id (lexer, "SMISSING"))
1322         {
1323           if (lex_match_id (lexer, "VARIABLE"))
1324             ct->smissing_listwise = false;
1325           else if (lex_match_id (lexer, "LISTWISE"))
1326             ct->smissing_listwise = true;
1327           else
1328             {
1329               lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
1330               goto error;
1331             }
1332         }
1333       /* XXX PCOMPUTE */
1334       else if (lex_match_id (lexer, "WEIGHT"))
1335         {
1336           if (!lex_force_match_id (lexer, "VARIABLE"))
1337             goto error;
1338           lex_match (lexer, T_EQUALS);
1339           ct->base_weight = parse_variable (lexer, dataset_dict (ds));
1340           if (!ct->base_weight)
1341             goto error;
1342         }
1343       else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
1344         {
1345           if (!lex_force_match_id (lexer, "COUNT"))
1346             goto error;
1347           lex_match (lexer, T_EQUALS);
1348           if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
1349             goto error;
1350           ct->hide_threshold = lex_integer (lexer);
1351           lex_get (lexer);
1352         }
1353       else
1354         {
1355           lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
1356                                "SMISSING", "PCOMPUTE", "PPROPERTIES",
1357                                "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
1358           goto error;
1359         }
1360
1361       if (!lex_force_match (lexer, T_SLASH))
1362         goto error;
1363     }
1364
1365   size_t allocated_tables = 0;
1366   do
1367     {
1368       if (ct->n_tables >= allocated_tables)
1369         ct->tables = x2nrealloc (ct->tables, &allocated_tables,
1370                                  sizeof *ct->tables);
1371
1372       struct ctables_table *t = &ct->tables[ct->n_tables++];
1373       *t = (struct ctables_table) {
1374         .slabels_position = PIVOT_AXIS_COLUMN,
1375         .slabels_visible = true,
1376         .row_labels = CTLP_NORMAL,
1377         .col_labels = CTLP_NORMAL,
1378         .categories = xcalloc (dict_get_n_vars (dataset_dict (ds)),
1379                                sizeof *t->categories),
1380         .n_categories = dict_get_n_vars (dataset_dict (ds)),
1381         .cilevel = 95,
1382       };
1383
1384       lex_match (lexer, T_EQUALS);
1385       if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
1386         goto error;
1387       if (lex_match (lexer, T_BY))
1388         {
1389           if (!ctables_axis_parse (lexer, dataset_dict (ds),
1390                                    ct, t, PIVOT_AXIS_COLUMN))
1391             goto error;
1392
1393           if (lex_match (lexer, T_BY))
1394             {
1395               if (!ctables_axis_parse (lexer, dataset_dict (ds),
1396                                        ct, t, PIVOT_AXIS_LAYER))
1397                 goto error;
1398             }
1399         }
1400
1401       if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
1402           && !t->axes[PIVOT_AXIS_LAYER])
1403         {
1404           lex_error (lexer, _("At least one variable must be specified."));
1405           goto error;
1406         }
1407
1408       const struct ctables_axis *scales[PIVOT_N_AXES];
1409       size_t n_scales = 0;
1410       for (size_t i = 0; i < 3; i++)
1411         {
1412           scales[i] = find_scale (t->axes[i]);
1413           if (scales[i])
1414             n_scales++;
1415         }
1416       if (n_scales > 1)
1417         {
1418           msg (SE, _("Scale variables may appear only on one dimension."));
1419           if (scales[PIVOT_AXIS_ROW])
1420             msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
1421                     _("This scale variable appears in the rows dimension."));
1422           if (scales[PIVOT_AXIS_COLUMN])
1423             msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
1424                     _("This scale variable appears in the columns dimension."));
1425           if (scales[PIVOT_AXIS_LAYER])
1426             msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
1427                     _("This scale variable appears in the layer dimension."));
1428           goto error;
1429         }
1430
1431       if (lex_token (lexer) == T_ENDCMD)
1432         break;
1433       if (!lex_force_match (lexer, T_SLASH))
1434         break;
1435
1436       /* XXX Validate axes. */
1437       while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
1438         {
1439           if (lex_match_id (lexer, "SLABELS"))
1440             {
1441               while (lex_token (lexer) != T_SLASH)
1442                 {
1443                   if (lex_match_id (lexer, "POSITION"))
1444                     {
1445                       lex_match (lexer, T_EQUALS);
1446                       if (lex_match_id (lexer, "COLUMN"))
1447                         t->slabels_position = PIVOT_AXIS_COLUMN;
1448                       else if (lex_match_id (lexer, "ROW"))
1449                         t->slabels_position = PIVOT_AXIS_ROW;
1450                       else if (lex_match_id (lexer, "LAYER"))
1451                         t->slabels_position = PIVOT_AXIS_LAYER;
1452                       else
1453                         {
1454                           lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
1455                           goto error;
1456                         }
1457                     }
1458                   else if (lex_match_id (lexer, "VISIBLE"))
1459                     {
1460                       lex_match (lexer, T_EQUALS);
1461                       if (!parse_bool (lexer, &t->slabels_visible))
1462                         goto error;
1463                     }
1464                   else
1465                     {
1466                       lex_error_expecting (lexer, "POSITION", "VISIBLE");
1467                       goto error;
1468                     }
1469                 }
1470             }
1471           else if (lex_match_id (lexer, "CLABELS"))
1472             {
1473               while (lex_token (lexer) != T_SLASH)
1474                 {
1475                   if (lex_match_id (lexer, "AUTO"))
1476                     t->row_labels = t->col_labels = CTLP_NORMAL;
1477                   else if (lex_match_id (lexer, "ROWLABELS"))
1478                     {
1479                       lex_match (lexer, T_EQUALS);
1480                       if (lex_match_id (lexer, "OPPOSITE"))
1481                         t->row_labels = CTLP_OPPOSITE;
1482                       else if (lex_match_id (lexer, "LAYER"))
1483                         t->row_labels = CTLP_LAYER;
1484                       else
1485                         {
1486                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1487                           goto error;
1488                         }
1489                     }
1490                   else if (lex_match_id (lexer, "COLLABELS"))
1491                     {
1492                       lex_match (lexer, T_EQUALS);
1493                       if (lex_match_id (lexer, "OPPOSITE"))
1494                         t->col_labels = CTLP_OPPOSITE;
1495                       else if (lex_match_id (lexer, "LAYER"))
1496                         t->col_labels = CTLP_LAYER;
1497                       else
1498                         {
1499                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1500                           goto error;
1501                         }
1502                     }
1503                   else
1504                     {
1505                       lex_error_expecting (lexer, "AUTO", "ROWLABELS",
1506                                            "COLLABELS");
1507                       goto error;
1508                     }
1509                 }
1510             }
1511           else if (lex_match_id (lexer, "CRITERIA"))
1512             {
1513               if (!lex_force_match_id (lexer, "CILEVEL"))
1514                 goto error;
1515               lex_match (lexer, T_EQUALS);
1516
1517               if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
1518                 goto error;
1519               t->cilevel = lex_number (lexer);
1520               lex_get (lexer);
1521             }
1522           else if (lex_match_id (lexer, "CATEGORIES"))
1523             {
1524               if (!ctables_table_parse_categories (lexer, dataset_dict (ds), t))
1525                 goto error;
1526             }
1527           else if (lex_match_id (lexer, "TITLES"))
1528             {
1529               do
1530                 {
1531                   char **textp;
1532                   if (lex_match_id (lexer, "CAPTION"))
1533                     textp = &t->caption;
1534                   else if (lex_match_id (lexer, "CORNER"))
1535                     textp = &t->corner;
1536                   else if (lex_match_id (lexer, "TITLE"))
1537                     textp = &t->title;
1538                   else
1539                     {
1540                       lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
1541                       goto error;
1542                     }
1543                   lex_match (lexer, T_EQUALS);
1544
1545                   struct string s = DS_EMPTY_INITIALIZER;
1546                   while (lex_is_string (lexer))
1547                     {
1548                       if (!ds_is_empty (&s))
1549                         ds_put_byte (&s, ' ');
1550                       ds_put_substring (&s, lex_tokss (lexer));
1551                       lex_get (lexer);
1552                     }
1553                   free (*textp);
1554                   *textp = ds_steal_cstr (&s);
1555                 }
1556               while (lex_token (lexer) != T_SLASH
1557                      && lex_token (lexer) != T_ENDCMD);
1558             }
1559           else if (lex_match_id (lexer, "SIGTEST"))
1560             {
1561               if (!t->chisq)
1562                 {
1563                   t->chisq = xmalloc (sizeof *t->chisq);
1564                   *t->chisq = (struct ctables_chisq) {
1565                     .alpha = .05,
1566                     .include_mrsets = true,
1567                     .all_visible = true,
1568                   };
1569                 }
1570
1571               do
1572                 {
1573                   if (lex_match_id (lexer, "TYPE"))
1574                     {
1575                       lex_match (lexer, T_EQUALS);
1576                       if (!lex_force_match_id (lexer, "CHISQUARE"))
1577                         goto error;
1578                     }
1579                   else if (lex_match_id (lexer, "ALPHA"))
1580                     {
1581                       lex_match (lexer, T_EQUALS);
1582                       if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
1583                         goto error;
1584                       t->chisq->alpha = lex_number (lexer);
1585                       lex_get (lexer);
1586                     }
1587                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1588                     {
1589                       lex_match (lexer, T_EQUALS);
1590                       if (parse_bool (lexer, &t->chisq->include_mrsets))
1591                         goto error;
1592                     }
1593                   else if (lex_match_id (lexer, "CATEGORIES"))
1594                     {
1595                       lex_match (lexer, T_EQUALS);
1596                       if (lex_match_id (lexer, "ALLVISIBLE"))
1597                         t->chisq->all_visible = true;
1598                       else if (lex_match_id (lexer, "SUBTOTALS"))
1599                         t->chisq->all_visible = false;
1600                       else
1601                         {
1602                           lex_error_expecting (lexer,
1603                                                "ALLVISIBLE", "SUBTOTALS");
1604                           goto error;
1605                         }
1606                     }
1607                   else
1608                     {
1609                       lex_error_expecting (lexer, "TYPE", "ALPHA",
1610                                            "INCLUDEMRSETS", "CATEGORIES");
1611                       goto error;
1612                     }
1613                 }
1614               while (lex_token (lexer) != T_SLASH
1615                      && lex_token (lexer) != T_ENDCMD);
1616             }
1617           else if (lex_match_id (lexer, "COMPARETEST"))
1618             {
1619               if (!t->pairwise)
1620                 {
1621                   t->pairwise = xmalloc (sizeof *t->pairwise);
1622                   *t->pairwise = (struct ctables_pairwise) {
1623                     .type = PROP,
1624                     .alpha = { .05, .05 },
1625                     .adjust = BONFERRONI,
1626                     .include_mrsets = true,
1627                     .meansvariance_allcats = true,
1628                     .all_visible = true,
1629                     .merge = false,
1630                     .apa_style = true,
1631                     .show_sig = false,
1632                   };
1633                 }
1634
1635               do
1636                 {
1637                   if (lex_match_id (lexer, "TYPE"))
1638                     {
1639                       lex_match (lexer, T_EQUALS);
1640                       if (lex_match_id (lexer, "PROP"))
1641                         t->pairwise->type = PROP;
1642                       else if (lex_match_id (lexer, "MEAN"))
1643                         t->pairwise->type = MEAN;
1644                       else
1645                         {
1646                           lex_error_expecting (lexer, "PROP", "MEAN");
1647                           goto error;
1648                         }
1649                     }
1650                   else if (lex_match_id (lexer, "ALPHA"))
1651                     {
1652                       lex_match (lexer, T_EQUALS);
1653
1654                       if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1655                         goto error;
1656                       double a0 = lex_number (lexer);
1657                       lex_get (lexer);
1658
1659                       lex_match (lexer, T_COMMA);
1660                       if (lex_is_number (lexer))
1661                         {
1662                           if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1663                             goto error;
1664                           double a1 = lex_number (lexer);
1665                           lex_get (lexer);
1666
1667                           t->pairwise->alpha[0] = MIN (a0, a1);
1668                           t->pairwise->alpha[1] = MAX (a0, a1);
1669                         }
1670                       else
1671                         t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1672                     }
1673                   else if (lex_match_id (lexer, "ADJUST"))
1674                     {
1675                       lex_match (lexer, T_EQUALS);
1676                       if (lex_match_id (lexer, "BONFERRONI"))
1677                         t->pairwise->adjust = BONFERRONI;
1678                       else if (lex_match_id (lexer, "BH"))
1679                         t->pairwise->adjust = BH;
1680                       else if (lex_match_id (lexer, "NONE"))
1681                         t->pairwise->adjust = 0;
1682                       else
1683                         {
1684                           lex_error_expecting (lexer, "BONFERRONI", "BH",
1685                                                "NONE");
1686                           goto error;
1687                         }
1688                     }
1689                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1690                     {
1691                       lex_match (lexer, T_EQUALS);
1692                       if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1693                         goto error;
1694                     }
1695                   else if (lex_match_id (lexer, "MEANSVARIANCE"))
1696                     {
1697                       lex_match (lexer, T_EQUALS);
1698                       if (lex_match_id (lexer, "ALLCATS"))
1699                         t->pairwise->meansvariance_allcats = true;
1700                       else if (lex_match_id (lexer, "TESTEDCATS"))
1701                         t->pairwise->meansvariance_allcats = false;
1702                       else
1703                         {
1704                           lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1705                           goto error;
1706                         }
1707                     }
1708                   else if (lex_match_id (lexer, "CATEGORIES"))
1709                     {
1710                       lex_match (lexer, T_EQUALS);
1711                       if (lex_match_id (lexer, "ALLVISIBLE"))
1712                         t->pairwise->all_visible = true;
1713                       else if (lex_match_id (lexer, "SUBTOTALS"))
1714                         t->pairwise->all_visible = false;
1715                       else
1716                         {
1717                           lex_error_expecting (lexer, "ALLVISIBLE",
1718                                                "SUBTOTALS");
1719                           goto error;
1720                         }
1721                     }
1722                   else if (lex_match_id (lexer, "MERGE"))
1723                     {
1724                       lex_match (lexer, T_EQUALS);
1725                       if (!parse_bool (lexer, &t->pairwise->merge))
1726                         goto error;
1727                     }
1728                   else if (lex_match_id (lexer, "STYLE"))
1729                     {
1730                       lex_match (lexer, T_EQUALS);
1731                       if (lex_match_id (lexer, "APA"))
1732                         t->pairwise->apa_style = true;
1733                       else if (lex_match_id (lexer, "SIMPLE"))
1734                         t->pairwise->apa_style = false;
1735                       else
1736                         {
1737                           lex_error_expecting (lexer, "APA", "SIMPLE");
1738                           goto error;
1739                         }
1740                     }
1741                   else if (lex_match_id (lexer, "SHOWSIG"))
1742                     {
1743                       lex_match (lexer, T_EQUALS);
1744                       if (!parse_bool (lexer, &t->pairwise->show_sig))
1745                         goto error;
1746                     }
1747                   else
1748                     {
1749                       lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1750                                            "INCLUDEMRSETS", "MEANSVARIANCE",
1751                                            "CATEGORIES", "MERGE", "STYLE",
1752                                            "SHOWSIG");
1753                       goto error;
1754                     }
1755                 }
1756               while (lex_token (lexer) != T_SLASH
1757                      && lex_token (lexer) != T_ENDCMD);
1758             }
1759           else
1760             {
1761               lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1762                                    "CRITERIA", "CATEGORIES", "TITLES",
1763                                    "SIGTEST", "COMPARETEST");
1764               goto error;
1765             }
1766         }
1767     }
1768   while (lex_token (lexer) != T_ENDCMD);
1769   ctables_destroy (ct);
1770   return CMD_SUCCESS;
1771
1772 error:
1773   ctables_destroy (ct);
1774   return CMD_FAILURE;
1775 }
1776