!BLANKS and !CONCAT pass basic tests.
[pspp] / src / language / lexer / macro.c
index 32185fb9cf03016bc3c8cf1bab602e0e536d2149..628b83af01bd3d5e80a67c88baf8c5963463c5ee 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);
@@ -531,10 +535,13 @@ me_enclose (struct macro_expander *me, const struct macro_token *mt)
 static const struct macro_param *
 macro_find_parameter_by_name (const struct macro *m, struct substring name)
 {
+  if (ss_first (name) == '!')
+    ss_advance (&name, 1);
+
   for (size_t i = 0; i < m->n_params; i++)
     {
       const struct macro_param *p = &m->params[i];
-      struct substring p_name = ss_cstr (p->name);
+      struct substring p_name = ss_cstr (p->name + 1);
       if (!utf8_strncasecmp (p_name.string, p_name.length,
                              name.string, name.length))
         return p;
@@ -613,7 +620,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;
@@ -866,33 +875,19 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
   else if (parse_macro_function (ctx, &args, ss_cstr ("!concat"), 1, INT_MAX,
                                  input_consumed))
     {
-      struct string s;
-      bool all_strings = true;
+      struct string s = DS_EMPTY_INITIALIZER;
       for (size_t i = 0; i < args.n; i++)
         {
           if (args.mts[i].token.type == T_STRING)
             ds_put_substring (&s, args.mts[i].token.string);
           else
-            {
-              all_strings = false;
-              ds_put_substring (&s, args.mts[i].representation);
-            }
+            ds_put_substring (&s, args.mts[i].representation);
         }
 
-      if (all_strings)
-        {
-          *output = (struct macro_token) {
-            .token = { .type = T_STRING, .string = s.ss },
-          };
-          output->representation = ss_cstr (token_to_string (&output->token));
-        }
-      else
-        {
-          *output = (struct macro_token) {
-            .token = { .type = T_MACRO_ID /*XXX*/, .string = s.ss },
-          };
-          ss_alloc_substring (&output->representation, s.ss);
-        }
+      *output = (struct macro_token) {
+        .token = { .type = T_MACRO_ID /*XXX*/, .string = s.ss },
+      };
+      ss_alloc_substring (&output->representation, s.ss);
     }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
                                  input_consumed))
@@ -918,6 +913,17 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       else
         macro_token_copy (output, &args.mts[0]);
     }
+  else if (ctx->n_input > 0
+           && ctx->input[0].token.type == T_MACRO_ID
+           && ss_equals_case (ctx->input[0].token.string, ss_cstr ("!null")))
+    {
+      *input_consumed = 1;
+      *output = (struct macro_token) {
+        .token = { .type = T_MACRO_ID /* XXX*/ },
+      };
+      ss_alloc_substring (&output->token.string, ss_cstr (""));
+      return true;
+    }
   else
     return false;
 
@@ -931,6 +937,13 @@ macro_expand (const struct macro_tokens *mts,
               const struct macro_expander *me, bool *expand,
               struct macro_tokens *exp)
 {
+  /* Macro expansion:
+
+     - Macro names in macro bodies are not expanded by default.  !EVAL()
+       expands them.
+
+     - Macro names in arguments to macro invocations (outside of macro bodies)
+       are expanded by default, unless !NOEXPAND. */
   if (nesting_countdown <= 0)
     {
       printf ("maximum nesting level exceeded\n");