From 58bf1ee64e64cc6fa37c5e29cf6025c12ac0700c Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 10 Aug 2024 20:04:58 +0900 Subject: [PATCH] [qfcc] Support generic scope blocks That is, `@generic(...) { ... };`, which is handy for bulk declarations (such as for glsl). This proved to be a lot harder than expected, I suspect handling of specifiers needs a lot of work. --- tools/qfcc/include/specifier.h | 2 + tools/qfcc/source/function.c | 8 ++-- tools/qfcc/source/qc-parse.y | 67 +++++++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/tools/qfcc/include/specifier.h b/tools/qfcc/include/specifier.h index 0080b0f57..86b23439a 100644 --- a/tools/qfcc/include/specifier.h +++ b/tools/qfcc/include/specifier.h @@ -59,6 +59,7 @@ typedef struct specifier_s { struct { bool multi_type:1; bool multi_store:1; + bool multi_generic:1; bool is_signed:1; bool is_unsigned:1; bool is_short:1; @@ -66,6 +67,7 @@ typedef struct specifier_s { bool is_typedef:1; bool is_overload:1; bool is_generic:1; + bool is_generic_block:1; bool is_function:1;//FIXME do proper void(*)() -> ev_func bool nosave:1; bool no_va_list:1; diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index a1ac7a818..1a55d2cfe 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -105,8 +105,8 @@ static void check_generic_param (genparam_t *param, genfunc_t *genfunc) { if (param->gentype < 0 || param->gentype >= genfunc->num_types) { - internal_error (0, "invalid type index %s for %s", - param->name, genfunc->name); + internal_error (0, "invalid type index %d on %s for %s", + param->gentype, param->name, genfunc->name); } } @@ -295,7 +295,7 @@ parse_generic_function (const char *name, specifier_t spec) for (auto p = &ret_param; p; p = p->next) { num_params++; } - auto generic_tab = current_symtab; + auto generic_tab = spec.symtab; for (auto s = generic_tab->symbols; s; s = s->next) { bool found = false; for (auto q = &ret_param; q; q = q->next) { @@ -309,7 +309,7 @@ parse_generic_function (const char *name, specifier_t spec) break; } } - if (!found) { + if (!spec.is_generic_block && !found) { warning (0, "generic parameter %s not used", s->name); } } diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 5aac3f302..c0432e80d 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -243,7 +243,25 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); static switch_block_t *switch_block; static const expr_t *break_label; static const expr_t *continue_label; -static bool generic_scope; +static bool generic_scope, generic_block; +static symtab_t *generic_symtab; + +static void +end_generic_scope (void) +{ + generic_scope = false; + generic_symtab = nullptr; +} + +static void +restore_storage (specifier_t st) +{ + if (generic_block && !st.is_generic) { + end_generic_scope (); + } + generic_block = st.is_generic; + current_storage = st.storage; +} static specifier_t type_spec (const type_t *type) @@ -291,6 +309,7 @@ generic_spec (void) .symtab = new_symtab (current_symtab, stab_local), .is_generic = true, }; + generic_symtab = spec.symtab; return spec; } @@ -348,6 +367,12 @@ spec_merge (specifier_t spec, specifier_t new) spec.storage = new.storage; spec.is_typedef = new.is_typedef; } + if (new.is_generic) { + if (spec.is_generic && !spec.multi_generic) { + error (0, "multiple @generic in declaration"); + spec.multi_generic = true; + } + } if ((new.is_unsigned && spec.is_signed) || (new.is_signed && spec.is_unsigned)) { if (!spec.multi_type) { @@ -398,6 +423,9 @@ function_spec (specifier_t spec, param_t *params) spec.sym->params = params; spec.sym->type = append_type (spec.sym->type, parse_params (0, params)); spec.is_function = true; //FIXME do proper void(*)() -> ev_func + spec.is_generic = generic_scope; + spec.is_generic_block = generic_block; + spec.symtab = generic_symtab; return spec; } @@ -581,13 +609,14 @@ program external_def_list : /* empty */ { - current_symtab = pr.symtab; + if (!generic_block) { + current_symtab = pr.symtab; + } } | external_def_list external_def { - if (generic_scope) { - generic_scope = false; - current_symtab = current_symtab->parent; + if (generic_scope && !generic_block) { + end_generic_scope (); } } | external_def_list obj_def @@ -599,10 +628,11 @@ external_def | storage_class '{' save_storage { current_storage = $1.storage; + generic_block = generic_scope; } external_def_list '}' ';' { - current_storage = $3.storage; + restore_storage ($3); } ; @@ -773,7 +803,7 @@ qc_code_func build_code_function ($1, $3, $6); current_symtab = $5.symtab; current_func = $5.function; - current_storage = $4.storage; + restore_storage ($4); } ; @@ -1054,6 +1084,7 @@ save_storage : /* emtpy */ { $$.storage = current_storage; + $$.is_generic = generic_block; } ; @@ -1080,7 +1111,7 @@ function_body build_code_function ($2, $1, $5); current_symtab = $4.symtab; current_func = $4.function; - current_storage = $3.storage; + restore_storage ($3); } | '=' '#' expr ';' { @@ -1100,13 +1131,14 @@ storage_class | GENERIC '(' { $$ = generic_spec (); } generic_param_list ')' { + $$ = $3; + if (generic_scope) { error (0, "multiple @generic in declaration"); + $$.multi_generic = true; } else { generic_scope = true; - current_symtab = $3.symtab; } - $$ = $3; } | ATTRIBUTE '(' attribute_list ')' { @@ -2438,7 +2470,7 @@ methoddef build_code_function ($4, $3, $7); current_symtab = $6.symtab; current_func = $6.function; - current_storage = $5.storage; + restore_storage ($5); } | ci methoddecl '=' '#' const ';' { @@ -2858,7 +2890,6 @@ qc_keyword_or_id (QC_YYSTYPE *lval, const char *token) static hashtab_t *rua_keyword_tab; keyword_t *keyword = 0; - symbol_t *sym; if (!keyword_tab) { size_t i; @@ -2908,9 +2939,17 @@ qc_keyword_or_id (QC_YYSTYPE *lval, const char *token) if (token[0] == '@') { return '@'; } - sym = symtab_lookup (current_symtab, token); - if (!sym) + + symbol_t *sym = nullptr; + if (generic_symtab) { + sym = symtab_lookup (generic_symtab, token); + } + if (!sym) { + sym = symtab_lookup (current_symtab, token); + } + if (!sym) { sym = new_symbol (token); + } lval->symbol = sym; if (sym->sy_type == sy_type) { lval->spec = (specifier_t) {