mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 13:11:00 +00:00
[qfcc] Support static type expressions in declarations
This allows types in declarations to be based on other types: int foo[3]; @vector(float,sizeof(foo)) bar;
This commit is contained in:
parent
fed1bce12a
commit
aff70aa243
3 changed files with 276 additions and 15 deletions
|
@ -857,6 +857,8 @@ expr_t *new_memset_expr (const expr_t *dst, const expr_t *val,
|
||||||
expr_t *new_type_expr (const type_t *type);
|
expr_t *new_type_expr (const type_t *type);
|
||||||
const expr_t *type_function (int op, const expr_t *params);
|
const expr_t *type_function (int op, const expr_t *params);
|
||||||
symbol_t *type_parameter (symbol_t *sym, const expr_t *type);
|
symbol_t *type_parameter (symbol_t *sym, const expr_t *type);
|
||||||
|
const type_t *resolve_type (const expr_t *te);
|
||||||
|
const expr_t *evaluate_type (const expr_t *te);
|
||||||
|
|
||||||
/** Convert a name to an expression of the appropriate type.
|
/** Convert a name to an expression of the appropriate type.
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,13 @@
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *(*check_params) (int count, const expr_t **args);
|
const char *(*check_params) (int arg_count, const expr_t **args);
|
||||||
|
const type_t *(*resolve) (int arg_count, const expr_t **args);
|
||||||
|
const expr_t *(*evaluate) (int arg_count, const expr_t **args);
|
||||||
} type_func_t;
|
} type_func_t;
|
||||||
|
|
||||||
|
static type_func_t type_funcs[];
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
check_type (const expr_t *arg)
|
check_type (const expr_t *arg)
|
||||||
{
|
{
|
||||||
|
@ -50,23 +54,37 @@ check_type (const expr_t *arg)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool __attribute__((pure))
|
||||||
check_int (const expr_t *arg)
|
check_int (const expr_t *arg)
|
||||||
{
|
{
|
||||||
|
if (arg->type == ex_expr || arg->type == ex_uexpr) {
|
||||||
|
if (!is_int (arg->expr.type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return check_int (arg->expr.e1)
|
||||||
|
&& arg->type == ex_expr && check_int (arg->expr.e2);
|
||||||
|
}
|
||||||
|
if (is_integral_val (arg)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param)
|
if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param)
|
||||||
&& arg->type != ex_type) {
|
&& arg->type != ex_type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
int op = arg->symbol->s.expr->typ.op;
|
||||||
|
if (op != QC_AT_WIDTH && op != QC_AT_ROWS && op != QC_AT_COLS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
single_type (int count, const expr_t **args)
|
single_type (int arg_count, const expr_t **args)
|
||||||
{
|
{
|
||||||
if (count < 1) {
|
if (arg_count < 1) {
|
||||||
return "too few arguments";
|
return "too few arguments";
|
||||||
}
|
}
|
||||||
if (count > 1) {
|
if (arg_count > 1) {
|
||||||
return "too many arguments";
|
return "too many arguments";
|
||||||
}
|
}
|
||||||
if (!check_type (args[0])) {
|
if (!check_type (args[0])) {
|
||||||
|
@ -76,96 +94,288 @@ single_type (int count, const expr_t **args)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
single_type_opt_int (int count, const expr_t **args)
|
single_type_opt_int (int arg_count, const expr_t **args)
|
||||||
{
|
{
|
||||||
if (count < 1) {
|
if (arg_count < 1) {
|
||||||
return "too few arguments";
|
return "too few arguments";
|
||||||
}
|
}
|
||||||
if (count > 2) {
|
if (arg_count > 2) {
|
||||||
return "too many arguments";
|
return "too many arguments";
|
||||||
}
|
}
|
||||||
if (!check_type (args[0])) {
|
if (!check_type (args[0])) {
|
||||||
return "first parameter must be a type";
|
return "first parameter must be a type";
|
||||||
}
|
}
|
||||||
if (count > 1 && !check_int (args[1])) {
|
if (arg_count > 1 && !check_int (args[1])) {
|
||||||
return "second parameter must be int";
|
return "second parameter must be int";
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
single_type_opt_int_pair (int count, const expr_t **args)
|
single_type_opt_int_pair (int arg_count, const expr_t **args)
|
||||||
{
|
{
|
||||||
if (count < 1) {
|
if (arg_count < 1) {
|
||||||
return "too few arguments";
|
return "too few arguments";
|
||||||
}
|
}
|
||||||
if (count > 3) {
|
if (arg_count > 3) {
|
||||||
return "too many arguments";
|
return "too many arguments";
|
||||||
}
|
}
|
||||||
if (count == 2) {
|
if (arg_count == 2) {
|
||||||
return "must have one or three arguments";
|
return "must have one or three arguments";
|
||||||
}
|
}
|
||||||
if (!check_type (args[0])) {
|
if (!check_type (args[0])) {
|
||||||
return "first parameter must be a type";
|
return "first parameter must be a type";
|
||||||
}
|
}
|
||||||
if (count > 1 && (!check_int (args[1]) || !check_int (args[2]))) {
|
if (arg_count > 1 && (!check_int (args[1]) || !check_int (args[2]))) {
|
||||||
return "second and third parameters must be int";
|
return "second and third parameters must be int";
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const expr_t *
|
||||||
|
evaluate_int (const expr_t *expr)
|
||||||
|
{
|
||||||
|
if (expr->type == ex_expr || expr->type == ex_uexpr) {
|
||||||
|
auto e = new_expr ();
|
||||||
|
*e = *expr;
|
||||||
|
e->expr.e1 = evaluate_int (expr->expr.e1);
|
||||||
|
if (expr->type == ex_uexpr) {
|
||||||
|
e->expr.e2 = evaluate_int (expr->expr.e2);
|
||||||
|
}
|
||||||
|
return fold_constants (e);
|
||||||
|
}
|
||||||
|
if (is_integral_val (expr)) {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
int op = expr->typ.op;
|
||||||
|
int ind = op - QC_GENERIC;
|
||||||
|
auto type = expr->typ.type;
|
||||||
|
if (!type) {
|
||||||
|
return error (expr, "invalid type passed to %s", type_funcs[ind].name);
|
||||||
|
}
|
||||||
|
if (op == QC_AT_WIDTH) {
|
||||||
|
return new_int_expr (type_width (type), false);
|
||||||
|
}
|
||||||
|
if (op == QC_AT_ROWS) {
|
||||||
|
return new_int_expr (type_rows (type), false);
|
||||||
|
}
|
||||||
|
if (op == QC_AT_COLS) {
|
||||||
|
return new_int_expr (type_cols (type), false);
|
||||||
|
}
|
||||||
|
internal_error (expr, "invalid type op");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const expr_t *
|
||||||
|
evaluate_int_op (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
return evaluate_int (args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_field (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = field_type (type);
|
||||||
|
type = find_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_pointer (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = pointer_type (type);
|
||||||
|
type = find_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_array (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
int count = 0;
|
||||||
|
if (arg_count > 1) {
|
||||||
|
auto count_expr = evaluate_int (args[1]);
|
||||||
|
if (is_error (count_expr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
count = expr_int (count_expr);
|
||||||
|
}
|
||||||
|
type = array_type (type, count);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_base (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = base_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_vector (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
int width = 0;
|
||||||
|
if (arg_count > 1) {
|
||||||
|
auto width_expr = evaluate_int (args[1]);
|
||||||
|
if (is_error (width_expr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
width = expr_integral (width_expr);
|
||||||
|
} else {
|
||||||
|
width = type_width (type);
|
||||||
|
}
|
||||||
|
type = vector_type (type, width);
|
||||||
|
if (!type) {
|
||||||
|
error (args[0], "invalid @vector");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_matrix (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
int rows = 0;
|
||||||
|
int cols = 0;
|
||||||
|
if (arg_count > 1) {
|
||||||
|
auto rows_expr = evaluate_int (args[2]);
|
||||||
|
auto cols_expr = evaluate_int (args[1]);
|
||||||
|
if (is_error (rows_expr) || is_error (cols_expr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
rows = expr_integral (rows_expr);
|
||||||
|
cols = expr_integral (cols_expr);
|
||||||
|
} else {
|
||||||
|
cols = type_cols (type);
|
||||||
|
rows = type_rows (type);
|
||||||
|
}
|
||||||
|
type = matrix_type (type, cols, rows);
|
||||||
|
if (!type) {
|
||||||
|
error (args[0], "invalid @matrix");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_int (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = int_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_uint (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = uint_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_bool (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = bool_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const type_t *
|
||||||
|
resolve_float (int arg_count, const expr_t **args)
|
||||||
|
{
|
||||||
|
auto type = resolve_type (args[0]);
|
||||||
|
if (type) {
|
||||||
|
type = float_type (type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
static type_func_t type_funcs[] = {
|
static type_func_t type_funcs[] = {
|
||||||
[QC_AT_FIELD - QC_GENERIC] = {
|
[QC_AT_FIELD - QC_GENERIC] = {
|
||||||
.name = "@field",
|
.name = "@field",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_field,
|
||||||
},
|
},
|
||||||
[QC_AT_POINTER - QC_GENERIC] = {
|
[QC_AT_POINTER - QC_GENERIC] = {
|
||||||
.name = "@pointer",
|
.name = "@pointer",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_pointer,
|
||||||
},
|
},
|
||||||
[QC_AT_ARRAY - QC_GENERIC] = {
|
[QC_AT_ARRAY - QC_GENERIC] = {
|
||||||
.name = "@array",
|
.name = "@array",
|
||||||
.check_params = single_type_opt_int,
|
.check_params = single_type_opt_int,
|
||||||
|
.resolve = resolve_array,
|
||||||
},
|
},
|
||||||
[QC_AT_BASE - QC_GENERIC] = {
|
[QC_AT_BASE - QC_GENERIC] = {
|
||||||
.name = "@base",
|
.name = "@base",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_base,
|
||||||
},
|
},
|
||||||
[QC_AT_WIDTH - QC_GENERIC] = {
|
[QC_AT_WIDTH - QC_GENERIC] = {
|
||||||
.name = "@width",
|
.name = "@width",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.evaluate = evaluate_int_op,
|
||||||
},
|
},
|
||||||
[QC_AT_VECTOR - QC_GENERIC] = {
|
[QC_AT_VECTOR - QC_GENERIC] = {
|
||||||
.name = "@vector",
|
.name = "@vector",
|
||||||
.check_params = single_type_opt_int,
|
.check_params = single_type_opt_int,
|
||||||
|
.resolve = resolve_vector,
|
||||||
},
|
},
|
||||||
[QC_AT_ROWS - QC_GENERIC] = {
|
[QC_AT_ROWS - QC_GENERIC] = {
|
||||||
.name = "@rows",
|
.name = "@rows",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.evaluate = evaluate_int_op,
|
||||||
},
|
},
|
||||||
[QC_AT_COLS - QC_GENERIC] = {
|
[QC_AT_COLS - QC_GENERIC] = {
|
||||||
.name = "@cols",
|
.name = "@cols",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.evaluate = evaluate_int_op,
|
||||||
},
|
},
|
||||||
[QC_AT_MATRIX - QC_GENERIC] = {
|
[QC_AT_MATRIX - QC_GENERIC] = {
|
||||||
.name = "@matrix",
|
.name = "@matrix",
|
||||||
.check_params = single_type_opt_int_pair,
|
.check_params = single_type_opt_int_pair,
|
||||||
|
.resolve = resolve_matrix,
|
||||||
},
|
},
|
||||||
[QC_AT_INT - QC_GENERIC] = {
|
[QC_AT_INT - QC_GENERIC] = {
|
||||||
.name = "@int",
|
.name = "@int",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_int,
|
||||||
},
|
},
|
||||||
[QC_AT_UINT - QC_GENERIC] = {
|
[QC_AT_UINT - QC_GENERIC] = {
|
||||||
.name = "@uint",
|
.name = "@uint",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_uint,
|
||||||
},
|
},
|
||||||
[QC_AT_BOOL - QC_GENERIC] = {
|
[QC_AT_BOOL - QC_GENERIC] = {
|
||||||
.name = "@bool",
|
.name = "@bool",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_bool,
|
||||||
},
|
},
|
||||||
[QC_AT_FLOAT - QC_GENERIC] = {
|
[QC_AT_FLOAT - QC_GENERIC] = {
|
||||||
.name = "@float",
|
.name = "@float",
|
||||||
.check_params = single_type,
|
.check_params = single_type,
|
||||||
|
.resolve = resolve_float,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -185,6 +395,7 @@ type_function (int op, const expr_t *params)
|
||||||
}
|
}
|
||||||
auto te = new_expr ();
|
auto te = new_expr ();
|
||||||
te->type = ex_type;
|
te->type = ex_type;
|
||||||
|
te->nodag = 1;
|
||||||
te->typ = (ex_type_t) {
|
te->typ = (ex_type_t) {
|
||||||
.op = op,
|
.op = op,
|
||||||
.params = params,
|
.params = params,
|
||||||
|
@ -202,3 +413,43 @@ type_parameter (symbol_t *sym, const expr_t *type)
|
||||||
sym->s.expr = type;
|
sym->s.expr = type;
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const type_t *
|
||||||
|
resolve_type (const expr_t *te)
|
||||||
|
{
|
||||||
|
if (te->type != ex_type) {
|
||||||
|
internal_error (te, "not a type expression");
|
||||||
|
}
|
||||||
|
if (!te->typ.op) {
|
||||||
|
if (!te->typ.type) {
|
||||||
|
internal_error (te, "no type in reference");
|
||||||
|
}
|
||||||
|
return te->typ.type;
|
||||||
|
}
|
||||||
|
int op = te->typ.op;
|
||||||
|
unsigned ind = op - QC_GENERIC;
|
||||||
|
if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0])) {
|
||||||
|
internal_error (te, "invalid type op: %d", op);
|
||||||
|
}
|
||||||
|
int arg_count = list_count (&te->typ.params->list);
|
||||||
|
const expr_t *args[arg_count];
|
||||||
|
list_scatter (&te->typ.params->list, args);
|
||||||
|
return type_funcs[ind].resolve (arg_count, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const expr_t *
|
||||||
|
evaluate_type (const expr_t *te)
|
||||||
|
{
|
||||||
|
if (te->type != ex_type) {
|
||||||
|
internal_error (te, "not a type expression");
|
||||||
|
}
|
||||||
|
int op = te->typ.op;
|
||||||
|
unsigned ind = op - QC_GENERIC;
|
||||||
|
if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0])) {
|
||||||
|
internal_error (te, "invalid type op: %d", op);
|
||||||
|
}
|
||||||
|
int arg_count = list_count (&te->typ.params->list);
|
||||||
|
const expr_t *args[arg_count];
|
||||||
|
list_scatter (&te->typ.params->list, args);
|
||||||
|
return type_funcs[ind].evaluate (arg_count, args);
|
||||||
|
}
|
||||||
|
|
|
@ -418,6 +418,10 @@ make_ellipsis (void)
|
||||||
static param_t *
|
static param_t *
|
||||||
make_param (specifier_t spec)
|
make_param (specifier_t spec)
|
||||||
{
|
{
|
||||||
|
if (spec.type_expr) {
|
||||||
|
if (!(spec.type = resolve_type (spec.type_expr))) {
|
||||||
|
}
|
||||||
|
}
|
||||||
spec = default_type (spec, spec.sym);
|
spec = default_type (spec, spec.sym);
|
||||||
spec.type = find_type (append_type (spec.sym->type, spec.type));
|
spec.type = find_type (append_type (spec.sym->type, spec.type));
|
||||||
param_t *param = new_param (0, spec.type, spec.sym->name);
|
param_t *param = new_param (0, spec.type, spec.sym->name);
|
||||||
|
@ -916,7 +920,11 @@ typespec
|
||||||
|
|
||||||
typespec_reserved
|
typespec_reserved
|
||||||
: TYPE_SPEC
|
: TYPE_SPEC
|
||||||
| type_function { $$ = make_spec ($1->typ.type, 0, 0, 0); }
|
| type_function
|
||||||
|
{
|
||||||
|
auto type = resolve_type ($1);
|
||||||
|
$$ = make_spec (type, 0, 0, 0);
|
||||||
|
}
|
||||||
| algebra_specifier %prec LOW
|
| algebra_specifier %prec LOW
|
||||||
| algebra_specifier '.' attribute
|
| algebra_specifier '.' attribute
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue