diff --git a/tools/qfcc/include/specifier.h b/tools/qfcc/include/specifier.h index 7927643df..66f83db2c 100644 --- a/tools/qfcc/include/specifier.h +++ b/tools/qfcc/include/specifier.h @@ -32,6 +32,8 @@ #define __specifier_h typedef struct type_s type_t; +typedef struct symbol_s symbol_t; +typedef struct symtab_s symtab_t; /** Specify the storage class of a def. */ @@ -48,7 +50,8 @@ typedef enum storage_class_e { typedef struct specifier_s { const type_t *type; struct param_s *params; - struct symbol_s *sym; + symbol_t *sym; + symtab_t *symtab; storage_class_t storage; union { struct { diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index d998e62c8..5eecb2920 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -160,6 +160,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE %token PROTECTED PROTOCOL PUBLIC SELECTOR REFERENCE SELF THIS +%token GENERIC %token AT_FIELD AT_POINTER AT_ARRAY %token AT_BASE AT_WIDTH AT_VECTOR AT_ROWS AT_COLS AT_MATRIX %token AT_INT AT_UINT AT_BOOL AT_FLOAT @@ -176,6 +177,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %type param_declarator_nostarttypename %type absdecl absdecl1 direct_absdecl typename ptr_spec copy_spec %type qc_comma +%type generic_param %type type_param type_expr type_ref %type attribute_list attribute @@ -238,6 +240,7 @@ 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 specifier_t make_spec (const type_t *type, storage_class_t storage, int is_typedef, @@ -537,6 +540,12 @@ external_def_list current_symtab = pr.symtab; } | external_def_list external_def + { + if (generic_scope) { + generic_scope = false; + current_symtab = current_symtab->parent; + } + } | external_def_list obj_def ; @@ -1019,12 +1028,51 @@ storage_class | SYSTEM { $$ = make_spec (0, sc_system, 0, 0); } | TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); } | OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); } + | GENERIC '(' + { + auto spec = make_spec (0, current_storage, 0, 0); + spec.symtab = new_symtab (current_symtab, stab_local); + $$ = spec; + } + generic_param_list ')' + { + if (generic_scope) { + error (0, "multiple @generic in declaration"); + } else { + generic_scope = true; + current_symtab = $3.symtab; + } + $$ = make_spec (0, current_storage, 0, 0); + } | ATTRIBUTE '(' attribute_list ')' { $$ = parse_attributes ($3); } ; +generic_param_list + : generic_param + { + auto spec = $0; + symtab_addsymbol (spec.symtab, $1); + } + | generic_param_list ',' generic_param + { + auto spec = $0; + symtab_addsymbol (spec.symtab, $3); + } + ; + +generic_param + : NAME { $$ = $1; } + | NAME '=' generic_type { $$ = $1; } + ; + +generic_type + : type_expr + | '[' type_list ']' + ; + type_expr : AT_FIELD '(' type_param ')' { $$ = field_type ($3); } | AT_POINTER '(' type_param ')' { $$ = pointer_type ($3); } @@ -1079,10 +1127,16 @@ type_param | type_ref ; +type_list + : type_ref + | type_list ',' type_ref + ; + type_ref : TYPE_SPEC { $$ = $1.type; } | TYPE_NAME { $$ = $1.type; } | CLASS_NAME { $$ = $1->type; } + | NAME { internal_error (nullptr, "not implemented"); } ; attribute_list @@ -2654,6 +2708,7 @@ static keyword_t qf_keywords[] = { {"@dual", QC_DUAL, }, {"@undual", QC_UNDUAL, }, + {"@generic", QC_GENERIC, }, {"@field", QC_AT_FIELD, }, {"@pointer", QC_AT_POINTER, }, {"@array", QC_AT_ARRAY, },