From fcb970c24584cf72f46676ff1cb2e08013951392 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 2 May 2021 23:10:47 -0700 Subject: [PATCH] !LENGTH and !BLANKS work --- src/language/lexer/macro.c | 200 +++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 43 deletions(-) diff --git a/src/language/lexer/macro.c b/src/language/lexer/macro.c index 8633de9857..7726e4c3ec 100644 --- a/src/language/lexer/macro.c +++ b/src/language/lexer/macro.c @@ -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); } -- 2.30.2