[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.
This commit is contained in:
Bill Currie 2024-04-26 15:20:13 +09:00
parent f7ed55d317
commit e8da9924c0
8 changed files with 265 additions and 98 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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 \

View file

@ -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;

View file

@ -0,0 +1,204 @@
/*
expr_type.c
type expressions
Copyright (C) 2024 Bill Currie <bill@taniwha.org>
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 <string.h>
#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 (&params->list);
const expr_t *args[arg_count];
list_scatter (&params->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;
}

View file

@ -178,8 +178,10 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
%type <spec> absdecl absdecl1 direct_absdecl typename ptr_spec copy_spec
%type <spec> qc_comma
%type <symbol> generic_param
%type <expr> type_param type_expr type_ref generic_type
%type <mut_expr> type_list
%type <op> type_func type_op
%type <expr> type_function type_param type_ref
%type <expr> generic_type
%type <mut_expr> type_list type_param_list
%type <attribute> 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;

View file

@ -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",