string ranges work
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 2 Aug 2022 05:46:58 +0000 (22:46 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 2 Aug 2022 05:47:12 +0000 (22:47 -0700)
src/language/stats/ctables.c
tests/language/stats/ctables.at

index a69e8d56593a035aad6e6fba5c249681a8c45387..78ba956aa4997ff138f588666580190e27744fbd 100644 (file)
@@ -1878,6 +1878,7 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
     case CTPO_CAT_NUMBER:
     case CTPO_CAT_STRING:
     case CTPO_CAT_NRANGE:
+    case CTPO_CAT_SRANGE:
     case CTPO_CAT_MISSING:
     case CTPO_CAT_OTHERNM:
     case CTPO_CAT_SUBTOTAL:
@@ -1950,10 +1951,9 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
               dict, e->subs[i], pc_cat, cats, cats_location))
           return false;
       return true;
-
-    default:
-      NOT_REACHED ();
     }
+
+  NOT_REACHED ();
 }
 
 static bool
@@ -5574,6 +5574,15 @@ ctpo_cat_nrange (double low, double high)
   };
 }
 
+static struct ctables_pcexpr
+ctpo_cat_srange (struct substring low, struct substring high)
+{
+  return (struct ctables_pcexpr) {
+    .op = CTPO_CAT_SRANGE,
+    .srange = { low, high },
+  };
+}
+
 static struct ctables_pcexpr *
 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
 {
@@ -5610,10 +5619,22 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
     {
       if (lex_match_id (lexer, "LO"))
         {
-          if (!lex_force_match_id (lexer, "THRU") || !lex_force_num (lexer))
+          if (!lex_force_match_id (lexer, "THRU"))
             return false;
-          e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
-          lex_get (lexer);
+
+          if (lex_is_string (lexer))
+            {
+              struct substring low = { .string = NULL };
+              struct substring high = parse_substring (lexer, dict);
+              e = ctpo_cat_srange (low, high);
+            }
+          else
+            {
+              if (lex_force_num (lexer))
+                return false;
+              e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
+              lex_get (lexer);
+            }
         }
       else if (lex_is_number (lexer))
         {
@@ -5637,12 +5658,28 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
         }
       else if (lex_is_string (lexer))
         {
-          struct substring s = recode_substring_pool (
-            dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
-          ss_rtrim (&s, ss_cstr (" "));
+          struct substring s = parse_substring (lexer, dict);
 
-          e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
-          lex_get (lexer);
+          if (lex_match_id (lexer, "THRU"))
+            {
+              struct substring high;
+
+              if (lex_match_id (lexer, "HI"))
+                high = (struct substring) { .string = NULL };
+              else
+                {
+                  if (!lex_force_string (lexer))
+                    {
+                      ss_dealloc (&s);
+                      return false;
+                    }
+                  high = parse_substring (lexer, dict);
+                }
+
+              e = ctpo_cat_srange (s, high);
+            }
+          else
+            e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
         }
       else
         {
@@ -5654,6 +5691,11 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
         {
           if (e.op == CTPO_CAT_STRING)
             ss_dealloc (&e.string);
+          else if (e.op == CTPO_CAT_SRANGE)
+            {
+              ss_dealloc (&e.srange[0]);
+              ss_dealloc (&e.srange[1]);
+            }
           return NULL;
         }
     }
index ebda3ce826c8378a8ff221130c282688024304da..9e2e272e0a603ea0c48a86b8ee54cafa86491735 100644 (file)
@@ -28,7 +28,6 @@ dnl - TITLES: )DATE, )TIME, )TABLE.
 dnl - Test PCOMPUTE:
 dnl   * PCOMPUTE for more than one kind of summary (e.g. [COUNT, ROWPCT]).
 dnl   * MISSING, OTHERNM
-dnl   * strings and string ranges
 dnl   * multi-dimensional (multiple CCT_POSTCOMPUTE in one cell)
 dnl   * dates
 dnl - PPROPERTIES:
@@ -758,7 +757,7 @@ CTABLES
     /TABLE licensed
     /CATEGORIES VARIABLES=licensed ['Yes', &notyes, 'No', 'DontKnow', 'Refused'].
 CTABLES
-    /PCOMPUTE &notyes=EXPR(['No']+['DontKnow']+['Refused'])
+    /PCOMPUTE &notyes=EXPR(['DontKnow' THRU 'No'] + ['Refused'])
     /PPROPERTIES &notyes LABEL='Not Yes' HIDESOURCECATS=YES
     /TABLE licensed
     /CATEGORIES VARIABLES=licensed ['Yes', &notyes, 'DontKnow' THRU 'No', 'Refused'].
@@ -817,6 +816,14 @@ AT_CHECK([pspp ctables.sps -O box=unicode -O width=80], [0], [dnl
 ├────────────────┼─────┤
 │licensed Yes    │ 6379│
 │         Not Yes│  620│
+╰────────────────┴─────╯
+
+      Custom Tables
+╭────────────────┬─────╮
+│                │Count│
+├────────────────┼─────┤
+│licensed Yes    │ 6379│
+│         Not Yes│  620│
 ╰────────────────┴─────╯
 ])
 AT_CLEANUP