Disentangle the interaction parser from the variable parser.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 20 Nov 2022 17:02:00 +0000 (09:02 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 20 Nov 2022 17:03:22 +0000 (09:03 -0800)
Both GLM and LOGISTIC REGRESSION used the interaction parser, but only GLM
used it to parse interactions in the full sense.  LOGISTIC REGRESSION was
just parsing a list of variables and then creating interactions from them.
This commit moves the interaction parser from variable-parser into GLM and
adds new code to LOGISTIC REGRESSION that satisfies its needs.

src/language/lexer/variable-parser.c
src/language/lexer/variable-parser.h
src/language/stats/glm.c
src/language/stats/logistic.c

index f444f15e2beb0c28ccc708af69463e64a3ebdd03..8b577578e721eb600b8c196f99185ecc3e954304 100644 (file)
@@ -1128,63 +1128,9 @@ var_set_create_from_array (struct variable *const *var, size_t n_vars)
 bool
 lex_match_variable (struct lexer *lexer, const struct dictionary *dict, const struct variable **var)
 {
-  if (lex_token (lexer) !=  T_ID)
+  if (lex_token (lexer) != T_ID)
     return false;
 
-  *var = parse_variable_const  (lexer, dict);
-
-  if (*var == NULL)
-    return false;
-  return true;
+  *var = parse_variable_const (lexer, dict);
+  return *var != NULL;
 }
-
-/* An interaction is a variable followed by {*, BY} followed by an interaction */
-static bool
-parse_internal_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact, struct interaction **it)
-{
-  const struct variable *v = NULL;
-  assert (iact);
-
-  switch  (lex_next_token (lexer, 1))
-    {
-    case T_ENDCMD:
-    case T_SLASH:
-    case T_COMMA:
-    case T_ID:
-    case T_BY:
-    case T_ASTERISK:
-      break;
-    default:
-      return false;
-      break;
-    }
-
-  if (! lex_match_variable (lexer, dict, &v))
-    {
-      if (it)
-       interaction_destroy (*it);
-      *iact = NULL;
-      return false;
-    }
-
-  assert (v);
-
-  if (*iact == NULL)
-    *iact = interaction_create (v);
-  else
-    interaction_add_variable (*iact, v);
-
-  if (lex_match (lexer, T_ASTERISK) || lex_match (lexer, T_BY))
-    {
-      return parse_internal_interaction (lexer, dict, iact, iact);
-    }
-
-  return true;
-}
-
-bool
-parse_design_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact)
-{
-  return parse_internal_interaction (lexer, dict, iact, NULL);
-}
-
index 4419c9ebf61a1467805ae3782cbfd90c98d95951..8fd4824671e8062aa1e33fea6b3453bf79cea6e9 100644 (file)
@@ -159,16 +159,4 @@ const_var_set_destroy (struct const_var_set *vs)
 bool
 lex_match_variable (struct lexer *lexer, const struct dictionary *dict, const struct variable **var);
 
-struct interaction;
-
-/* Parse an interaction.
-   If not successful return false.
-   Otherwise, a newly created interaction will be placed in IACT.
-   It is the caller's responsibility to destroy this interaction.
- */
-bool
-parse_design_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact);
-
-
-
 #endif /* variable-parser.h */
index 004ae97feef474389de5015fbc8de71edad4f75f..6a98dd4f49cd192c6e46c5f45f96c60d4275be11 100644 (file)
@@ -797,6 +797,61 @@ parse_nested_variable (struct lexer *lexer, struct glm_spec *glm)
   return false;
 }
 
+/* An interaction is a variable followed by {*, BY} followed by an interaction */
+static bool
+parse_internal_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact, struct interaction **it)
+{
+  const struct variable *v = NULL;
+  assert (iact);
+
+  switch  (lex_next_token (lexer, 1))
+    {
+    case T_ENDCMD:
+    case T_SLASH:
+    case T_COMMA:
+    case T_ID:
+    case T_BY:
+    case T_ASTERISK:
+      break;
+    default:
+      return false;
+      break;
+    }
+
+  if (! lex_match_variable (lexer, dict, &v))
+    {
+      if (it)
+       interaction_destroy (*it);
+      *iact = NULL;
+      return false;
+    }
+
+  assert (v);
+
+  if (*iact == NULL)
+    *iact = interaction_create (v);
+  else
+    interaction_add_variable (*iact, v);
+
+  if (lex_match (lexer, T_ASTERISK) || lex_match (lexer, T_BY))
+    {
+      return parse_internal_interaction (lexer, dict, iact, iact);
+    }
+
+  return true;
+}
+
+/* Parse an interaction.
+   If not successful return false.
+   Otherwise, a newly created interaction will be placed in IACT.
+   It is the caller's responsibility to destroy this interaction.
+ */
+static bool
+parse_design_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact)
+{
+  return parse_internal_interaction (lexer, dict, iact, NULL);
+}
+
 /* A design term is an interaction OR a nested variable */
 static bool
 parse_design_term (struct lexer *lexer, struct glm_spec *glm)
index 75f1d2a13fb4f6a439fb0a6e4428b3cc3a45cec1..6365fb8278a3877d041d624f7ecfd81c5e3c9ef4 100644 (file)
@@ -834,15 +834,17 @@ cmd_logistic (struct lexer *lexer, struct dataset *ds)
       else if (lex_match_id (lexer, "CATEGORICAL"))
        {
          lex_match (lexer, T_EQUALS);
-         do
-           {
-             lr.cat_predictors = xrealloc (lr.cat_predictors,
-                                 sizeof (*lr.cat_predictors) * ++lr.n_cat_predictors);
-             lr.cat_predictors[lr.n_cat_predictors - 1] = 0;
-           }
-         while (parse_design_interaction (lexer, lr.dict,
-                                          lr.cat_predictors + lr.n_cat_predictors - 1));
-         lr.n_cat_predictors--;
+          struct variable **cats;
+          size_t n_cats;
+          if (!parse_variables (lexer, lr.dict, &cats, &n_cats, PV_NO_DUPLICATE))
+            goto error;
+
+          lr.cat_predictors = xrealloc (lr.cat_predictors,
+                                        sizeof *lr.cat_predictors
+                                        * (n_cats + lr.n_cat_predictors));
+          for (size_t i = 0; i < n_cats; i++)
+            lr.cat_predictors[lr.n_cat_predictors++] = interaction_create (cats[i]);
+          free (cats);
        }
       else if (lex_match_id (lexer, "PRINT"))
        {