Macro arguments and the !length function work.
[pspp] / src / language / lexer / macro.c
index b94a04ad7f4be3455c796e7531e7b0f3f39a01fb..8633de9857608b8babbfd1cf28f38a8ce49d5f99 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <stdlib.h>
 
+#include "data/settings.h"
 #include "language/lexer/segment.h"
 #include "language/lexer/scan.h"
 #include "libpspp/assertion.h"
@@ -64,6 +65,7 @@ macro_destroy (struct macro *m)
     }
   free (m->params);
   ss_dealloc (&m->body);
+  tokens_uninit (&m->body_tokens);
   free (m);
 }
 \f
@@ -134,7 +136,8 @@ macro_set_add (struct macro_set *set, struct macro *m)
 \f
 enum me_state
   {
-    ME_START,
+    /* Error state. */
+    ME_ERROR,
 
     /* Accumulating tokens in me->params toward the end of any type of
        argument. */
@@ -160,7 +163,7 @@ struct macro_expander
 
     const struct macro *macro;
     struct tokens **args;
-    size_t arg_index;
+    const struct macro_param *param;
   };
 
 static int
@@ -178,22 +181,19 @@ me_finished (struct macro_expander *me)
 static int
 me_next_arg (struct macro_expander *me)
 {
-  if (me->arg_index >= me->macro->n_params)
+  if (!me->param)
     {
       assert (!me->macro->n_params);
       return me_finished (me);
     }
-  else if (!me->macro->params[me->arg_index].name)
+  else if (me->param->positional)
     {
-      me->arg_index++;
-      if (me->arg_index >= me->macro->n_params)
+      me->param++;
+      if (me->param >= &me->macro->params[me->macro->n_params])
         return me_finished (me);
       else
         {
-          if (!me->macro->params[me->arg_index].name)
-            me->state = ME_ARG;
-          else
-            me->state = ME_KEYWORD;
+          me->state = me->param->positional ? ME_ARG : ME_KEYWORD;
           return 0;
         }
     }
@@ -209,48 +209,28 @@ me_next_arg (struct macro_expander *me)
     }
 }
 
-static int
-me_add_start (struct macro_expander *me, const struct token *token)
-{
-  if (token->type != T_ID && token->type != T_MACRO_ID)
-    return -1;
-
-  me->macro = macro_set_find (me->macros, token->string.string);
-  if (!me->macro)
-    return -1;
-
-  me->n_tokens = 1;
-  me->args = xcalloc (me->macro->n_params, sizeof *me->args);
-  me->arg_index = 0;
-  return me_next_arg (me);
-}
-
 static int
 me_error (struct macro_expander *me)
 {
-  me->state = ME_START;
+  me->state = ME_ERROR;
   return -1;
 }
 
 static int
 me_add_arg (struct macro_expander *me, const struct token *token)
 {
-  const struct macro_param *p = &me->macro->params[me->arg_index];
   if (token->type == T_STOP)
     {
-      char *param_name = (p->name
-                          ? xstrdup (p->name)
-                          : xasprintf ("%zu", me->arg_index));
       msg (SE, _("Unexpected end of file reading argument %s "
-                 "to macro %s."), param_name, me->macro->name);
-      free (param_name);
+                 "to macro %s."), me->param->name, me->macro->name);
 
       return me_error (me);
     }
 
   me->n_tokens++;
 
-  struct tokens **argp = &me->args[me->arg_index];
+  const struct macro_param *p = me->param;
+  struct tokens **argp = &me->args[p - me->macro->params];
   if (!*argp)
     *argp = xzalloc (sizeof **argp);
   struct tokens *arg = *argp;
@@ -283,20 +263,15 @@ static int
 me_expected (struct macro_expander *me, const struct token *token,
              const struct token *wanted)
 {
-  const struct macro_param *p = &me->macro->params[me->arg_index];
-  char *param_name = (p->name
-                      ? xstrdup (p->name)
-                      : xasprintf ("%zu", me->arg_index));
   char *actual = token_to_string (token);
   if (!actual)
     actual = xstrdup ("<eof>");
   char *expected = token_to_string (wanted);
   msg (SE, _("Found `%s' while expecting `%s' reading argument %s "
              "to macro %s."),
-       actual, expected, param_name, me->macro->name);
+       actual, expected, me->param->name, me->macro->name);
   free (expected);
   free (actual);
-  free (param_name);
 
   return me_error (me);
 }
@@ -306,14 +281,27 @@ me_enclose (struct macro_expander *me, const struct token *token)
 {
   me->n_tokens++;
 
-  const struct macro_param *p = &me->macro->params[me->arg_index];
-  if (token_equal (&p->enclose[0], token))
+  if (token_equal (&me->param->enclose[0], token))
     {
       me->state = ME_ARG;
       return 0;
     }
 
-  return me_expected (me, token, &p->enclose[0]);
+  return me_expected (me, token, &me->param->enclose[0]);
+}
+
+static const struct macro_param *
+macro_find_parameter_by_name (const struct macro *m, struct substring name)
+{
+  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);
+      if (!utf8_strncasecmp (p_name.string, p_name.length,
+                             name.string, name.length))
+        return p;
+    }
+  return NULL;
 }
 
 static int
@@ -322,24 +310,23 @@ me_keyword (struct macro_expander *me, const struct token *token)
   if (token->type != T_ID)
     return me_finished (me);
 
-  for (size_t i = 0; i < me->macro->n_params; i++)
+  const struct macro_param *p = macro_find_parameter_by_name (me->macro,
+                                                              token->string);
+  if (p)
     {
-      const struct macro_param *p = &me->macro->params[i];
-      if (p->name && ss_equals_case (ss_cstr (p->name), token->string))
+      size_t arg_index = p - me->macro->params;
+      me->param = p;
+      if (me->args[arg_index])
         {
-          me->arg_index = i;
-          if (me->args[i])
-            {
-              msg (SE,
-                   _("Argument %s multiply specified in call to macro %s."),
-                   p->name, me->macro->name);
-              return me_error (me);
-            }
-
-          me->n_tokens++;
-          me->state = ME_EQUALS;
-          return 0;
+          msg (SE,
+               _("Argument %s multiply specified in call to macro %s."),
+               p->name, me->macro->name);
+          return me_error (me);
         }
+
+      me->n_tokens++;
+      me->state = ME_EQUALS;
+      return 0;
     }
 
   return me_finished (me);
@@ -378,16 +365,20 @@ macro_expander_create (const struct macro_set *macros,
   struct macro_expander *me = xmalloc (sizeof *me);
   *me = (struct macro_expander) {
     .macros = macros,
-
-    .state = ME_START,
     .n_tokens = 1,
-
     .macro = macro,
-    .args = xcalloc (macro->n_params, sizeof *me->args),
-    .arg_index = 0,
   };
   *mep = me;
-  return me_next_arg (me);
+
+  if (!macro->n_params)
+    return 1;
+  else
+    {
+      me->state = macro->params[0].positional ? ME_ARG : ME_KEYWORD;
+      me->args = xcalloc (macro->n_params, sizeof *me->args);
+      me->param = macro->params;
+      return 0;
+    }
 }
 
 void
@@ -426,8 +417,8 @@ macro_expander_add (struct macro_expander *me, const struct token *token)
 {
   switch (me->state)
     {
-    case ME_START:
-      return me_add_start (me, token);
+    case ME_ERROR:
+      return -1;
 
     case ME_ARG:
       return me_add_arg (me, token);
@@ -446,63 +437,139 @@ macro_expander_add (struct macro_expander *me, const struct token *token)
     }
 }
 
-void
-macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
+static void
+macro_expand (const struct tokens *tokens, int nesting_countdown,
+              const struct macro_set *macros, const struct macro_expander *me,
+              bool *expand, struct tokens *exp)
 {
-  struct state
+  if (nesting_countdown <= 0)
     {
-      struct segmenter segmenter;
-      struct substring body;
-    };
-
-  struct state state;
-  segmenter_init (&state.segmenter, SEG_MODE_INTERACTIVE /*XXX*/);
-  state.body = me->macro->body;
-
-  struct state saved = state;
-
-  struct token token = { .type = T_STOP };
+      printf ("maximum nesting level exceeded\n");
+      for (size_t i = 0; i < tokens->n; i++)
+        tokens_add (exp, &tokens->tokens[i]);
+      return;
+    }
 
-  while (state.body.length > 0)
+  for (size_t i = 0; i < tokens->n; i++)
     {
-      struct scanner scanner;
-      scanner_init (&scanner, &token);
+      const struct token *token = &tokens->tokens[i];
+      if (token->type == T_MACRO_ID && me)
+        {
+          const struct macro_param *param = macro_find_parameter_by_name (
+            me->macro, token->string);
+          if (param)
+            {
+              printf ("expand %s to:\n", param->name);
+              const struct tokens *arg = me->args[param - me->macro->params];
+              tokens_print (arg, stdout);
+              if (*expand && param->expand_arg)
+                macro_expand (arg, nesting_countdown, macros, NULL, expand, exp);
+              else
+                for (size_t i = 0; i < arg->n; i++)
+                  tokens_add (exp, &arg->tokens[i]);
+              continue;
+            }
+        }
 
-      for (;;)
+      if (*expand)
         {
-          enum segment_type type;
-          int seg_len = segmenter_push (&state.segmenter, state.body.string,
-                                        state.body.length, true, &type);
-          assert (seg_len >= 0);
-
-          struct substring segment = ss_head (state.body, seg_len);
-          ss_advance (&state.body, seg_len);
-          printf ("segment \"%.*s\" %s token.type=%d\n", (int) segment.length, segment.string, segment_type_to_string (type), token.type);
-
-          enum scan_result result = scanner_push (&scanner, type, segment, &token);
-          if (result == SCAN_SAVE)
-            saved = state;
-          else if (result == SCAN_BACK)
+          struct macro_expander *subme;
+          int retval = macro_expander_create (macros, token, &subme);
+          for (size_t j = 1; !retval; j++)
             {
-              printf ("back\n");
-              state = saved;
-              break;
+              static const struct token stop = { .type = T_STOP };
+              retval = macro_expander_add (
+                subme, i + j < tokens->n ? &tokens->tokens[i + j] : &stop);
             }
-          else if (result == SCAN_DONE)
+          if (retval > 0)
             {
-              printf ("done\n");
-              break;
+              i += retval - 1;
+              macro_expand (&subme->macro->body_tokens, nesting_countdown - 1,
+                            macros, subme, expand, exp);
+              macro_expander_destroy (subme);
+              continue;
             }
+
+          macro_expander_destroy (subme);
         }
 
-      /* We have a token in 'token'. */
-      printf ("add token %d %s\n", token.type, token_type_to_name (token.type));
-      if (is_scan_type (token.type))
+      if (token->type != T_MACRO_ID)
         {
-          /* XXX report error if it's not SCAN_SKIP */
+          tokens_add (exp, token);
+          continue;
+        }
+
+      if (ss_equals_case (token->string, ss_cstr ("!onexpand")))
+        *expand = true;
+      else if (ss_equals_case (token->string, ss_cstr ("!offexpand")))
+        *expand = false;
+      else if (ss_equals_case (token->string, ss_cstr ("!length")))
+        {
+          if (i + 1 >= tokens->n || tokens->tokens[i + 1].type != T_LPAREN)
+            {
+              printf ("`(' expected following !LENGTH'\n");
+              continue;
+            }
+
+          int n_parens = 1;
+          size_t j;
+          for (j = i + 2; n_parens && j < tokens->n; j++)
+            if (tokens->tokens[j].type == T_LPAREN)
+              n_parens++;
+            else if (tokens->tokens[j].type == T_RPAREN)
+              n_parens--;
+          if (n_parens)
+            {
+              printf ("Unbalanced parentheses in !LENGTH argument.\n");
+              continue;
+            }
+
+          size_t lparen_idx = i + 1;
+          size_t rparen_idx = j - 1;
+          const struct tokens unexpanded_args = {
+            .tokens = &tokens->tokens[lparen_idx + 1],
+            .n = rparen_idx - (lparen_idx + 1),
+          };
+          struct tokens args = { .n = 0 };
+          macro_expand (&unexpanded_args, nesting_countdown, macros,
+                        me, expand, &args);
+
+          if (args.n != 1)
+            {
+              tokens_uninit (&args);
+              printf ("!LENGTH argument must be a single token (not %zu)\n", args.n);
+              continue;
+            }
+
+          char *s = token_to_string (&args.tokens[0]);
+          struct token t = { .type = T_POS_NUM, .number = strlen (s) };
+          tokens_add (exp, &t);
+          free (s);
+
+          tokens_uninit (&args);
+
+          i = rparen_idx;
         }
       else
-        tokens_add (exp, &token);
-      token_destroy (&token);
+        tokens_add (exp, token);
     }
 }
+
+
+void
+macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
+{
+  for (size_t i = 0; i < me->macro->n_params; i++)
+    {
+      printf ("%s:\n", me->macro->params[i].name);
+      tokens_print (me->args[i], stdout);
+    }
+
+  bool expand = true;
+  macro_expand (&me->macro->body_tokens, settings_get_mnest (),
+                me->macros, me, &expand, exp);
+
+  printf ("expansion:\n");
+  tokens_print (exp, stdout);
+}
+