diff --git a/tools/qfcc/include/rua-lang.h b/tools/qfcc/include/rua-lang.h index 31c0f8c4c..6842be9b9 100644 --- a/tools/qfcc/include/rua-lang.h +++ b/tools/qfcc/include/rua-lang.h @@ -53,6 +53,7 @@ typedef struct rua_macro_s { rua_expr_t *tokens; rua_expr_t **tail; int num_tokens; + int num_params; } rua_macro_t; typedef struct rua_tok_s { @@ -78,6 +79,7 @@ rua_macro_t *rua_end_params (rua_macro_t *macro, void *scanner); rua_macro_t *rua_macro_append (rua_macro_t *macro, rua_tok_t *token, void *scanner); void rua_macro_finish (rua_macro_t *macro, void *scanner); +rua_macro_t *rua_macro_arg (rua_macro_t *arg, void *scanner); void rua_start_text (void *scanner); void rua_start_expr (void *scanner); void rua_expand_on (void *scanner); diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index aa2986275..e6156617a 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -75,7 +75,7 @@ typedef struct symbol_s { struct param_s *params; ///< the parameters if a function unsigned no_auto_init; ///< skip for non-designated initializers union { - int offset; ///< sy_var (in a struct/union) + int offset; ///< sy_var (in a struct/union/macro) struct def_s *def; ///< sy_var struct ex_value_s *value; ///< sy_const const struct expr_s *expr; ///< sy_expr diff --git a/tools/qfcc/source/pre-parse.y b/tools/qfcc/source/pre-parse.y index 86f95e25b..5d753f128 100644 --- a/tools/qfcc/source/pre-parse.y +++ b/tools/qfcc/source/pre-parse.y @@ -130,7 +130,7 @@ parse_error (void *scanner) %token DEFINED EOD %token CONCAT ARGS -%type params body +%type params body arg arg_list %type text text_text %type unary_expr expr id defined defined_id @@ -143,7 +143,12 @@ parse_error (void *scanner) start : directive_list - | ARGS args ')' { YYACCEPT; } + | ARGS + { + auto arg = rua_start_macro (0, false, scanner); + $$ = rua_macro_arg (arg, scanner); + } + args ')' { YYACCEPT; } ; directive_list @@ -222,16 +227,23 @@ params ; args: arg_list - | args ',' arg_list + | args ',' + { + auto arg = rua_start_macro (0, false, scanner); + $$ = rua_macro_arg (arg, scanner); + } + arg_list ; arg_list - : /* emtpy */ - | arg_list arg + : /* emtpy */ { $$ = $0; } + | arg_list arg { $$ = $2; } ; -arg : '(' args ')' - | TOKEN +arg : '(' { $$ = rua_macro_append ($0, yyvsp, scanner); } + args { $$ = rua_macro_append ($0, yyvsp, scanner); } + ')' { $$ = rua_macro_append ($0, yyvsp, scanner); } + | TOKEN { $$ = rua_macro_append ($0, yyvsp, scanner); } ; id : ID { $$ = 0; } diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index 7190127a7..83f719514 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -95,6 +95,7 @@ typedef struct { typedef struct DARRAY_TYPE (rua_cond_t) rua_cond_stack_t; typedef struct DARRAY_TYPE (rua_expr_t) rua_expr_stack_t; +typedef struct DARRAY_TYPE (rua_macro_t *) rua_macro_list_t; typedef struct rua_extra_s { int start_state; @@ -106,6 +107,7 @@ typedef struct rua_extra_s { pre_yypstate *pre_state; rua_cond_stack_t cond_stack; rua_expr_stack_t expr_stack; + rua_macro_list_t arg_list; symtab_t *macro_tab; yyscan_t subscanner; } rua_extra_t; @@ -1096,16 +1098,35 @@ static void expand_macro (rua_extra_t *extra, rua_macro_t *macro) { int num_tokens = macro->num_tokens; + for (auto t = macro->tokens; t; t = t->next) { + if (t->token == PRE_ID) { + auto sym = symtab_lookup (macro->params, t->text); + if (sym) { + num_tokens += extra->arg_list.a[sym->s.offset]->num_tokens - 1; + } + } + } int base = extra->expr_stack.size; DARRAY_OPEN_AT (&extra->expr_stack, base, num_tokens); int index = num_tokens; for (auto t = macro->tokens; t; t = t->next) { - extra->expr_stack.a[base + --index] = *t; + auto e = t; + if (t->token == PRE_ID) { + auto sym = symtab_lookup (macro->params, t->text); + int param = sym->s.offset; + for (e = extra->arg_list.a[param]->tokens; e; e = e->next) { + extra->expr_stack.a[base + --index] = *e; + } + } + if (e) { + extra->expr_stack.a[base + --index] = *e; + } } if (index) { internal_error (0, "taniwha can't count: %d", index); } + for (index = num_tokens; index-- > 0;) { auto e = &extra->expr_stack.a[base + index]; if (e->token == '#') { @@ -1255,6 +1276,10 @@ parse_args (rua_extra_t *extra, rua_loc_t *location, yyscan_t scanner) auto state = pre_yypstate_new (); int token = PRE_ARGS; rua_tok_t tok = { .location = *location, }; + for (auto i = extra->arg_list.size; i-- > 0; ) { + free (extra->arg_list.a[i]); + } + extra->arg_list.size = 0; do { if (token != PRE_ARGS) { token = yylex (&tok, &tok.location, scanner); @@ -1286,7 +1311,8 @@ qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t scanner) sym = symtab_lookup (extra->macro_tab, tok->text); } if (sym) { - if (sym->s.macro->params) { + auto macro = sym->s.macro; + if (macro->params) { rua_tok_t ttok = { .location = tok->location, }; int ttoken; bool saw_space = false; @@ -1311,10 +1337,20 @@ qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t scanner) sym = 0; return -1; } + int num_args = extra->arg_list.size; + if (num_args == 1 && !extra->arg_list.a[0]->num_tokens) { + num_args = 0; + } + if (num_args != macro->num_params) { + error (0, "macro \"%s\" passed %d arguments," + " but takes just %d", + macro->name, num_args, macro->num_params); + sym = 0; + } } } if (sym) { - expand_macro (extra, sym->s.macro); + expand_macro (extra, macro); return YYPUSH_MORE; } } @@ -1352,6 +1388,7 @@ qc_yyparse (FILE *in) .pre_state = pre_yypstate_new (), .cond_stack = DARRAY_STATIC_INIT (8), .expr_stack = DARRAY_STATIC_INIT (32), + .arg_list = DARRAY_STATIC_INIT (8), .macro_tab = new_symtab (0, stab_global), }; @@ -1382,6 +1419,7 @@ qc_yyparse (FILE *in) pre_yypstate_delete (extra.pre_state); free (extra.cond_stack.a); free (extra.expr_stack.a); + free (extra.arg_list.a); return status; } @@ -1392,11 +1430,13 @@ rua_start_macro (const char *name, bool params, void *scanner) extra->params = params; extra->recording = true; - int len = strlen (name); - if (name[len - 1] == '(') { - name = save_substring (name, len - 1); - } else { - name = save_string (name); + if (name) { + int len = strlen (name); + if (name[len - 1] == '(') { + name = save_substring (name, len - 1); + } else { + name = save_string (name); + } } yy_pop_state (scanner); @@ -1462,6 +1502,8 @@ rua_macro_param (rua_macro_t *macro, rua_tok_t *token, void *scanner) return macro; } auto sym = new_symbol (token->text); + sym->sy_type = sy_var; + sym->s.offset = macro->num_params++; symtab_addsymbol (macro->params, sym); return macro; } @@ -1517,6 +1559,14 @@ rua_macro_finish (rua_macro_t *macro, void *scanner) symtab_addsymbol (macro_tab, sym); } +rua_macro_t * +rua_macro_arg (rua_macro_t *arg, void *scanner) +{ + auto extra = qc_yyget_extra (scanner); + DARRAY_APPEND (&extra->arg_list, arg); + return arg; +} + void rua_start_text (void *scanner) {