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)
{
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 ();
}
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) {
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)
{
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)
{
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);
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);
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)
{
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