}
}
+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,
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);
}