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);
|
||||
const expr_t *type_function (int op, const expr_t *params);
|
||||
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.
|
||||
|
||||
|
|
|
@ -37,9 +37,13 @@
|
|||
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
static type_func_t type_funcs[];
|
||||
|
||||
static bool
|
||||
check_type (const expr_t *arg)
|
||||
{
|
||||
|
@ -50,23 +54,37 @@ check_type (const expr_t *arg)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
static bool __attribute__((pure))
|
||||
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)
|
||||
&& arg->type != ex_type) {
|
||||
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;
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
if (count > 1) {
|
||||
if (arg_count > 1) {
|
||||
return "too many arguments";
|
||||
}
|
||||
if (!check_type (args[0])) {
|
||||
|
@ -76,96 +94,288 @@ single_type (int count, const expr_t **args)
|
|||
}
|
||||
|
||||
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";
|
||||
}
|
||||
if (count > 2) {
|
||||
if (arg_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])) {
|
||||
if (arg_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)
|
||||
single_type_opt_int_pair (int arg_count, const expr_t **args)
|
||||
{
|
||||
if (count < 1) {
|
||||
if (arg_count < 1) {
|
||||
return "too few arguments";
|
||||
}
|
||||
if (count > 3) {
|
||||
if (arg_count > 3) {
|
||||
return "too many arguments";
|
||||
}
|
||||
if (count == 2) {
|
||||
if (arg_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]))) {
|
||||
if (arg_count > 1 && (!check_int (args[1]) || !check_int (args[2]))) {
|
||||
return "second and third parameters must be int";
|
||||
}
|
||||
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[] = {
|
||||
[QC_AT_FIELD - QC_GENERIC] = {
|
||||
.name = "@field",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_field,
|
||||
},
|
||||
[QC_AT_POINTER - QC_GENERIC] = {
|
||||
.name = "@pointer",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_pointer,
|
||||
},
|
||||
[QC_AT_ARRAY - QC_GENERIC] = {
|
||||
.name = "@array",
|
||||
.check_params = single_type_opt_int,
|
||||
.resolve = resolve_array,
|
||||
},
|
||||
[QC_AT_BASE - QC_GENERIC] = {
|
||||
.name = "@base",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_base,
|
||||
},
|
||||
[QC_AT_WIDTH - QC_GENERIC] = {
|
||||
.name = "@width",
|
||||
.check_params = single_type,
|
||||
.evaluate = evaluate_int_op,
|
||||
},
|
||||
[QC_AT_VECTOR - QC_GENERIC] = {
|
||||
.name = "@vector",
|
||||
.check_params = single_type_opt_int,
|
||||
.resolve = resolve_vector,
|
||||
},
|
||||
[QC_AT_ROWS - QC_GENERIC] = {
|
||||
.name = "@rows",
|
||||
.check_params = single_type,
|
||||
.evaluate = evaluate_int_op,
|
||||
},
|
||||
[QC_AT_COLS - QC_GENERIC] = {
|
||||
.name = "@cols",
|
||||
.check_params = single_type,
|
||||
.evaluate = evaluate_int_op,
|
||||
},
|
||||
[QC_AT_MATRIX - QC_GENERIC] = {
|
||||
.name = "@matrix",
|
||||
.check_params = single_type_opt_int_pair,
|
||||
.resolve = resolve_matrix,
|
||||
},
|
||||
[QC_AT_INT - QC_GENERIC] = {
|
||||
.name = "@int",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_int,
|
||||
},
|
||||
[QC_AT_UINT - QC_GENERIC] = {
|
||||
.name = "@uint",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_uint,
|
||||
},
|
||||
[QC_AT_BOOL - QC_GENERIC] = {
|
||||
.name = "@bool",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_bool,
|
||||
},
|
||||
[QC_AT_FLOAT - QC_GENERIC] = {
|
||||
.name = "@float",
|
||||
.check_params = single_type,
|
||||
.resolve = resolve_float,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -185,6 +395,7 @@ type_function (int op, const expr_t *params)
|
|||
}
|
||||
auto te = new_expr ();
|
||||
te->type = ex_type;
|
||||
te->nodag = 1;
|
||||
te->typ = (ex_type_t) {
|
||||
.op = op,
|
||||
.params = params,
|
||||
|
@ -202,3 +413,43 @@ type_parameter (symbol_t *sym, const expr_t *type)
|
|||
sym->s.expr = type;
|
||||
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 *
|
||||
make_param (specifier_t spec)
|
||||
{
|
||||
if (spec.type_expr) {
|
||||
if (!(spec.type = resolve_type (spec.type_expr))) {
|
||||
}
|
||||
}
|
||||
spec = default_type (spec, spec.sym);
|
||||
spec.type = find_type (append_type (spec.sym->type, spec.type));
|
||||
param_t *param = new_param (0, spec.type, spec.sym->name);
|
||||
|
@ -916,7 +920,11 @@ typespec
|
|||
|
||||
typespec_reserved
|
||||
: 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 '.' attribute
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue