From f4aa41e356cfddfac22add29193d6e2e43259c93 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 20 Nov 2022 09:02:00 -0800 Subject: [PATCH] Disentangle the interaction parser from the variable parser. 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 | 60 ++-------------------------- src/language/lexer/variable-parser.h | 12 ------ src/language/stats/glm.c | 55 +++++++++++++++++++++++++ src/language/stats/logistic.c | 20 +++++----- 4 files changed, 69 insertions(+), 78 deletions(-) diff --git a/src/language/lexer/variable-parser.c b/src/language/lexer/variable-parser.c index f444f15e2b..8b577578e7 100644 --- a/src/language/lexer/variable-parser.c +++ b/src/language/lexer/variable-parser.c @@ -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); -} - diff --git a/src/language/lexer/variable-parser.h b/src/language/lexer/variable-parser.h index 4419c9ebf6..8fd4824671 100644 --- a/src/language/lexer/variable-parser.h +++ b/src/language/lexer/variable-parser.h @@ -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 */ diff --git a/src/language/stats/glm.c b/src/language/stats/glm.c index 004ae97fee..6a98dd4f49 100644 --- a/src/language/stats/glm.c +++ b/src/language/stats/glm.c @@ -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) diff --git a/src/language/stats/logistic.c b/src/language/stats/logistic.c index 75f1d2a13f..6365fb8278 100644 --- a/src/language/stats/logistic.c +++ b/src/language/stats/logistic.c @@ -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")) { -- 2.30.2