From e8da9924c0cb8296107f53317abf7564860b5ccc Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 26 Apr 2024 15:20:13 +0900 Subject: [PATCH] [qfcc] Rework type expression handling The expression grammar has been tidied up and some basic checks are made of parameters to the type functions. Also, type parameters are looked up so parsing now works properly. However, the type parameters are not used correctly, so function generation doesn't work. --- tools/qfcc/include/expr.h | 4 + tools/qfcc/include/specifier.h | 2 + tools/qfcc/include/symtab.h | 3 +- tools/qfcc/source/Makemodule.am | 1 + tools/qfcc/source/expr_assign.c | 1 + tools/qfcc/source/expr_type.c | 204 ++++++++++++++++++++++++++++++++ tools/qfcc/source/qc-parse.y | 147 ++++++++--------------- tools/qfcc/source/symtab.c | 1 + 8 files changed, 265 insertions(+), 98 deletions(-) create mode 100644 tools/qfcc/source/expr_type.c 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",