mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-07 01:42:04 +00:00
[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:
parent
f7ed55d317
commit
e8da9924c0
8 changed files with 265 additions and 98 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
204
tools/qfcc/source/expr_type.c
Normal file
204
tools/qfcc/source/expr_type.c
Normal 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 (¶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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue