diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index b7b21ae92..eaa193d5d 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -307,6 +307,8 @@ typedef struct { } ex_multivec_t; typedef struct { + int op; ///< type "function" + const expr_t *params; ///< if dynamic const type_t *type; } ex_type_t; @@ -848,6 +850,8 @@ expr_t *new_memset_expr (const expr_t *dst, const expr_t *val, const expr_t *count); expr_t *new_type_expr (const type_t *type); +const expr_t *type_function (int op, const expr_t *params); +symbol_t *type_parameter (symbol_t *sym, const expr_t *type); /** Convert a name to an expression of the appropriate type. diff --git a/tools/qfcc/include/specifier.h b/tools/qfcc/include/specifier.h index 66f83db2c..b317f9158 100644 --- a/tools/qfcc/include/specifier.h +++ b/tools/qfcc/include/specifier.h @@ -32,6 +32,7 @@ #define __specifier_h typedef struct type_s type_t; +typedef struct expr_s expr_t; typedef struct symbol_s symbol_t; typedef struct symtab_s symtab_t; @@ -49,6 +50,7 @@ typedef enum storage_class_e { typedef struct specifier_s { const type_t *type; + const expr_t *type_expr; struct param_s *params; symbol_t *sym; symtab_t *symtab; diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index 025971a9d..b091a5be4 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -53,6 +53,7 @@ typedef enum { sy_var, ///< symbol refers to a variable sy_const, ///< symbol refers to a constant sy_type, ///< symbol refers to a type + sy_type_param, ///< symbol refers to a generic type parameter sy_expr, ///< symbol refers to an expression sy_func, ///< symbol refers to a function sy_class, ///< symbol refers to a class @@ -78,7 +79,7 @@ typedef struct symbol_s { 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 + const struct expr_s *expr; ///< sy_expr/sy_type_param struct function_s *func; ///< sy_func symconv_t convert; ///< sy_convert struct rua_macro_s *macro; ///< sy_macro diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index b273e093e..57d2acedf 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -35,6 +35,7 @@ qfcc_SOURCES = \ tools/qfcc/source/expr_dag.c \ tools/qfcc/source/expr_obj.c \ tools/qfcc/source/expr_optimize.c \ + tools/qfcc/source/expr_type.c \ tools/qfcc/source/expr_vector.c \ tools/qfcc/source/evaluate.c \ tools/qfcc/source/flow.c \ diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index 9563ca11f..68febbc14 100644 --- a/tools/qfcc/source/expr_assign.c +++ b/tools/qfcc/source/expr_assign.c @@ -96,6 +96,7 @@ is_lvalue (const expr_t *expr) case sy_const: break; case sy_type: + case sy_type_param: break; case sy_expr: break; diff --git a/tools/qfcc/source/expr_type.c b/tools/qfcc/source/expr_type.c new file mode 100644 index 000000000..91369958a --- /dev/null +++ b/tools/qfcc/source/expr_type.c @@ -0,0 +1,204 @@ +/* + expr_type.c + + type expressions + + Copyright (C) 2024 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/rua-lang.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" + +typedef struct { + const char *name; + const char *(*check_params) (int count, const expr_t **args); +} type_func_t; + +static bool +check_type (const expr_t *arg) +{ + if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param) + && arg->type != ex_type) { + return false; + } + return true; +} + +static bool +check_int (const expr_t *arg) +{ + if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param) + && arg->type != ex_type) { + return false; + } + return true; +} + +static const char * +single_type (int count, const expr_t **args) +{ + if (count < 1) { + return "too few arguments"; + } + if (count > 1) { + return "too many arguments"; + } + if (!check_type (args[0])) { + return "first parameter must be a type"; + } + return nullptr; +} + +static const char * +single_type_opt_int (int count, const expr_t **args) +{ + if (count < 1) { + return "too few arguments"; + } + if (count > 2) { + return "too many arguments"; + } + if (!check_type (args[0])) { + return "first parameter must be a type"; + } + if (count > 1 && !check_int (args[1])) { + return "second parameter must be int"; + } + return nullptr; +} + +static const char * +single_type_opt_int_pair (int count, const expr_t **args) +{ + if (count < 1) { + return "too few arguments"; + } + if (count > 3) { + return "too many arguments"; + } + if (count == 2) { + return "must have one or three arguments"; + } + if (!check_type (args[0])) { + return "first parameter must be a type"; + } + if (count > 1 && (!check_int (args[1]) || !check_int (args[2]))) { + return "second and third parameters must be int"; + } + return nullptr; +} + +static type_func_t type_funcs[] = { + [QC_AT_FIELD - QC_GENERIC] = { + .name = "@field", + .check_params = single_type, + }, + [QC_AT_POINTER - QC_GENERIC] = { + .name = "@pointer", + .check_params = single_type, + }, + [QC_AT_ARRAY - QC_GENERIC] = { + .name = "@array", + .check_params = single_type_opt_int, + }, + [QC_AT_BASE - QC_GENERIC] = { + .name = "@base", + .check_params = single_type, + }, + [QC_AT_WIDTH - QC_GENERIC] = { + .name = "@width", + .check_params = single_type, + }, + [QC_AT_VECTOR - QC_GENERIC] = { + .name = "@vector", + .check_params = single_type_opt_int, + }, + [QC_AT_ROWS - QC_GENERIC] = { + .name = "@rows", + .check_params = single_type, + }, + [QC_AT_COLS - QC_GENERIC] = { + .name = "@cols", + .check_params = single_type, + }, + [QC_AT_MATRIX - QC_GENERIC] = { + .name = "@matrix", + .check_params = single_type_opt_int_pair, + }, + [QC_AT_INT - QC_GENERIC] = { + .name = "@int", + .check_params = single_type, + }, + [QC_AT_UINT - QC_GENERIC] = { + .name = "@uint", + .check_params = single_type, + }, + [QC_AT_BOOL - QC_GENERIC] = { + .name = "@bool", + .check_params = single_type, + }, + [QC_AT_FLOAT - QC_GENERIC] = { + .name = "@float", + .check_params = single_type, + }, +}; + +const expr_t * +type_function (int op, const expr_t *params) +{ + int arg_count = list_count (¶ms->list); + const expr_t *args[arg_count]; + list_scatter (¶ms->list, args); + unsigned ind = op - QC_GENERIC; + if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0])) { + internal_error (params, "invalid type op: %d", op); + } + const char *msg = type_funcs[ind].check_params (arg_count, args); + if (msg) { + return error (params, "%s for %s", msg, type_funcs[ind].name); + } + auto te = new_expr (); + te->type = ex_type; + te->typ = (ex_type_t) { + .op = op, + .params = params, + }; + return te; +} + +symbol_t * +type_parameter (symbol_t *sym, const expr_t *type) +{ + if (sym->table) { + sym = new_symbol (sym->name); + } + sym->sy_type = sy_type_param; + sym->s.expr = type; + return sym; +} diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 4b44556c6..5843324e7 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -178,8 +178,10 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %type absdecl absdecl1 direct_absdecl typename ptr_spec copy_spec %type qc_comma %type generic_param -%type type_param type_expr type_ref generic_type -%type type_list +%type type_func type_op +%type type_function type_param type_ref +%type generic_type +%type type_list type_param_list %type attribute_list attribute @@ -914,7 +916,7 @@ typespec typespec_reserved : TYPE_SPEC - | type_expr { $$ = make_spec ($1->typ.type, 0, 0, 0); } + | type_function { $$ = make_spec ($1->typ.type, 0, 0, 0); } | algebra_specifier %prec LOW | algebra_specifier '.' attribute { @@ -1065,97 +1067,47 @@ generic_param_list ; generic_param - : NAME { $$ = $1; } - | NAME '=' generic_type { $$ = $1; } + : NAME { $$ = type_parameter ($1, nullptr); } + | NAME '=' generic_type { $$ = type_parameter ($1, $3); } ; generic_type - : type_expr { $$ = $1; } + : type_function { $$ = $1; } | '[' type_list ']' { $$ = $2; } ; -type_expr - : AT_FIELD '(' type_param ')' - { - $$ = new_type_expr (field_type ($3->typ.type)); - } - | AT_POINTER '(' type_param ')' - { - $$ = new_type_expr (pointer_type ($3->typ.type)); - } - | AT_ARRAY '(' type_param ')' - { - $$ = new_type_expr (array_type ($3->typ.type, 0)); - } - | AT_ARRAY '(' type_param ',' expr ')' - { - auto count = $5; - auto type = $3->typ.type; - if (!is_int_val (count)) { - error (count, "count must be an int constant"); - } else { - type = vector_type (type, expr_int (count)); - } - $$ = new_type_expr (type); - } - | AT_BASE '(' type_param ')' - { - $$ = new_type_expr (base_type ($3->typ.type)); - } - | AT_VECTOR '(' type_param ')' - { - $$ = new_type_expr (vector_type ($3->typ.type, 0)); - } - | AT_VECTOR '(' type_param ',' expr ')' - { - auto width = $5; - auto type = $3->typ.type; - if (!is_int_val (width)) { - error (width, "width must be an int constant"); - } else { - type = vector_type (type, expr_int (width)); - } - $$ = new_type_expr (type); - } - | AT_MATRIX '(' type_param ')' - { - $$ = new_type_expr (matrix_type ($3->typ.type, 0, 0)); - } - | AT_MATRIX '(' type_param ',' expr ',' expr ')' - { - auto cols = $5; - auto rows = $7; - auto type = $3->typ.type; - if (!is_int_val (cols)) { - error (cols, "cols must be an int constant"); - } else if (!is_int_val (rows)) { - error (rows, "rows must be an int constant"); - } else { - type = matrix_type (type, expr_int (cols), expr_int (rows)); - } - $$ = new_type_expr (type); - } - | AT_INT '(' type_param ')' - { - $$ = new_type_expr (int_type ($3->typ.type)); - } - | AT_UINT '(' type_param ')' - { - $$ = new_type_expr (uint_type ($3->typ.type)); - } - | AT_BOOL '(' type_param ')' - { - $$ = new_type_expr (bool_type ($3->typ.type)); - } - | AT_FLOAT '(' type_param ')' - { - $$ = new_type_expr (float_type ($3->typ.type)); - } +type_function + : type_func '(' type_param_list ')' { $$ = type_function ($1, $3); } + ; + +type_func + : AT_FIELD { $$ = QC_AT_FIELD; } + | AT_POINTER { $$ = QC_AT_POINTER; } + | AT_ARRAY { $$ = QC_AT_ARRAY; } + | AT_BASE { $$ = QC_AT_BASE; } + | AT_VECTOR { $$ = QC_AT_VECTOR; } + | AT_MATRIX { $$ = QC_AT_MATRIX; } + | AT_INT { $$ = QC_AT_INT; } + | AT_UINT { $$ = QC_AT_UINT; } + | AT_BOOL { $$ = QC_AT_BOOL; } + | AT_FLOAT { $$ = QC_AT_FLOAT; } + ; + +type_op + : AT_WIDTH { $$ = QC_AT_WIDTH; } + | AT_ROWS { $$ = QC_AT_ROWS; } + | AT_COLS { $$ = QC_AT_COLS; } + ; + +type_param_list + : type_param { $$ = new_list_expr ($1); } + | type_param_list ',' type_param { $$ = expr_append_expr ($1, $3); } ; type_param - : type_expr + : type_function | type_ref + | expr ; type_list @@ -1167,7 +1119,6 @@ type_ref : TYPE_SPEC { $$ = new_type_expr ($1.type); } | TYPE_NAME { $$ = new_type_expr ($1.type); } | CLASS_NAME { $$ = new_type_expr ($1->type); } - | NAME { $$ = new_symbol_expr ($1); } ; attribute_list @@ -1890,18 +1841,7 @@ unary_expr { $$ = sizeof_expr (0, $3.type); } - | AT_WIDTH '(' type_param ')' - { - $$ = new_int_expr (type_width ($3->typ.type), false); - } - | AT_ROWS '(' type_param ')' - { - $$ = new_int_expr (type_rows ($3->typ.type), false); - } - | AT_COLS '(' type_param ')' - { - $$ = new_int_expr (type_cols ($3->typ.type), false); - } + | type_op '(' type_param_list ')' { $$ = type_function ($1, $3); } | vector_expr { $$ = new_vector_list ($1); } | obj_expr { $$ = $1; } ; @@ -2795,6 +2735,13 @@ qc_process_keyword (QC_YYSTYPE *lval, keyword_t *keyword, const char *token) .sym = sym, }; return QC_OBJECT_NAME; + } else if (sym->sy_type == sy_type_param) { + // id has been redeclared via a generic type param + lval->spec = (specifier_t) { + .type_expr = sym->s.expr, + .sym = sym, + }; + return QC_TYPE_NAME; } else if (sym->sy_type == sy_type) { // id has been redeclared via a typedef lval->spec = (specifier_t) { @@ -2881,6 +2828,12 @@ qc_keyword_or_id (QC_YYSTYPE *lval, const char *token) .sym = sym, }; return QC_TYPE_NAME; + } else if (sym->sy_type == sy_type_param) { + lval->spec = (specifier_t) { + .type_expr = sym->s.expr, + .sym = sym, + }; + return QC_TYPE_NAME; } if (sym->sy_type == sy_class) return QC_CLASS_NAME; diff --git a/tools/qfcc/source/symtab.c b/tools/qfcc/source/symtab.c index 884b3a73e..600eaae7c 100644 --- a/tools/qfcc/source/symtab.c +++ b/tools/qfcc/source/symtab.c @@ -57,6 +57,7 @@ static const char * const sy_type_names[] = { "sy_var", "sy_const", "sy_type", + "sy_type_param", "sy_expr", "sy_func", "sy_class",