Basic tests for macros and arguments pass.
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 31 May 2021 01:08:21 +0000 (18:08 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 31 May 2021 01:08:21 +0000 (18:08 -0700)
src/language/control/define.c
src/language/lexer/lexer.c
src/language/lexer/lexer.h
src/language/lexer/macro.c
tests/language/control/define.at

index 6c71f02c09e3986d40f5f35011a8590d1be6b210..187eb9fcf840b1e7932d36c49cfc77f1bde1b1ae 100644 (file)
@@ -23,6 +23,7 @@
 #include "language/lexer/macro.h"
 #include "language/lexer/scan.h"
 #include "language/lexer/token.h"
+#include "libpspp/message.h"
 
 #include "gl/xalloc.h"
 
@@ -59,7 +60,7 @@ parse_quoted_token (struct lexer *lexer, struct token *token)
   struct substring s = lex_tokss (lexer);
   struct string_lexer slex;
   string_lexer_init (&slex, s.string, s.length, SEG_MODE_INTERACTIVE);
-  struct token another_token;
+  struct token another_token = { .type = T_STOP };
   if (!string_lexer_next (&slex, token)
       || string_lexer_next (&slex, &another_token))
     {
@@ -228,6 +229,13 @@ cmd_debug_expand (struct lexer *lexer, struct dataset *ds UNUSED)
   settings_set_mprint (true);
 
   while (lex_token (lexer) != T_STOP)
-    lex_get (lexer);
+    {
+      if (!lex_next_is_from_macro (lexer, 0) && lex_token (lexer) != T_ENDCMD)
+        {
+          struct substring rep = lex_next_representation (lexer, 0, 0);
+          msg (MN, "unexpanded token \"%.*s\"", (int) rep.length, rep.string);
+        }
+      lex_get (lexer);
+    }
   return CMD_SUCCESS;
 }
index f577c598b89c9bf5a0ce939e94476feef5cfd417..534ed077e1b2a6ae3d54f13c7241100a9f583bd9 100644 (file)
@@ -216,6 +216,7 @@ lex_push_token__ (struct lex_source *src)
 
   token = &src->tokens[deque_push_front (&src->deque)];
   token->token = (struct token) { .type = T_STOP };
+  token->from_macro = false;
   return token;
 }
 
@@ -972,6 +973,12 @@ lex_next_representation (const struct lexer *lexer, int n0, int n1)
   return lex_source_get_syntax__ (lex_source__ (lexer), n0, n1);
 }
 
+bool
+lex_next_is_from_macro (const struct lexer *lexer, int n)
+{
+  return lex_next__ (lexer, n)->from_macro;
+}
+
 static bool
 lex_tokens_match (const struct token *actual, const struct token *expected)
 {
index 86bb4f2b5dadd2089d0c6ec4d175b5fe67fb648d..596e27a335422eda40b2139e47ceae3480126544 100644 (file)
@@ -149,6 +149,7 @@ struct substring lex_next_tokss (const struct lexer *, int n);
 /* Token representation. */
 struct substring lex_next_representation (const struct lexer *,
                                           int n0, int n1);
+bool lex_next_is_from_macro (const struct lexer *, int n);
 
 /* Current position. */
 int lex_get_first_line_number (const struct lexer *, int n);
index 32185fb9cf03016bc3c8cf1bab602e0e536d2149..fd5ac452cff3a0421902d40b4321e8bf98d56347 100644 (file)
@@ -429,7 +429,9 @@ me_next_arg (struct macro_expander *me)
         return me_finished (me);
       else
         {
-          me->state = me->param->positional ? ME_ARG : ME_KEYWORD;
+          me->state = (!me->param->positional ? ME_KEYWORD
+                       : me->param->arg_type == ARG_ENCLOSE ? ME_ENCLOSE
+                       : ME_ARG);
           return 0;
         }
     }
@@ -455,8 +457,11 @@ me_error (struct macro_expander *me)
 static int
 me_add_arg (struct macro_expander *me, const struct macro_token *mt)
 {
+  const struct macro_param *p = me->param;
+
   const struct token *token = &mt->token;
-  if (token->type == T_ENDCMD || token->type == T_STOP)
+  if ((token->type == T_ENDCMD || token->type == T_STOP)
+      && p->arg_type != ARG_CMDEND)
     {
       msg (SE, _("Unexpected end of command reading argument %s "
                  "to macro %s."), me->param->name, me->macro->name);
@@ -466,7 +471,6 @@ me_add_arg (struct macro_expander *me, const struct macro_token *mt)
 
   me->n_tokens++;
 
-  const struct macro_param *p = me->param;
   struct macro_tokens **argp = &me->args[p - me->macro->params];
   if (!*argp)
     *argp = xzalloc (sizeof **argp);
@@ -613,7 +617,9 @@ macro_expander_create (const struct macro_set *macros,
     return 1;
   else
     {
-      me->state = macro->params[0].positional ? ME_ARG : ME_KEYWORD;
+      me->state = (!macro->params[0].positional ? ME_KEYWORD
+                   : macro->params[0].arg_type == ARG_ENCLOSE ? ME_ENCLOSE
+                   : ME_ARG);
       me->args = xcalloc (macro->n_params, sizeof *me->args);
       me->param = macro->params;
       return 0;
index f1f3f41fa062bbdb7346dcad911d9c4729177009..0211c71785511c75f647ebe47fec7ac5c7ee2a7b 100644 (file)
@@ -29,7 +29,7 @@ m(n,o).
 "x "" y".
 !ENDDEFINE.
 DEBUG EXPAND.
-!macro()
+!macro
 ])
 AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
 a b c d e f g h.
@@ -43,30 +43,101 @@ 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.
+DEFINE !title(!positional !tokens(1)) !1 !ENDDEFINE.
+DEFINE !t1(!positional !tokens(1)) t1 (!1) !ENDDEFINE.
+DEFINE !t2(!positional !tokens(2)) t2 (!1) !ENDDEFINE.
+
+DEFINE !ce(!positional !charend('/')) ce (!1) !ENDDEFINE.
+DEFINE !ce2(!positional !charend('(')
+           /!positional !charend(')'))
+ce2 (!1, !2)
+!ENDDEFINE.
+
+DEFINE !e(!positional !enclose('{','}')) e (!1) !ENDDEFINE.
+
+DEFINE !cmd(!positional !cmdend) cmd(!1) !ENDDEFINE.
+DEFINE !cmd2(!positional !cmdend
+            /!positional !tokens(1))
+cmd2(!1, !2)
+!ENDDEFINE.
+
 DEBUG EXPAND.
+!title "!TOKENS(1) argument."
 !t1 a.
 !t1 b.
 !t1 a b.
 
+!title "!TOKENS(2) argument."
 !t2 a b.
 !t2 b c d.
 
+!title "!CHAREND argument."
+!ce/.
+!ce x/.
+!ce x y/.
 !ce x y z/.
+
+!title "Two !CHAREND arguments."
+!ce2 x(y).
+!ce2 1 2 3 4().
+
+!title "!ENCLOSE argument."
+!e {}.
+!e {a}.
+!e {a b}.
+
+!title "!CMDEND argument."
+!cmd 1 2 3 4.
+!cmd2 5 6.
+7.
 ])
 AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
-(a)
+"!TOKENS(1) argument."
+
+t1(a)
+
+t1(b)
+
+t1(a)
+
+note: unexpanded token "b"
+
+"!TOKENS(2) argument."
+
+t2(a b)
+
+t2(b c)
+
+note: unexpanded token "d"
+
+"!CHAREND argument."
+
+ce( )
+
+ce(x)
+
+ce(x y)
+
+ce(x y z)
+
+"Two !CHAREND arguments."
+
+ce2(x, y)
+
+ce2(1 2 3 4, )
+
+"!ENCLOSE argument."
+
+e( )
 
-(b)
+e(a)
 
-(a)
+e(a b)
 
-(a b)
+"!CMDEND argument."
 
-(b c)
+cmd(1 2 3 4)
 
-(x y z)
+cmd2(5 6, 7)
 ])
 AT_CLEANUP