From: Ben Pfaff Date: Sun, 30 May 2021 22:50:46 +0000 (-0700) Subject: Some basic macros and tests work. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=db57300f720b0916461aad06cec4d268481f06e0 Some basic macros and tests work. --- diff --git a/src/language/command.def b/src/language/command.def index 12f30c7c03..63df224bde 100644 --- a/src/language/command.def +++ b/src/language/command.def @@ -155,6 +155,7 @@ DEF_CMD (S_INPUT_PROGRAM, 0, "END INPUT PROGRAM", cmd_end_input_program) DEF_CMD (S_INPUT_PROGRAM, 0, "REREAD", cmd_reread) /* Commands for testing PSPP. */ +DEF_CMD (S_ANY, F_TESTING, "DEBUG EXPAND", cmd_debug_expand) DEF_CMD (S_ANY, F_TESTING, "DEBUG EVALUATE", cmd_debug_evaluate) DEF_CMD (S_ANY, F_TESTING, "DEBUG FORMAT GUESSER", cmd_debug_format_guesser) DEF_CMD (S_ANY, F_TESTING, "DEBUG MOMENTS", cmd_debug_moments) diff --git a/src/language/control/define.c b/src/language/control/define.c index 686685fc9c..6c71f02c09 100644 --- a/src/language/control/define.c +++ b/src/language/control/define.c @@ -221,3 +221,13 @@ error: macro_destroy (m); return CMD_FAILURE; } + +int +cmd_debug_expand (struct lexer *lexer, struct dataset *ds UNUSED) +{ + settings_set_mprint (true); + + while (lex_token (lexer) != T_STOP) + lex_get (lexer); + return CMD_SUCCESS; +} diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 5ff5099652..f577c598b8 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -1694,9 +1694,10 @@ lex_source_get (const struct lex_source *src_) { if (!lex_source_get__ (src)) { - /* This should not be reachable because we always get a T_STOP at the - end of input and the macro_expander should always terminate - expansion on T_STOP. */ + /* This should not be reachable because we always get a T_ENDCMD at + the end of an input file (transformed from T_STOP by + lex_source_try_get()) and the macro_expander should always + terminate expansion on T_ENDCMD. */ NOT_REACHED (); } @@ -1723,6 +1724,15 @@ lex_source_get (const struct lex_source *src_) macro_expander_get_expansion (me, &expansion); macro_expander_destroy (me); + if (settings_get_mprint ()) + { + struct string mprint = DS_EMPTY_INITIALIZER; + macro_tokens_to_representation (&expansion, &mprint); + output_item_submit (text_item_create (TEXT_ITEM_LOG, ds_cstr (&mprint), + _("Macro Expansion"))); + ds_destroy (&mprint); + } + for (size_t i = 0; i < expansion.n; i++) { *lex_push_token__ (src) = (struct lex_token) { diff --git a/src/language/lexer/macro.c b/src/language/lexer/macro.c index a15b7064a5..32185fb9cf 100644 --- a/src/language/lexer/macro.c +++ b/src/language/lexer/macro.c @@ -46,6 +46,12 @@ macro_token_uninit (struct macro_token *mt) ss_dealloc (&mt->representation); } +void +macro_token_to_representation (struct macro_token *mt, struct string *s) +{ + ds_put_substring (s, mt->representation); +} + void macro_tokens_copy (struct macro_tokens *dst, const struct macro_tokens *src) { @@ -153,6 +159,115 @@ macro_tokens_print (const struct macro_tokens *mts, FILE *stream) token_print (&mts->mts[i].token, stream); } +enum token_class + { + TC_ENDCMD, /* No space before or after (new-line after). */ + TC_BINOP, /* Space on both sides. */ + TC_COMMA, /* Space afterward. */ + TC_ID, /* Don't need spaces except sequentially. */ + TC_PUNCT, /* Don't need spaces except sequentially. */ + }; + +static bool +needs_space (enum token_class prev, enum token_class next) +{ + /* Don't need a space before or after the end of a command. + (A new-line is needed afterward as a special case.) */ + if (prev == TC_ENDCMD || next == TC_ENDCMD) + return false; + + /* Binary operators always have a space on both sides. */ + if (prev == TC_BINOP || next == TC_BINOP) + return true; + + /* A comma always has a space afterward. */ + if (prev == TC_COMMA) + return true; + + /* Otherwise, PREV is TC_ID or TC_PUNCT, which only need a space if there are + two or them in a row. */ + return prev == next; +} + +static enum token_class +classify_token (enum token_type type) +{ + switch (type) + { + case T_ID: + case T_MACRO_ID: + case T_POS_NUM: + case T_NEG_NUM: + case T_STRING: + return TC_ID; + + case T_STOP: + return TC_PUNCT; + + case T_ENDCMD: + return TC_ENDCMD; + + case T_LPAREN: + case T_RPAREN: + case T_LBRACK: + case T_RBRACK: + return TC_PUNCT; + + case T_PLUS: + case T_DASH: + case T_ASTERISK: + case T_SLASH: + case T_EQUALS: + case T_AND: + case T_OR: + case T_NOT: + case T_EQ: + case T_GE: + case T_GT: + case T_LE: + case T_LT: + case T_NE: + case T_ALL: + case T_BY: + case T_TO: + case T_WITH: + case T_EXP: + case T_MACRO_PUNCT: + return TC_BINOP; + + case T_COMMA: + return TC_COMMA; + } + + NOT_REACHED (); +} + +void +macro_tokens_to_representation (struct macro_tokens *mts, struct string *s) +{ + if (!mts->n) + return; + + macro_token_to_representation (&mts->mts[0], s); + for (size_t i = 1; i < mts->n; i++) + { + enum token_type prev = mts->mts[i - 1].token.type; + enum token_type next = mts->mts[i].token.type; + + if (prev == T_ENDCMD) + ds_put_byte (s, '\n'); + else + { + enum token_class pc = classify_token (prev); + enum token_class nc = classify_token (next); + if (needs_space (pc, nc)) + ds_put_byte (s, ' '); + } + + macro_token_to_representation (&mts->mts[i], s); + } +} + void macro_destroy (struct macro *m) { @@ -341,9 +456,9 @@ static int me_add_arg (struct macro_expander *me, const struct macro_token *mt) { const struct token *token = &mt->token; - if (token->type == T_STOP) + if (token->type == T_ENDCMD || token->type == T_STOP) { - msg (SE, _("Unexpected end of file reading argument %s " + msg (SE, _("Unexpected end of command reading argument %s " "to macro %s."), me->param->name, me->macro->name); return me_error (me); @@ -373,7 +488,7 @@ me_add_arg (struct macro_expander *me, const struct macro_token *mt) else { const struct token *end - = p->arg_type == ARG_CMDEND ? &p->charend : &p->enclose[1]; + = p->arg_type == ARG_CHAREND ? &p->charend : &p->enclose[1]; if (token_equal (token, end)) return me_next_arg (me); macro_tokens_add (arg, mt); @@ -851,9 +966,9 @@ macro_expand (const struct macro_tokens *mts, int retval = macro_expander_create (macros, token, &subme); for (size_t j = 1; !retval; j++) { - const struct macro_token stop = { .token = { .type = T_STOP } }; + const struct macro_token endcmd = { .token = { .type = T_ENDCMD } }; retval = macro_expander_add ( - subme, i + j < mts->n ? &mts->mts[i + j] : &stop); + subme, i + j < mts->n ? &mts->mts[i + j] : &endcmd); } if (retval > 0) { diff --git a/src/language/lexer/macro.h b/src/language/lexer/macro.h index 23ae1d9a18..0b6fd0dac3 100644 --- a/src/language/lexer/macro.h +++ b/src/language/lexer/macro.h @@ -36,6 +36,8 @@ struct macro_token void macro_token_copy (struct macro_token *, const struct macro_token *); void macro_token_uninit (struct macro_token *); +void macro_token_to_representation (struct macro_token *, struct string *); + struct macro_tokens { struct macro_token *mts; @@ -51,6 +53,8 @@ void macro_tokens_add (struct macro_tokens *, const struct macro_token *); void macro_tokens_from_string (struct macro_tokens *, const struct substring, enum segmenter_mode); +void macro_tokens_to_representation (struct macro_tokens *, struct string *); + void macro_tokens_print (const struct macro_tokens *, FILE *); struct macro_param diff --git a/tests/language/control/define.at b/tests/language/control/define.at index d187b046d5..f1f3f41fa0 100644 --- a/tests/language/control/define.at +++ b/tests/language/control/define.at @@ -16,11 +16,57 @@ dnl along with this program. If not, see . dnl AT_BANNER([DEFINE]) -AT_SETUP([DEFINE]) +AT_SETUP([simple macro expansion]) AT_DATA([define.sps], [dnl -DEFINE !variables() - brand model license color +DEFINE !macro() +a b c d +e f g h. +i j k l +1,2,3,4. +5+6+7. +m(n,o). +"a" "b" "c" 'a' 'b' 'c'. +"x "" y". !ENDDEFINE. +DEBUG EXPAND. +!macro() +]) +AT_CHECK([pspp --testing-mode define.sps], [0], [dnl +a b c d e f g h. +i j k l 1, 2, 3, 4. +5 + 6 + 7. +m(n, o). +"a" "b" "c" 'a' 'b' 'c'. +"x "" y". +]) +AT_CLEANUP + +AT_SETUP([macro expansion with arguments]) +AT_DATA([define.sps], [dnl +DEFINE !t1(!positional !tokens(1)) (!1) !ENDDEFINE. +DEFINE !t2(!positional !tokens(2)) (!1) !ENDDEFINE. +DEFINE !ce(!positional !charend('/')) (!1) !ENDDEFINE. +DEBUG EXPAND. +!t1 a. +!t1 b. +!t1 a b. + +!t2 a b. +!t2 b c d. + +!ce x y z/. +]) +AT_CHECK([pspp --testing-mode define.sps], [0], [dnl +(a) + +(b) + +(a) + +(a b) + +(b c) + +(x y z) ]) -AT_CHECK([pspp define.sps]) AT_CLEANUP