basic parsing works
[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 struct ctables
62   {
63     struct pivot_table_look *look;
64
65     /* If this is NULL, zeros are displayed using the normal print format.
66        Otherwise, this string is displayed. */
67     char *zero;
68
69     /* If this is NULL, missing values are displayed using the normal print
70        format.  Otherwise, this string is displayed. */
71     char *missing;
72
73     /* Indexed by variable dictionary index. */
74     enum ctables_vlabel *vlabels;
75
76     bool mrsets_count_duplicates; /* MRSETS. */
77     bool smissing_listwise;       /* SMISSING. */
78     struct variable *base_weight; /* WEIGHT. */
79     int hide_threshold;           /* HIDESMALLCOUNTS. */
80
81     struct ctables_table *tables;
82     size_t n_tables;
83   };
84
85 struct ctables_postcompute
86   {
87     struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
88     const char *name;           /* Name, without leading &. */
89
90     struct ctables_postcompute_expr *expr;
91     char *label;
92     /* XXX FORMAT */
93     bool hide_source_cats;
94   };
95
96 struct ctables_postcompute_expr
97   {
98     enum ctables_postcompute_op
99       {
100         /* Terminals. */
101         CTPO_CAT_NUMBER,
102         CTPO_CAT_STRING,
103         CTPO_CAT_RANGE,
104         CTPO_CAT_MISSING,
105         /* XXX OTHERNM */
106         /* XXX SUBTOTAL and HSUBTOTAL */
107
108         /* Nonterminals. */
109         CTPO_ADD,
110         CTPO_SUB,
111         CTPO_MUL,
112         CTPO_DIV,
113         CTPO_POW,
114       }
115     op;
116
117     union
118       {
119         /* CTPO_CAT_NUMBER, CTPO_NUMBER. */
120         double number;
121
122         /* CTPO_CAT_RANGE.
123
124            XXX what about string ranges? */
125         struct
126           {
127             double low;         /* -DBL_MAX for LO. */
128             double high;        /* DBL_MAX for HIGH. */
129           }
130         range;
131
132         /* CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW. */
133         struct ctables_postcompute_expr *subs[2];
134       };
135   };
136
137 enum ctables_label_position
138   {
139     CTLP_NORMAL,
140     CTLP_OPPOSITE,
141     CTLP_LAYER,
142   };
143
144 struct ctables_table
145   {
146     struct ctables_axis *axes[PIVOT_N_AXES];
147
148     enum pivot_axis_type slabels_position;
149     bool slabels_visible;
150
151     enum ctables_label_position row_labels;
152     enum ctables_label_position col_labels;
153
154     /* XXX CATEGORIES */
155
156     double cilevel;
157
158     char *caption;
159     char *corner;
160     char *title;
161
162     struct ctables_chisq *chisq;
163     struct ctables_pairwise *pairwise;
164   };
165
166 /* Chi-square test (SIGTEST). */
167 struct ctables_chisq
168   {
169     double alpha;
170     bool include_mrsets;
171     bool all_visible;
172   };
173
174 /* Pairwise comparison test (COMPARETEST). */
175 struct ctables_pairwise
176   {
177     enum { PROP, MEAN } type;
178     double alpha[2];
179     bool include_mrsets;
180     bool meansvariance_allcats;
181     bool all_visible;
182     enum { BONFERRONI = 1, BH } adjust;
183     bool merge;
184     bool apa_style;
185     bool show_sig;
186   };
187
188 struct ctables_axis
189   {
190     enum ctables_axis_op
191       {
192         /* Terminals. */
193         CTAO_VAR,
194         CTAO_MRSET,
195
196         /* Nonterminals. */
197         CTAO_STACK,             /* + */
198         CTAO_NEST,              /* > */
199       }
200     op;
201
202     union
203       {
204         /* Terminals. */
205         struct
206           {
207             union
208               {
209                 struct variable *var;
210                 const struct mrset *mrset;
211               };
212
213             bool scale;
214             struct ctables_summary *summaries;
215             size_t n_summaries;
216           };
217
218         /* Nonterminals. */
219         struct ctables_axis *subs[2];
220       };
221   };
222
223 static void ctables_axis_destroy (struct ctables_axis *);
224
225 enum ctables_format
226   {
227     CTF_COUNT,
228     CTF_PERCENT,
229     CTF_GENERAL
230   };
231
232 #define SUMMARIES                                                       \
233     /* All variables. */                                                \
234     S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT)                      \
235     S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT)           \
236     S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT)      \
237     S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT)   \
238     S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT) \
239     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT) \
240     S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT) \
241     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT) \
242     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT) \
243     S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT) \
244     S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT) \
245     S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT) \
246     S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT) \
247     S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT) \
248     S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT) \
249     S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT) \
250     S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT) \
251     S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT) \
252     S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT) \
253     S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT) \
254     S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT) \
255     S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT) \
256     S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT) \
257                                                                         \
258     /* Scale variables, totals, and subtotals. */                       \
259     S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL)              \
260     S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL)                       \
261     S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL)                 \
262     S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL)              \
263     S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL)              \
264     S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL)                       \
265     S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL)               \
266     S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL)                    \
267     S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL)      \
268     S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL)          \
269     S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL)                          \
270     S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT)                  \
271     S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT)       \
272     S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT)                  \
273     S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT)       \
274     S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL)           \
275     S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT)      \
276     S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT)   \
277     S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT) \
278     S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT) \
279     S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT) \
280     S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT) \
281     S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT) \
282                                                                         \
283     /* Multiple response sets. */                                       \
284     S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT)           \
285     S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT) \
286     S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT) \
287     S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT) \
288     S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT) \
289     S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT) \
290     S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT) \
291     S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT) \
292     S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT) \
293     S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT) \
294     S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT) \
295     S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT) \
296     S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT) \
297     S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT) \
298     S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT) \
299     S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT) \
300     S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT) \
301     S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT) \
302     S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT) \
303     S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT) \
304     S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT) \
305     S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT)
306
307 enum ctables_summary_function
308   {
309 #define S(ENUM, NAME, LABEL, FORMAT) ENUM,
310     SUMMARIES
311 #undef S
312   };
313
314 enum {
315 #define S(ENUM, NAME, LABEL, FORMAT) +1
316   N_CTSF_FUNCTIONS = SUMMARIES
317 #undef S
318 };
319
320 struct ctables_summary
321   {
322     enum ctables_summary_function function;
323     double percentile;          /* CTSF_PTILE only. */
324     char *label;
325     struct fmt_spec format;     /* XXX extra CTABLES formats */
326   };
327
328 static void
329 ctables_summary_uninit (struct ctables_summary *s)
330 {
331   if (s)
332     free (s->label);
333 }
334
335 static bool
336 parse_col_width (struct lexer *lexer, const char *name, double *width)
337 {
338   lex_match (lexer, T_EQUALS);
339   if (lex_match_id (lexer, "DEFAULT"))
340     *width = SYSMIS;
341   else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
342     {
343       *width = lex_number (lexer);
344       lex_get (lexer);
345     }
346   else
347     return false;
348
349   return true;
350 }
351
352 static bool
353 parse_bool (struct lexer *lexer, bool *b)
354 {
355   if (lex_match_id (lexer, "NO"))
356     *b = false;
357   else if (lex_match_id (lexer, "YES"))
358     *b = true;
359   else
360     {
361       lex_error_expecting (lexer, "YES", "NO");
362       return false;
363     }
364   return true;
365 }
366
367 static bool
368 parse_ctables_summary_function (struct lexer *lexer,
369                                 enum ctables_summary_function *f)
370 {
371   struct pair
372     {
373       enum ctables_summary_function function;
374       struct substring name;
375     };
376   static struct pair names[] = {
377 #define S(ENUM, NAME, LABEL, FORMAT) { ENUM, SS_LITERAL_INITIALIZER (NAME) },
378     SUMMARIES
379
380     /* The .COUNT suffix may be omitted. */
381     S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _)
382     S(CTSF_COLPCT_COUNT, "COLPCT", _, _)
383     S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _)
384     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _)
385     S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _)
386     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _)
387     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _)
388 #undef S
389   };
390
391   if (!lex_force_id (lexer))
392     return false;
393
394   for (size_t i = 0; i < sizeof names / sizeof *names; i++)
395     if (ss_equals_case (names[i].name, lex_tokss (lexer)))
396       {
397         *f = names[i].function;
398         return true;
399       }
400
401   lex_error (lexer, _("Expecting summary function name."));
402   return false;
403 }
404
405 static void
406 ctables_axis_destroy (struct ctables_axis *axis)
407 {
408   if (!axis)
409     return;
410
411   switch (axis->op)
412     {
413     case CTAO_VAR:
414     case CTAO_MRSET:
415       for (size_t i = 0; i < axis->n_summaries; i++)
416         ctables_summary_uninit (&axis->summaries[i]);
417       free (axis->summaries);
418       break;
419
420     case CTAO_STACK:
421     case CTAO_NEST:
422       ctables_axis_destroy (axis->subs[0]);
423       ctables_axis_destroy (axis->subs[1]);
424       break;
425     }
426   free (axis);
427 }
428
429 static struct ctables_axis *
430 ctables_axis_new_nonterminal (enum ctables_axis_op op,
431                               struct ctables_axis *sub0,
432                               struct ctables_axis *sub1)
433 {
434   struct ctables_axis *axis = xmalloc (sizeof *axis);
435   *axis = (struct ctables_axis) { .op = op, .subs = { sub0, sub1 } };
436   return axis;
437 }
438
439 struct ctables_axis_parse_ctx
440   {
441     struct lexer *lexer;
442     struct dictionary *dict;
443     struct ctables *ct;
444     struct ctables_table *t;
445   };
446
447 static struct ctables_summary *
448 add_summary (struct ctables_axis *axis, enum ctables_summary_function function,
449              double percentile, size_t *allocated_summaries)
450 {
451   if (axis->n_summaries >= *allocated_summaries)
452     axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
453                                   sizeof *axis->summaries);
454
455   static const char *default_labels[] = {
456 #define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = LABEL,
457     SUMMARIES
458 #undef S
459   };
460   char *label = (function == CTSF_PTILE
461                  ? xasprintf (_("Percentile %.2f"), percentile)
462                  : xstrdup (gettext (default_labels[function])));
463
464   static const enum ctables_format default_formats[] = {
465 #define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = FORMAT,
466     SUMMARIES
467 #undef S
468   };
469   struct fmt_spec format;
470   switch (default_formats[function])
471     {
472     case CTF_COUNT:
473       format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
474       break;
475
476     case CTF_PERCENT:
477       format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
478       break;
479
480     case CTF_GENERAL:
481       format = *(axis->op == CTAO_VAR
482                  ? var_get_print_format (axis->var)
483                  : var_get_print_format (axis->mrset->vars[0]));
484       break;
485
486     default:
487       NOT_REACHED ();
488     }
489
490   struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
491   *s = (struct ctables_summary) {
492     .function = function,
493     .percentile = percentile,
494     .label = label,
495     .format = format,
496   };
497   return s;
498 }
499
500 static struct ctables_axis *ctables_axis_parse_stack (
501   struct ctables_axis_parse_ctx *);
502
503 static struct ctables_axis *
504 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
505 {
506   if (lex_match (ctx->lexer, T_LPAREN))
507     {
508       struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
509       if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
510         {
511           ctables_axis_destroy (sub);
512           return NULL;
513         }
514       return sub;
515     }
516
517   if (!lex_force_id (ctx->lexer))
518     return NULL;
519
520   const struct mrset *mrset = NULL;
521   struct variable *var = NULL;
522   if (ss_starts_with (lex_tokss (ctx->lexer), ss_cstr ("$")))
523     {
524       mrset = dict_lookup_mrset (ctx->dict, lex_tokcstr (ctx->lexer));
525       if (!mrset)
526         {
527           lex_error (ctx->lexer, _("'%s' is not the name of a "
528                                    "multiple-response set in the active file "
529                                    "dictionary."),
530                      lex_tokcstr (ctx->lexer));
531           return NULL;
532         }
533       lex_get (ctx->lexer);
534     }
535   else
536     {
537       var = parse_variable (ctx->lexer, ctx->dict);
538       if (!var)
539         return NULL;
540     }
541
542   struct ctables_axis *axis = xmalloc (sizeof *axis);
543   if (mrset)
544     *axis = (struct ctables_axis) { .op = CTAO_MRSET, .mrset = mrset };
545   else
546     *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
547
548   /* XXX should figure out default measures by reading data */
549   axis->scale = (mrset ? false
550                  : lex_match_phrase (ctx->lexer, "[S]") ? true
551                  : lex_match_phrase (ctx->lexer, "[C]") ? false
552                  : var_get_measure (var) == MEASURE_SCALE);
553
554   size_t allocated_summaries = 0;
555   if (lex_match (ctx->lexer, T_LBRACK))
556     {
557       do
558         {
559           enum ctables_summary_function function;
560           if (!parse_ctables_summary_function (ctx->lexer, &function))
561             goto error;
562
563           double percentile = 0;
564           if (function == CTSF_PTILE)
565             {
566               if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
567                 goto error;
568               percentile = lex_number (ctx->lexer);
569               lex_get (ctx->lexer);
570             }
571
572           struct ctables_summary *s = add_summary (axis, function, percentile,
573                                                    &allocated_summaries);
574           if (lex_is_string (ctx->lexer))
575             {
576               free (s->label);
577               s->label = ss_xstrdup (lex_tokss (ctx->lexer));
578               lex_get (ctx->lexer);
579             }
580           if (lex_token (ctx->lexer) == T_ID)
581             {
582               if (!parse_format_specifier (ctx->lexer, &s->format)
583                   || !fmt_check_output (&s->format)
584                   || !fmt_check_type_compat (&s->format, VAL_NUMERIC))
585                 goto error;
586             }
587           lex_match (ctx->lexer, T_COMMA);
588         }
589       while (!lex_match (ctx->lexer, T_RBRACK));
590     }
591   else
592     add_summary (axis, axis->scale ? CTSF_MEAN : CTSF_COUNT, 0,
593                  &allocated_summaries);
594   return axis;
595
596 error:
597   ctables_axis_destroy (axis);
598   return NULL;
599 }
600
601 static struct ctables_axis *
602 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
603 {
604   struct ctables_axis *lhs = ctables_axis_parse_primary (ctx);
605   if (!lhs)
606     return NULL;
607
608   while (lex_match (ctx->lexer, T_GT))
609     {
610       struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
611       if (!rhs)
612         return NULL;
613
614       lhs = ctables_axis_new_nonterminal (CTAO_NEST, lhs, rhs);
615     }
616
617   return lhs;
618 }
619
620 static struct ctables_axis *
621 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
622 {
623   struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
624   if (!lhs)
625     return NULL;
626
627   while (lex_match (ctx->lexer, T_PLUS))
628     {
629       struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
630       if (!rhs)
631         return NULL;
632
633       lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs);
634     }
635
636   return lhs;
637 }
638
639 static bool
640 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
641                     struct ctables *ct, struct ctables_table *t,
642                     enum pivot_axis_type a)
643 {
644   if (lex_token (lexer) == T_BY
645       || lex_token (lexer) == T_SLASH
646       || lex_token (lexer) == T_ENDCMD)
647     return true;
648
649   struct ctables_axis_parse_ctx ctx = {
650     .lexer = lexer,
651     .dict = dict,
652     .ct = ct,
653     .t = t
654   };
655   t->axes[a] = ctables_axis_parse_stack (&ctx);
656   return t->axes[a] != NULL;
657 }
658
659 static void
660 ctables_chisq_destroy (struct ctables_chisq *chisq)
661 {
662   free (chisq);
663 }
664
665 static void
666 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
667 {
668   free (pairwise);
669 }
670
671 static void
672 ctables_table_uninit (struct ctables_table *t)
673 {
674   if (!t)
675     return;
676
677   ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
678   ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
679   ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
680   free (t->caption);
681   free (t->corner);
682   free (t->title);
683   ctables_chisq_destroy (t->chisq);
684   ctables_pairwise_destroy (t->pairwise);
685 }
686
687 static void
688 ctables_destroy (struct ctables *ct)
689 {
690   if (!ct)
691     return;
692
693   pivot_table_look_unref (ct->look);
694   free (ct->zero);
695   free (ct->missing);
696   free (ct->vlabels);
697   for (size_t i = 0; i < ct->n_tables; i++)
698     ctables_table_uninit (&ct->tables[i]);
699   free (ct->tables);
700   free (ct);
701 }
702
703 int
704 cmd_ctables (struct lexer *lexer, struct dataset *ds)
705 {
706   size_t n_vars = dict_get_n_vars (dataset_dict (ds));
707   enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
708   for (size_t i = 0; i < n_vars; i++)
709     vlabels[i] = CTVL_DEFAULT;
710
711   struct ctables *ct = xmalloc (sizeof *ct);
712   *ct = (struct ctables) {
713     .look = pivot_table_look_unshare (pivot_table_look_ref (
714                                         pivot_table_look_get_default ())),
715     .vlabels = vlabels,
716     .hide_threshold = 5,
717   };
718
719   if (!lex_force_match (lexer, T_SLASH))
720     goto error;
721
722   while (!lex_match_id (lexer, "TABLE"))
723     {
724       if (lex_match_id (lexer, "FORMAT"))
725         {
726           double widths[2] = { SYSMIS, SYSMIS };
727           double units_per_inch = 72.0;
728
729           while (lex_token (lexer) != T_SLASH)
730             {
731               if (lex_match_id (lexer, "MINCOLWIDTH"))
732                 {
733                   if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
734                     goto error;
735                 }
736               else if (lex_match_id (lexer, "MAXCOLWIDTH"))
737                 {
738                   if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
739                     goto error;
740                 }
741               else if (lex_match_id (lexer, "UNITS"))
742                 {
743                   lex_match (lexer, T_EQUALS);
744                   if (lex_match_id (lexer, "POINTS"))
745                     units_per_inch = 72.0;
746                   else if (lex_match_id (lexer, "INCHES"))
747                     units_per_inch = 1.0;
748                   else if (lex_match_id (lexer, "CM"))
749                     units_per_inch = 2.54;
750                   else
751                     {
752                       lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
753                       goto error;
754                     }
755                 }
756               else if (lex_match_id (lexer, "EMPTY"))
757                 {
758                   free (ct->zero);
759                   ct->zero = NULL;
760
761                   lex_match (lexer, T_EQUALS);
762                   if (lex_match_id (lexer, "ZERO"))
763                     {
764                       /* Nothing to do. */
765                     }
766                   else if (lex_match_id (lexer, "BLANK"))
767                     ct->zero = xstrdup ("");
768                   else if (lex_force_string (lexer))
769                     {
770                       ct->zero = ss_xstrdup (lex_tokss (lexer));
771                       lex_get (lexer);
772                     }
773                   else
774                     goto error;
775                 }
776               else if (lex_match_id (lexer, "MISSING"))
777                 {
778                   lex_match (lexer, T_EQUALS);
779                   if (!lex_force_string (lexer))
780                     goto error;
781
782                   free (ct->missing);
783                   ct->missing = (strcmp (lex_tokcstr (lexer), ".")
784                                  ? ss_xstrdup (lex_tokss (lexer))
785                                  : NULL);
786                   lex_get (lexer);
787                 }
788               else
789                 {
790                   lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
791                                        "UNITS", "EMPTY", "MISSING");
792                   goto error;
793                 }
794             }
795
796           if (widths[0] != SYSMIS && widths[1] != SYSMIS
797               && widths[0] > widths[1])
798             {
799               msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
800               goto error;
801             }
802
803           for (size_t i = 0; i < 2; i++)
804             if (widths[i] != SYSMIS)
805               {
806                 int *wr = ct->look->width_ranges[TABLE_HORZ];
807                 wr[i] = widths[i] / units_per_inch * 96.0;
808                 if (wr[0] > wr[1])
809                   wr[!i] = wr[i];
810               }
811         }
812       else if (lex_match_id (lexer, "VLABELS"))
813         {
814           if (!lex_force_match_id (lexer, "VARIABLES"))
815             goto error;
816           lex_match (lexer, T_EQUALS);
817
818           struct variable **vars;
819           size_t n_vars;
820           if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
821                                 PV_NO_SCRATCH))
822             goto error;
823
824           if (!lex_force_match_id (lexer, "DISPLAY"))
825             {
826               free (vars);
827               goto error;
828             }
829           lex_match (lexer, T_EQUALS);
830
831           enum ctables_vlabel vlabel;
832           if (lex_match_id (lexer, "DEFAULT"))
833             vlabel = CTVL_DEFAULT;
834           else if (lex_match_id (lexer, "NAME"))
835             vlabel = CTVL_NAME;
836           else if (lex_match_id (lexer, "LABEL"))
837             vlabel = CTVL_LABEL;
838           else if (lex_match_id (lexer, "BOTH"))
839             vlabel = CTVL_BOTH;
840           else if (lex_match_id (lexer, "NONE"))
841             vlabel = CTVL_NONE;
842           else
843             {
844               lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
845                                    "BOTH", "NONE");
846               free (vars);
847               goto error;
848             }
849
850           for (size_t i = 0; i < n_vars; i++)
851             ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
852           free (vars);
853         }
854       else if (lex_match_id (lexer, "MRSETS"))
855         {
856           if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
857             goto error;
858           lex_match (lexer, T_EQUALS);
859           if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
860             goto error;
861         }
862       else if (lex_match_id (lexer, "SMISSING"))
863         {
864           if (lex_match_id (lexer, "VARIABLE"))
865             ct->smissing_listwise = false;
866           else if (lex_match_id (lexer, "LISTWISE"))
867             ct->smissing_listwise = true;
868           else
869             {
870               lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
871               goto error;
872             }
873         }
874       /* XXX PCOMPUTE */
875       else if (lex_match_id (lexer, "WEIGHT"))
876         {
877           if (!lex_force_match_id (lexer, "VARIABLE"))
878             goto error;
879           lex_match (lexer, T_EQUALS);
880           ct->base_weight = parse_variable (lexer, dataset_dict (ds));
881           if (!ct->base_weight)
882             goto error;
883         }
884       else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
885         {
886           if (!lex_force_match_id (lexer, "COUNT"))
887             goto error;
888           lex_match (lexer, T_EQUALS);
889           if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
890             goto error;
891           ct->hide_threshold = lex_integer (lexer);
892           lex_get (lexer);
893         }
894       else
895         {
896           lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
897                                "SMISSING", "PCOMPUTE", "PPROPERTIES",
898                                "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
899           goto error;
900         }
901
902       if (!lex_force_match (lexer, T_SLASH))
903         goto error;
904     }
905
906   size_t allocated_tables = 0;
907   do
908     {
909       if (ct->n_tables >= allocated_tables)
910         ct->tables = x2nrealloc (ct->tables, &allocated_tables,
911                                  sizeof *ct->tables);
912
913       struct ctables_table *t = &ct->tables[ct->n_tables++];
914       *t = (struct ctables_table) {
915         .slabels_position = PIVOT_AXIS_COLUMN,
916         .slabels_visible = true,
917         .row_labels = CTLP_NORMAL,
918         .col_labels = CTLP_NORMAL,
919         .cilevel = 95,
920       };
921
922       lex_match (lexer, T_EQUALS);
923       if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
924         goto error;
925
926       if (lex_match (lexer, T_BY))
927         {
928           if (!ctables_axis_parse (lexer, dataset_dict (ds),
929                                    ct, t, PIVOT_AXIS_COLUMN))
930             goto error;
931
932           if (lex_match (lexer, T_BY))
933             {
934               if (!ctables_axis_parse (lexer, dataset_dict (ds),
935                                        ct, t, PIVOT_AXIS_LAYER))
936                 goto error;
937             }
938         }
939       if (lex_token (lexer) == T_ENDCMD)
940         break;
941       if (!lex_force_match (lexer, T_SLASH))
942         break;
943
944       /* XXX Validate axes. */
945       while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
946         {
947           if (lex_match_id (lexer, "SLABELS"))
948             {
949               while (lex_token (lexer) != T_SLASH)
950                 {
951                   if (lex_match_id (lexer, "POSITION"))
952                     {
953                       lex_match (lexer, T_EQUALS);
954                       if (lex_match_id (lexer, "COLUMN"))
955                         t->slabels_position = PIVOT_AXIS_COLUMN;
956                       else if (lex_match_id (lexer, "ROW"))
957                         t->slabels_position = PIVOT_AXIS_ROW;
958                       else if (lex_match_id (lexer, "LAYER"))
959                         t->slabels_position = PIVOT_AXIS_LAYER;
960                       else
961                         {
962                           lex_error_expecting (lexer, "COLUMN", "ROW",
963                                                "LAYER");
964                           goto error;
965                         }
966                     }
967                   else if (lex_match_id (lexer, "VISIBLE"))
968                     {
969                       lex_match (lexer, T_EQUALS);
970                       if (!parse_bool (lexer, &t->slabels_visible))
971                         goto error;
972                     }
973                   else
974                     {
975                       lex_error_expecting (lexer, "POSITION", "VISIBLE");
976                       goto error;
977                     }
978                 }
979             }
980           else if (lex_match_id (lexer, "CLABELS"))
981             {
982               while (lex_token (lexer) != T_SLASH)
983                 {
984                   if (lex_match_id (lexer, "AUTO"))
985                     t->row_labels = t->col_labels = CTLP_NORMAL;
986                   else if (lex_match_id (lexer, "ROWLABELS"))
987                     {
988                       lex_match (lexer, T_EQUALS);
989                       if (lex_match_id (lexer, "OPPOSITE"))
990                         t->row_labels = CTLP_OPPOSITE;
991                       else if (lex_match_id (lexer, "LAYER"))
992                         t->row_labels = CTLP_LAYER;
993                       else
994                         {
995                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
996                           goto error;
997                         }
998                     }
999                   else if (lex_match_id (lexer, "COLLABELS"))
1000                     {
1001                       lex_match (lexer, T_EQUALS);
1002                       if (lex_match_id (lexer, "OPPOSITE"))
1003                         t->col_labels = CTLP_OPPOSITE;
1004                       else if (lex_match_id (lexer, "LAYER"))
1005                         t->col_labels = CTLP_LAYER;
1006                       else
1007                         {
1008                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1009                           goto error;
1010                         }
1011                     }
1012                   else
1013                     {
1014                       lex_error_expecting (lexer, "AUTO", "ROWLABELS",
1015                                            "COLLABELS");
1016                       goto error;
1017                     }
1018                 }
1019             }
1020           else if (lex_match_id (lexer, "CRITERIA"))
1021             {
1022               if (!lex_force_match_id (lexer, "CILEVEL"))
1023                 goto error;
1024               lex_match (lexer, T_EQUALS);
1025
1026               if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
1027                 goto error;
1028               t->cilevel = lex_number (lexer);
1029               lex_get (lexer);
1030             }
1031           else if (lex_match_id (lexer, "TITLES"))
1032             {
1033               do
1034                 {
1035                   char **textp;
1036                   if (lex_match_id (lexer, "CAPTION"))
1037                     textp = &t->caption;
1038                   else if (lex_match_id (lexer, "CORNER"))
1039                     textp = &t->corner;
1040                   else if (lex_match_id (lexer, "TITLE"))
1041                     textp = &t->title;
1042                   else
1043                     {
1044                       lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
1045                       goto error;
1046                     }
1047                   lex_match (lexer, T_EQUALS);
1048
1049                   struct string s = DS_EMPTY_INITIALIZER;
1050                   while (lex_is_string (lexer))
1051                     {
1052                       if (!ds_is_empty (&s))
1053                         ds_put_byte (&s, ' ');
1054                       ds_put_substring (&s, lex_tokss (lexer));
1055                       lex_get (lexer);
1056                     }
1057                   free (*textp);
1058                   *textp = ds_steal_cstr (&s);
1059                 }
1060               while (lex_token (lexer) != T_SLASH
1061                      && lex_token (lexer) != T_ENDCMD);
1062             }
1063           else if (lex_match_id (lexer, "SIGTEST"))
1064             {
1065               if (!t->chisq)
1066                 {
1067                   t->chisq = xmalloc (sizeof *t->chisq);
1068                   *t->chisq = (struct ctables_chisq) {
1069                     .alpha = .05,
1070                     .include_mrsets = true,
1071                     .all_visible = true,
1072                   };
1073                 }
1074
1075               do
1076                 {
1077                   if (lex_match_id (lexer, "TYPE"))
1078                     {
1079                       lex_match (lexer, T_EQUALS);
1080                       if (!lex_force_match_id (lexer, "CHISQUARE"))
1081                         goto error;
1082                     }
1083                   else if (lex_match_id (lexer, "ALPHA"))
1084                     {
1085                       lex_match (lexer, T_EQUALS);
1086                       if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
1087                         goto error;
1088                       t->chisq->alpha = lex_number (lexer);
1089                       lex_get (lexer);
1090                     }
1091                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1092                     {
1093                       lex_match (lexer, T_EQUALS);
1094                       if (parse_bool (lexer, &t->chisq->include_mrsets))
1095                         goto error;
1096                     }
1097                   else if (lex_match_id (lexer, "CATEGORIES"))
1098                     {
1099                       lex_match (lexer, T_EQUALS);
1100                       if (lex_match_id (lexer, "ALLVISIBLE"))
1101                         t->chisq->all_visible = true;
1102                       else if (lex_match_id (lexer, "SUBTOTALS"))
1103                         t->chisq->all_visible = false;
1104                       else
1105                         {
1106                           lex_error_expecting (lexer,
1107                                                "ALLVISIBLE", "SUBTOTALS");
1108                           goto error;
1109                         }
1110                     }
1111                   else
1112                     {
1113                       lex_error_expecting (lexer, "TYPE", "ALPHA",
1114                                            "INCLUDEMRSETS", "CATEGORIES");
1115                       goto error;
1116                     }
1117                 }
1118               while (lex_token (lexer) != T_SLASH
1119                      && lex_token (lexer) != T_ENDCMD);
1120             }
1121           else if (lex_match_id (lexer, "COMPARETEST"))
1122             {
1123               if (!t->pairwise)
1124                 {
1125                   t->pairwise = xmalloc (sizeof *t->pairwise);
1126                   *t->pairwise = (struct ctables_pairwise) {
1127                     .type = PROP,
1128                     .alpha = { .05, .05 },
1129                     .adjust = BONFERRONI,
1130                     .include_mrsets = true,
1131                     .meansvariance_allcats = true,
1132                     .all_visible = true,
1133                     .merge = false,
1134                     .apa_style = true,
1135                     .show_sig = false,
1136                   };
1137                 }
1138
1139               do
1140                 {
1141                   if (lex_match_id (lexer, "TYPE"))
1142                     {
1143                       lex_match (lexer, T_EQUALS);
1144                       if (lex_match_id (lexer, "PROP"))
1145                         t->pairwise->type = PROP;
1146                       else if (lex_match_id (lexer, "MEAN"))
1147                         t->pairwise->type = MEAN;
1148                       else
1149                         {
1150                           lex_error_expecting (lexer, "PROP", "MEAN");
1151                           goto error;
1152                         }
1153                     }
1154                   else if (lex_match_id (lexer, "ALPHA"))
1155                     {
1156                       lex_match (lexer, T_EQUALS);
1157
1158                       if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1159                         goto error;
1160                       double a0 = lex_number (lexer);
1161                       lex_get (lexer);
1162
1163                       lex_match (lexer, T_COMMA);
1164                       if (lex_is_number (lexer))
1165                         {
1166                           if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1167                             goto error;
1168                           double a1 = lex_number (lexer);
1169                           lex_get (lexer);
1170
1171                           t->pairwise->alpha[0] = MIN (a0, a1);
1172                           t->pairwise->alpha[1] = MAX (a0, a1);
1173                         }
1174                       else
1175                         t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1176                     }
1177                   else if (lex_match_id (lexer, "ADJUST"))
1178                     {
1179                       lex_match (lexer, T_EQUALS);
1180                       if (lex_match_id (lexer, "BONFERRONI"))
1181                         t->pairwise->adjust = BONFERRONI;
1182                       else if (lex_match_id (lexer, "BH"))
1183                         t->pairwise->adjust = BH;
1184                       else if (lex_match_id (lexer, "NONE"))
1185                         t->pairwise->adjust = 0;
1186                       else
1187                         {
1188                           lex_error_expecting (lexer, "BONFERRONI", "BH",
1189                                                "NONE");
1190                           goto error;
1191                         }
1192                     }
1193                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1194                     {
1195                       lex_match (lexer, T_EQUALS);
1196                       if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1197                         goto error;
1198                     }
1199                   else if (lex_match_id (lexer, "MEANSVARIANCE"))
1200                     {
1201                       lex_match (lexer, T_EQUALS);
1202                       if (lex_match_id (lexer, "ALLCATS"))
1203                         t->pairwise->meansvariance_allcats = true;
1204                       else if (lex_match_id (lexer, "TESTEDCATS"))
1205                         t->pairwise->meansvariance_allcats = false;
1206                       else
1207                         {
1208                           lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1209                           goto error;
1210                         }
1211                     }
1212                   else if (lex_match_id (lexer, "CATEGORIES"))
1213                     {
1214                       lex_match (lexer, T_EQUALS);
1215                       if (lex_match_id (lexer, "ALLVISIBLE"))
1216                         t->pairwise->all_visible = true;
1217                       else if (lex_match_id (lexer, "SUBTOTALS"))
1218                         t->pairwise->all_visible = false;
1219                       else
1220                         {
1221                           lex_error_expecting (lexer, "ALLVISIBLE",
1222                                                "SUBTOTALS");
1223                           goto error;
1224                         }
1225                     }
1226                   else if (lex_match_id (lexer, "MERGE"))
1227                     {
1228                       lex_match (lexer, T_EQUALS);
1229                       if (!parse_bool (lexer, &t->pairwise->merge))
1230                         goto error;
1231                     }
1232                   else if (lex_match_id (lexer, "STYLE"))
1233                     {
1234                       lex_match (lexer, T_EQUALS);
1235                       if (lex_match_id (lexer, "APA"))
1236                         t->pairwise->apa_style = true;
1237                       else if (lex_match_id (lexer, "SIMPLE"))
1238                         t->pairwise->apa_style = false;
1239                       else
1240                         {
1241                           lex_error_expecting (lexer, "APA", "SIMPLE");
1242                           goto error;
1243                         }
1244                     }
1245                   else if (lex_match_id (lexer, "SHOWSIG"))
1246                     {
1247                       lex_match (lexer, T_EQUALS);
1248                       if (!parse_bool (lexer, &t->pairwise->show_sig))
1249                         goto error;
1250                     }
1251                   else
1252                     {
1253                       lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1254                                            "INCLUDEMRSETS", "MEANSVARIANCE",
1255                                            "CATEGORIES", "MERGE", "STYLE",
1256                                            "SHOWSIG");
1257                       goto error;
1258                     }
1259                 }
1260               while (lex_token (lexer) != T_SLASH
1261                      && lex_token (lexer) != T_ENDCMD);
1262             }
1263           else
1264             {
1265               lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1266                                    "CRITERIA", "CATEGORIES", "TITLES",
1267                                    "SIGTEST", "COMPARETEST");
1268               goto error;
1269             }
1270         }
1271     }
1272   while (lex_token (lexer) != T_ENDCMD);
1273   ctables_destroy (ct);
1274   return CMD_SUCCESS;
1275
1276 error:
1277   ctables_destroy (ct);
1278   return CMD_FAILURE;
1279 }
1280