!LENGTH and !BLANKS work
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 3 May 2021 06:10:47 +0000 (23:10 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 30 May 2021 20:31:45 +0000 (13:31 -0700)
src/language/lexer/macro.c

index 8633de9857608b8babbfd1cf28f38a8ce49d5f99..7726e4c3ecd41e86ca08cb3cbc91c16c27f1c34b 100644 (file)
@@ -437,6 +437,113 @@ macro_expander_add (struct macro_expander *me, const struct token *token)
     }
 }
 
+struct parse_macro_function_ctx
+  {
+    const struct tokens *tokens;
+    size_t *idx;
+    struct tokens *args;
+    int nesting_countdown;
+    const struct macro_set *macros;
+    const struct macro_expander *me;
+    bool *expand;
+  };
+
+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);
+
+static bool
+parse_macro_function (struct parse_macro_function_ctx *ctx,
+                      struct substring function,
+                      int min_args, int max_args)
+{
+  const struct token *tokens = ctx->tokens->tokens;
+  size_t n_tokens = ctx->tokens->n;
+
+  if (!ss_equals_case (tokens[0].string, function))
+    return false;
+
+  size_t lparen_idx = *ctx->idx + 1;
+  if (lparen_idx >= n_tokens || tokens[lparen_idx].type != T_LPAREN)
+    {
+      printf ("`(' expected following %s'\n", function.string);
+      return false;
+    }
+
+  *ctx->args = (struct tokens) { .n = 0 };
+
+  size_t i = lparen_idx + 1;
+  for (size_t j = i; ; j++)
+    {
+      if (j >= n_tokens)
+        {
+          printf ("Missing closing parenthesis in arguments to %s.\n",
+                  function.string);
+          goto error;
+        }
+
+      int type = tokens[j].type;
+      if (type == T_LPAREN)
+        {
+          int paren_nesting_level = 1;
+          do
+            {
+              j++;
+              if (j >= n_tokens)
+                {
+                  printf ("Missing closing parenthesis in argument %zu to %s.\n",
+                          ctx->args->n + 1, function.string);
+                  goto error;
+                }
+              if (tokens[j].type == T_LPAREN)
+                paren_nesting_level++;
+              else if (tokens[j].type == T_RPAREN)
+                paren_nesting_level--;
+            }
+          while (paren_nesting_level != 0);
+        }
+      else if (type == T_RPAREN || type == T_COMMA)
+        {
+          const struct tokens unexpanded_arg = {
+            .tokens = CONST_CAST (struct token *, &tokens[i]),
+            .n = j - i,
+          };
+          struct tokens expanded_arg = { .n = 0 };
+          macro_expand (&unexpanded_arg, ctx->nesting_countdown, ctx->macros,
+                        ctx->me, ctx->expand, &expanded_arg);
+
+          if (expanded_arg.n != 1)
+            {
+              printf ("argument %zu to %s must be a single token "
+                      "(not %zu tokens)\n", ctx->args->n + 1, function.string,
+                      expanded_arg.n);
+              tokens_uninit (&expanded_arg);
+              goto error;
+            }
+
+          tokens_add (ctx->args, &expanded_arg.tokens[0]);
+          tokens_uninit (&expanded_arg);
+
+          i = j + 1;
+          if (type == T_RPAREN)
+            break;
+        }
+    }
+
+  if (ctx->args->n < min_args || ctx->args->n > max_args)
+    {
+      printf ("Wrong number of argument to %s.\n", function.string);
+      goto error;
+    }
+  *ctx->idx = i;
+  return true;
+
+error:
+  tokens_uninit (ctx->args);
+  return false;
+}
+
 static void
 macro_expand (const struct tokens *tokens, int nesting_countdown,
               const struct macro_set *macros, const struct macro_expander *me,
@@ -499,57 +606,64 @@ macro_expand (const struct tokens *tokens, int nesting_countdown,
           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 0
+      struct macro_function
+        {
+          const char *name;
+          int min_args;
+          int max_args;
+        };
+      static const struct macro_function functions[] = {
+        { "!length", 1, 1 },
+        { "!concat", 1, INT_MAX },
+        { "!substr", 2, 3 },
+        { "!index", 2, 2 },
+        { "!head", 1, 1 },
+        { "!tail", 1, 1 },
+        { "!quote", 1, 1 },
+        { "!unquote", 1, 1 },
+        { "!upcase", 1, 1 },
+        { "!blanks", 1, 1 },
+        { "!eval", 1, 1 },
+      };
+#endif
+      struct tokens args;
+      struct parse_macro_function_ctx ctx = {
+        .tokens = tokens,
+        .idx = &i,
+        .args = &args,
+        .nesting_countdown = nesting_countdown,
+        .macros = macros,
+        .me = me,
+        .expand = expand,
+      };
+      if (parse_macro_function (&ctx, ss_cstr ("!length"), 1, 1))
         {
-          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 if (parse_macro_function (&ctx, ss_cstr ("!blanks"), 1, 1))
+        {
+          if (args.tokens[0].type != T_POS_NUM)
+            printf ("argument to !BLANKS must be positive integer\n");
+          else
+            {
+              struct string s = DS_EMPTY_INITIALIZER;
+              ds_put_byte_multiple (&s, ' ', args.tokens[0].number);
+              struct token t = { .type = T_ID, .string = s.ss };
+              tokens_add (exp, &t);
+              ds_destroy (&s);
+            }
+          tokens_uninit (&args);
+        }
+      else if (ss_equals_case (token->string, ss_cstr ("!onexpand")))
+        *expand = true;
+      else if (ss_equals_case (token->string, ss_cstr ("!offexpand")))
+        *expand = false;
       else
         tokens_add (exp, token);
     }