)TABLE, )DATE, )TIME
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 3 Jul 2022 17:16:54 +0000 (10:16 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 3 Jul 2022 17:16:54 +0000 (10:16 -0700)
doc/statistics.texi
src/language/stats/ctables.c
tests/language/stats/ctables.at

index 5b8b841933f3974b2f12410df414864a15b87523..4966d6afb8ef5ad078b56577be5a827d7af50be4 100644 (file)
@@ -923,7 +923,7 @@ where each @i{axis} may be empty or take one of the following forms:
 @i{axis} + @i{axis}
 @i{axis} > @i{axis}
 (@i{axis})
-@i{axis} @t{(}@i{summary} [@i{string}] [@i{format}]@t{)}
+@i{axis} @t{[}@i{summary} [@i{string}] [@i{format}]@t{]}
 @end display
 
 The following subcommands precede the first @code{TABLE} subcommand
index 497969a057133f702eb9f898de53fa86ad41c1a3..072b9f83a8d4f688d65c2a095f4498e454597f5a 100644 (file)
@@ -31,6 +31,7 @@
 #include "language/command.h"
 #include "language/lexer/format-parser.h"
 #include "language/lexer/lexer.h"
+#include "language/lexer/token.h"
 #include "language/lexer/variable-parser.h"
 #include "libpspp/array.h"
 #include "libpspp/assertion.h"
@@ -1730,7 +1731,6 @@ parse_category_string (const struct ctables_category *cat,
                        struct substring s, struct dictionary *dict,
                        enum fmt_type format, double *n)
 {
-  printf ("parse %.*s as %s\n", (int) s.length, s.string, fmt_name (format));
   union value v;
   char *error = data_in (s, dict_get_encoding (dict), format,
                          settings_get_fmt_settings (), &v, 0, NULL);
@@ -3302,7 +3302,6 @@ ctables_cell_insert (struct ctables_section *s,
           if (var_missing)
             is_missing = true;
 
-          printf ("ctables_cell_insert %s: ", var_get_name (var));
           cats[a][i] = ctables_categories_match (
             s->table->categories[var_get_dict_index (var)], value, var);
           if (!cats[a][i])
@@ -3687,7 +3686,7 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
     pivot_table_set_caption (
       pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
   if (t->corner)
-    pivot_table_set_caption (
+    pivot_table_set_corner_text (
       pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
 
   bool summary_dimension = (t->summary_axis != t->slabels_axis
@@ -5082,6 +5081,92 @@ error:
   return false;
 }
 
+static void
+put_strftime (struct string *out, time_t now, const char *format)
+{
+  const struct tm *tm = localtime (&now);
+  char value[128];
+  strftime (value, sizeof value, format, tm);
+  ds_put_cstr (out, value);
+}
+
+static bool
+skip_prefix (struct substring *s, struct substring prefix)
+{
+  if (ss_starts_with (*s, prefix))
+    {
+      ss_advance (s, prefix.length);
+      return true;
+    }
+  else
+    return false;
+}
+
+static void
+put_table_expression (struct string *out, struct lexer *lexer,
+                      struct dictionary *dict, int expr_start, int expr_end)
+{
+  size_t nest = 0;
+  for (int ofs = expr_start; ofs < expr_end; ofs++)
+    {
+      const struct token *t = lex_ofs_token (lexer, ofs);
+      if (t->type == T_LBRACK)
+        nest++;
+      else if (t->type == T_RBRACK && nest > 0)
+        nest--;
+      else if (nest > 0)
+        {
+          /* Nothing. */
+        }
+      else if (t->type == T_ID)
+        {
+          const struct variable *var
+            = dict_lookup_var (dict, t->string.string);
+          const char *label = var ? var_get_label (var) : NULL;
+          ds_put_cstr (out, label ? label : t->string.string);
+        }
+      else
+        {
+          if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
+            ds_put_byte (out, ' ');
+
+          char *repr = lex_ofs_representation (lexer, ofs, ofs);
+          ds_put_cstr (out, repr);
+          free (repr);
+
+          if (ofs + 1 != expr_end && t->type != T_LPAREN)
+            ds_put_byte (out, ' ');
+        }
+    }
+}
+
+static void
+put_title_text (struct string *out, struct substring in, time_t now,
+                struct lexer *lexer, struct dictionary *dict,
+                int expr_start, int expr_end)
+{
+  for (;;)
+    {
+      size_t chunk = ss_find_byte (in, ')');
+      ds_put_substring (out, ss_head (in, chunk));
+      ss_advance (&in, chunk);
+      if (ss_is_empty (in))
+        return;
+
+      if (skip_prefix (&in, ss_cstr (")DATE")))
+        put_strftime (out, now, "%x");
+      else if (skip_prefix (&in, ss_cstr (")TIME")))
+        put_strftime (out, now, "%X");
+      else if (skip_prefix (&in, ss_cstr (")TABLE")))
+        put_table_expression (out, lexer, dict, expr_start, expr_end);
+      else
+        {
+          ds_put_byte (out, ')');
+          ss_advance (&in, 1);
+        }
+    }
+}
+
 int
 cmd_ctables (struct lexer *lexer, struct dataset *ds)
 {
@@ -5104,6 +5189,8 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
     .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
   };
 
+  time_t now = time (NULL);
+
   struct ctf
     {
       enum fmt_type type;
@@ -5371,6 +5458,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
       ct->tables[ct->n_tables++] = t;
 
       lex_match (lexer, T_EQUALS);
+      int expr_start = lex_ofs (lexer);
       if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
         goto error;
       if (lex_match (lexer, T_BY))
@@ -5386,6 +5474,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
                 goto error;
             }
         }
+      int expr_end = lex_ofs (lexer);
 
       if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
           && !t->axes[PIVOT_AXIS_LAYER])
@@ -5574,7 +5663,9 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
                     {
                       if (!ds_is_empty (&s))
                         ds_put_byte (&s, ' ');
-                      ds_put_substring (&s, lex_tokss (lexer));
+                      put_title_text (&s, lex_tokss (lexer), now,
+                                      lexer, dataset_dict (ds),
+                                      expr_start, expr_end);
                       lex_get (lexer);
                     }
                   free (*textp);
index 0923769b0b7198fb243158cea2e4c52e1ae78a23..a5ae1eebf0f00d03b6816bd63e77287bd5f8efff 100644 (file)
@@ -12,10 +12,7 @@ dnl   * U-prefix for unweighted summaries.
 dnl   * .LCL and .UCL suffixes.
 dnl   * .SE suffixes.
 dnl - CATEGORIES:
-dnl   * String values
-dnl   * Date values
 dnl   * Data-dependent sorting.
-dnl - TITLES: )DATE, )TIME, )TABLE.
 dnl - PCOMPUTE:
 dnl   * multi-dimensional
 dnl   * MISSING, OTHERNM
@@ -36,6 +33,8 @@ dnl - EMPTY=INCLUDE For string ranges.
 dnl - Summary functions:
 dnl   * Separate summary functions for totals and subtotals.
 dnl - CATEGORIES:
+dnl   * String values
+dnl   * Date values
 dnl   * THRU (numeric ranges)
 dnl   * THRU (string ranges)
 dnl   * OTHERNM
@@ -46,6 +45,7 @@ dnl   * MISSING.
 dnl - HIDESMALLCOUNTS.
 dnl - Date/time variables and values
 dnl - Special formats for summary functions: NEGPAREN, NEQUAL, PAREN, PCTPAREN.
+dnl - TITLES: )DATE, )TIME, )TABLE.
 dnl
 dnl Not for v1:
 dnl - Multiple response sets