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