X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=672ce86799044024dfc955d3dfc5aec31d5e4f28;hp=4cfd87f4bd19cb4ff831b415ac2c485004a68a7e;hb=41a76ab91dc6f872b03cd64f51aba86578f330c0;hpb=cddf70f46bc9c46b6adda39a83e9564bf0cf34d3 diff --git a/parser.c b/parser.c index 4cfd87f..672ce86 100644 --- a/parser.c +++ b/parser.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 + * Copyright (C) 2012, 2013, 2014, 2015 * Wolfgang Bumiller * Dale Weiler * @@ -24,91 +24,12 @@ #include #include -#include "gmqcc.h" -#include "lexer.h" -#include "ast.h" +#include "parser.h" -/* beginning of locals */ #define PARSER_HT_LOCALS 2 - #define PARSER_HT_SIZE 512 #define TYPEDEF_HT_SIZE 512 -typedef struct parser_s { - lex_file *lex; - int tok; - - bool ast_cleaned; - - ast_expression **globals; - ast_expression **fields; - ast_function **functions; - ast_value **imm_float; - ast_value **imm_string; - ast_value **imm_vector; - size_t translated; - - ht ht_imm_string; - ht ht_imm_string_dotranslate; - - /* must be deleted first, they reference immediates and values */ - ast_value **accessors; - - ast_value *imm_float_zero; - ast_value *imm_float_one; - ast_value *imm_float_neg_one; - - ast_value *imm_vector_zero; - - ast_value *nil; - ast_value *reserved_version; - - size_t crc_globals; - size_t crc_fields; - - ast_function *function; - ht aliases; - - /* All the labels the function defined... - * Should they be in ast_function instead? - */ - ast_label **labels; - ast_goto **gotos; - const char **breaks; - const char **continues; - - /* A list of hashtables for each scope */ - ht *variables; - ht htfields; - ht htglobals; - ht *typedefs; - - /* same as above but for the spelling corrector */ - correct_trie_t **correct_variables; - size_t ***correct_variables_score; /* vector of vector of size_t* */ - - /* not to be used directly, we use the hash table */ - ast_expression **_locals; - size_t *_blocklocals; - ast_value **_typedefs; - size_t *_blocktypedefs; - lex_ctx *_block_ctx; - - /* we store the '=' operator info */ - const oper_info *assign_op; - - /* magic values */ - ast_value *const_vec[3]; - - /* pragma flags */ - bool noref; - - /* collected information */ - size_t max_param_count; -} parser_t; - -static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; - static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); @@ -123,13 +44,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels); static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname); static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname); -static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); +static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg); static void parseerror(parser_t *parser, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - vcompile_error(*(parser->lex->tok.ctx), fmt, ap); + vcompile_error(parser->lex->tok.ctx, fmt, ap); va_end(ap); } @@ -139,47 +60,11 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char * bool r; va_list ap; va_start(ap, fmt); - r = vcompile_warning(*(parser->lex->tok.ctx), warntype, fmt, ap); + r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap); va_end(ap); return r; } -/********************************************************************** - * some maths used for constant folding - */ - -vector vec3_add(vector a, vector b) -{ - vector out; - out.x = a.x + b.x; - out.y = a.y + b.y; - out.z = a.z + b.z; - return out; -} - -vector vec3_sub(vector a, vector b) -{ - vector out; - out.x = a.x - b.x; - out.y = a.y - b.y; - out.z = a.z - b.z; - return out; -} - -qcfloat vec3_mulvv(vector a, vector b) -{ - return (a.x * b.x + a.y * b.y + a.z * b.z); -} - -vector vec3_mulvf(vector a, float b) -{ - vector out; - out.x = a.x * b; - out.y = a.y * b; - out.z = a.z * b; - return out; -} - /********************************************************************** * parsing */ @@ -199,53 +84,8 @@ static bool parser_next(parser_t *parser) #define parser_tokval(p) ((p)->lex->tok.value) #define parser_token(p) (&((p)->lex->tok)) -#define parser_ctx(p) (*((p)->lex->tok.ctx)) - -static ast_value* parser_const_float(parser_t *parser, double d) -{ - size_t i; - ast_value *out; - lex_ctx ctx; - for (i = 0; i < vec_size(parser->imm_float); ++i) { - const double compare = parser->imm_float[i]->constval.vfloat; - if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0) - return parser->imm_float[i]; - } - if (parser->lex) - ctx = parser_ctx(parser); - else { - memset(&ctx, 0, sizeof(ctx)); - } - out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT); - out->cvq = CV_CONST; - out->hasvalue = true; - out->isimm = true; - out->constval.vfloat = d; - vec_push(parser->imm_float, out); - return out; -} - -static ast_value* parser_const_float_0(parser_t *parser) -{ - if (!parser->imm_float_zero) - parser->imm_float_zero = parser_const_float(parser, 0); - return parser->imm_float_zero; -} - -static ast_value* parser_const_float_neg1(parser_t *parser) { - if (!parser->imm_float_neg_one) - parser->imm_float_neg_one = parser_const_float(parser, -1); - return parser->imm_float_neg_one; -} -static ast_value* parser_const_float_1(parser_t *parser) -{ - if (!parser->imm_float_one) - parser->imm_float_one = parser_const_float(parser, 1); - return parser->imm_float_one; -} - -static char *parser_strdup(const char *str) +char *parser_strdup(const char *str) { if (str && !*str) { /* actually dup empty strings */ @@ -256,74 +96,6 @@ static char *parser_strdup(const char *str) return util_strdup(str); } -static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate) -{ - ht ht_string = (dotranslate) - ? parser->ht_imm_string_dotranslate - : parser->ht_imm_string; - - ast_value *out; - size_t hash = util_hthash(ht_string, str); - - if ((out = (ast_value*)util_htgeth(ht_string, str, hash))) - return out; - /* - for (i = 0; i < vec_size(parser->imm_string); ++i) { - if (!strcmp(parser->imm_string[i]->constval.vstring, str)) - return parser->imm_string[i]; - } - */ - if (dotranslate) { - char name[32]; - util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); - out = ast_value_new(parser_ctx(parser), name, TYPE_STRING); - out->expression.flags |= AST_FLAG_INCLUDE_DEF; - } else - out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING); - - out->cvq = CV_CONST; - out->hasvalue = true; - out->isimm = true; - out->constval.vstring = parser_strdup(str); - vec_push(parser->imm_string, out); - util_htseth(ht_string, str, hash, out); - - return out; -} - -static ast_value* parser_const_vector(parser_t *parser, vector v) -{ - size_t i; - ast_value *out; - for (i = 0; i < vec_size(parser->imm_vector); ++i) { - if (!memcmp(&parser->imm_vector[i]->constval.vvec, &v, sizeof(v))) - return parser->imm_vector[i]; - } - out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR); - out->cvq = CV_CONST; - out->hasvalue = true; - out->isimm = true; - out->constval.vvec = v; - vec_push(parser->imm_vector, out); - return out; -} - -static ast_value* parser_const_vector_f(parser_t *parser, float x, float y, float z) -{ - vector v; - v.x = x; - v.y = y; - v.z = z; - return parser_const_vector(parser, v); -} - -static ast_value* parser_const_vector_0(parser_t *parser) -{ - if (!parser->imm_vector_zero) - parser->imm_vector_zero = parser_const_vector_f(parser, 0, 0, 0); - return parser->imm_vector_zero; -} - static ast_expression* parser_find_field(parser_t *parser, const char *name) { return ( ast_expression*)util_htget(parser->htfields, name); @@ -338,7 +110,7 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name) return NULL; } -static ast_expression* parser_find_global(parser_t *parser, const char *name) +ast_expression* parser_find_global(parser_t *parser, const char *name) { ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser)); if (var) @@ -400,9 +172,6 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t return NULL; } -/* include intrinsics */ -#include "intrin.h" - typedef struct { size_t etype; /* 0 = expression, others are operators */ @@ -410,7 +179,7 @@ typedef struct size_t off; ast_expression *out; ast_block *block; /* for commas and function calls */ - lex_ctx ctx; + lex_ctx_t ctx; } sy_elem; enum { @@ -428,7 +197,7 @@ typedef struct unsigned int *paren; } shunt; -static sy_elem syexp(lex_ctx ctx, ast_expression *v) { +static sy_elem syexp(lex_ctx_t ctx, ast_expression *v) { sy_elem e; e.etype = 0; e.off = 0; @@ -439,7 +208,7 @@ static sy_elem syexp(lex_ctx ctx, ast_expression *v) { return e; } -static sy_elem syblock(lex_ctx ctx, ast_block *v) { +static sy_elem syblock(lex_ctx_t ctx, ast_block *v) { sy_elem e; e.etype = 0; e.off = 0; @@ -450,7 +219,7 @@ static sy_elem syblock(lex_ctx ctx, ast_block *v) { return e; } -static sy_elem syop(lex_ctx ctx, const oper_info *op) { +static sy_elem syop(lex_ctx_t ctx, const oper_info *op) { sy_elem e; e.etype = 1 + (op - operators); e.off = 0; @@ -461,7 +230,7 @@ static sy_elem syop(lex_ctx ctx, const oper_info *op) { return e; } -static sy_elem syparen(lex_ctx ctx, size_t off) { +static sy_elem syparen(lex_ctx_t ctx, size_t off) { sy_elem e; e.etype = 0; e.off = off; @@ -484,7 +253,7 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) ast_expression *sub; ast_expression *entity; - lex_ctx ctx = ast_ctx(*out); + lex_ctx_t ctx = ast_ctx(*out); if (!ast_istype(*out, ast_array_index)) return false; @@ -514,43 +283,39 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } -static bool immediate_is_true(lex_ctx ctx, ast_value *v) +static bool check_write_to(lex_ctx_t ctx, ast_expression *expr) { - switch (v->expression.vtype) { - case TYPE_FLOAT: - return !!v->constval.vfloat; - case TYPE_INTEGER: - return !!v->constval.vint; - case TYPE_VECTOR: - if (OPTS_FLAG(CORRECT_LOGIC)) - return v->constval.vvec.x && - v->constval.vvec.y && - v->constval.vvec.z; - else - return !!(v->constval.vvec.x); - case TYPE_STRING: - if (!v->constval.vstring) + if (ast_istype(expr, ast_value)) { + ast_value *val = (ast_value*)expr; + if (val->cvq == CV_CONST) { + if (val->name[0] == '#') { + compile_error(ctx, "invalid assignment to a literal constant"); return false; - if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS)) - return true; - return !!v->constval.vstring[0]; - default: - compile_error(ctx, "internal error: immediate_is_true on invalid type"); - return !!v->constval.vfunc; + } + /* + * To work around quakeworld we must elide the error and make it + * a warning instead. + */ + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) + compile_error(ctx, "assignment to constant `%s`", val->name); + else + (void)!compile_warning(ctx, WARN_CONST_OVERWRITE, "assignment to constant `%s`", val->name); + return false; + } } + return true; } static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { const oper_info *op; - lex_ctx ctx; + lex_ctx_t ctx; ast_expression *out = NULL; ast_expression *exprs[3]; ast_block *blocks[3]; - ast_value *asvalue[3]; ast_binstore *asbinstore; size_t i, assignop, addop, subop; - qcint generated_op = 0; + qcint_t generated_op = 0; char ty1[1024]; char ty2[1024]; @@ -569,8 +334,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ctx = vec_last(sy->ops).ctx; if (vec_size(sy->out) < op->operands) { - compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out), - op->op, (int)op->id); + if (op->flags & OP_PREFIX) + compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id); + else /* this should have errored previously already */ + compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id); return false; } @@ -584,7 +351,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) for (i = 0; i < op->operands; ++i) { exprs[i] = sy->out[vec_size(sy->out)+i].out; blocks[i] = sy->out[vec_size(sy->out)+i].block; - asvalue[i] = (ast_value*)exprs[i]; if (exprs[i]->vtype == TYPE_NOEXPR && !(i != 0 && op->id == opid2('?',':')) && @@ -606,14 +372,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) #define NotSameType(T) \ (exprs[0]->vtype != exprs[1]->vtype || \ exprs[0]->vtype != T) -#define CanConstFold1(A) \ - (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\ - (A)->vtype != TYPE_FUNCTION) -#define CanConstFold(A, B) \ - (CanConstFold1(A) && CanConstFold1(B)) -#define ConstV(i) (asvalue[(i)]->constval.vvec) -#define ConstF(i) (asvalue[(i)]->constval.vfloat) -#define ConstS(i) (asvalue[(i)]->constval.vstring) + switch (op->id) { default: @@ -708,132 +467,100 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = exprs[0]; break; case opid2('-','P'): - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - if (CanConstFold1(exprs[0])) - out = (ast_expression*)parser_const_float(parser, -ConstF(0)); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, - (ast_expression*)parser_const_float_0(parser), - exprs[0]); - break; - case TYPE_VECTOR: - if (CanConstFold1(exprs[0])) - out = (ast_expression*)parser_const_vector_f(parser, - -ConstV(0).x, -ConstV(0).y, -ConstV(0).z); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, - (ast_expression*)parser_const_vector_0(parser), - exprs[0]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot negate type %s", - type_name[exprs[0]->vtype]); + if ((out = fold_op(parser->fold, op, exprs))) + break; + + if (exprs[0]->vtype != TYPE_FLOAT && + exprs[0]->vtype != TYPE_VECTOR) { + compile_error(ctx, "invalid types used in unary expression: cannot negate type %s", + type_name[exprs[0]->vtype]); return false; } + if (exprs[0]->vtype == TYPE_FLOAT) + out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_F, exprs[0]); + else + out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_V, exprs[0]); break; case opid2('!','P'): - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - if (CanConstFold1(exprs[0])) - out = (ast_expression*)parser_const_float(parser, !ConstF(0)); - else + if (!(out = fold_op(parser->fold, op, exprs))) { + switch (exprs[0]->vtype) { + case TYPE_FLOAT: out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]); - break; - case TYPE_VECTOR: - if (CanConstFold1(exprs[0])) - out = (ast_expression*)parser_const_float(parser, - (!ConstV(0).x && !ConstV(0).y && !ConstV(0).z)); - else + break; + case TYPE_VECTOR: out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]); - break; - case TYPE_STRING: - if (CanConstFold1(exprs[0])) { - if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) - out = (ast_expression*)parser_const_float(parser, !ConstS(0)); - else - out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0)); - } else { + break; + case TYPE_STRING: if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]); else out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]); - } - break; - /* we don't constant-fold NOT for these types */ - case TYPE_ENTITY: - out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]); - break; - case TYPE_FUNCTION: - out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot logically negate type %s", - type_name[exprs[0]->vtype]); - return false; + break; + /* we don't constant-fold NOT for these types */ + case TYPE_ENTITY: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]); + break; + case TYPE_FUNCTION: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot logically negate type %s", + type_name[exprs[0]->vtype]); + return false; + } } break; case opid1('+'): if (exprs[0]->vtype != exprs[1]->vtype || - (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) + (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) { compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", type_name[exprs[0]->vtype], type_name[exprs[1]->vtype]); return false; } - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - if (CanConstFold(exprs[0], exprs[1])) - { - out = (ast_expression*)parser_const_float(parser, ConstF(0) + ConstF(1)); - } - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); - break; - case TYPE_VECTOR: - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_vector(parser, vec3_add(ConstV(0), ConstV(1))); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", - type_name[exprs[0]->vtype], - type_name[exprs[1]->vtype]); - return false; - }; + if (!(out = fold_op(parser->fold, op, exprs))) { + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + out = fold_binary(ctx, INSTR_ADD_V, exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + } break; case opid1('-'): - if (exprs[0]->vtype != exprs[1]->vtype || - (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) + if (exprs[0]->vtype != exprs[1]->vtype || + (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT)) { compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s", type_name[exprs[1]->vtype], type_name[exprs[0]->vtype]); return false; } - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1)); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]); - break; - case TYPE_VECTOR: - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_vector(parser, vec3_sub(ConstV(0), ConstV(1))); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s", - type_name[exprs[1]->vtype], - type_name[exprs[0]->vtype]); - return false; - }; + if (!(out = fold_op(parser->fold, op, exprs))) { + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + out = fold_binary(ctx, INSTR_SUB_V, exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + } + } break; case opid1('*'): if (exprs[0]->vtype != exprs[1]->vtype && @@ -848,104 +575,29 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) type_name[exprs[0]->vtype]); return false; } - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - if (exprs[1]->vtype == TYPE_VECTOR) - { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0))); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); - } - else - { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, ConstF(0) * ConstF(1)); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]); - } - break; - case TYPE_VECTOR: - if (exprs[1]->vtype == TYPE_FLOAT) - { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1))); + if (!(out = fold_op(parser->fold, op, exprs))) { + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (exprs[1]->vtype == TYPE_VECTOR) + out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); - } - else - { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1))); - else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) { - vector vec = ConstV(0); - if (!vec.y && !vec.z) { /* 'n 0 0' * v */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.x != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out); - } - else if (!vec.x && !vec.z) { /* '0 n 0' * v */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.y != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out); - } - else if (!vec.x && !vec.y) { /* '0 n 0' * v */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.z != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out); - } - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); - } - else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) { - vector vec = ConstV(1); - if (!vec.y && !vec.z) { /* v * 'n 0 0' */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.x != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x)); - } - else if (!vec.x && !vec.z) { /* v * '0 n 0' */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.y != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y)); - } - else if (!vec.x && !vec.y) { /* v * '0 n 0' */ - ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; - out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); - out->node.keep = false; - ((ast_member*)out)->rvalue = true; - if (vec.z != 1) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z)); - } - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); - } + out = fold_binary(ctx, INSTR_MUL_F, exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + if (exprs[1]->vtype == TYPE_FLOAT) + out = fold_binary(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); - } - break; - default: - compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s", - type_name[exprs[1]->vtype], - type_name[exprs[0]->vtype]); - return false; - }; + out = fold_binary(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + } + } break; + case opid1('/'): if (exprs[1]->vtype != TYPE_FLOAT) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -953,37 +605,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2); return false; } - if (exprs[0]->vtype == TYPE_FLOAT) { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1)); - else - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]); - } - else if (exprs[0]->vtype == TYPE_VECTOR) { - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1))); + if (!(out = fold_op(parser->fold, op, exprs))) { + if (exprs[0]->vtype == TYPE_FLOAT) + out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[1]); else { - if (CanConstFold1(exprs[1])) { - out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1)); - } else { - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, - (ast_expression*)parser_const_float_1(parser), - exprs[1]); - } - if (!out) { - compile_error(ctx, "internal error: failed to generate division"); - return false; - } - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], out); + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2); + return false; } } - else - { - ast_type_to_string(exprs[0], ty1, sizeof(ty1)); - ast_type_to_string(exprs[1], ty2, sizeof(ty2)); - compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2); - return false; - } break; case opid1('%'): @@ -992,13 +623,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) type_name[exprs[0]->vtype], type_name[exprs[1]->vtype]); return false; - } - if (CanConstFold(exprs[0], exprs[1])) { - out = (ast_expression*)parser_const_float(parser, - (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1)))); - } else { + } else if (!(out = fold_op(parser->fold, op, exprs))) { /* generate a call to __builtin_mod */ - ast_expression *mod = intrin_func(parser, "mod"); + ast_expression *mod = intrin_func(parser->intrin, "mod"); ast_call *call = NULL; if (!mod) return false; /* can return null for missing floor */ @@ -1016,59 +643,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('|'): case opid1('&'): - if (NotSameType(TYPE_FLOAT)) { - compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s", - type_name[exprs[0]->vtype], - type_name[exprs[1]->vtype]); - return false; - } - if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, - (op->id == opid1('|') ? (float)( ((qcint)ConstF(0)) | ((qcint)ConstF(1)) ) : - (float)( ((qcint)ConstF(0)) & ((qcint)ConstF(1)) ) )); - else - out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), - exprs[0], exprs[1]); - break; case opid1('^'): - /* - * Okay lets designate what the hell is an acceptable use - * of the ^ operator. In many vector processing units, XOR - * is allowed to be used on vectors, but only if the first - * operand is a vector, the second operand can be a float - * or vector. It's never legal for the first operand to be - * a float, and then the following operand to be a vector. - * Further more, the only time it is legal to do XOR otherwise - * is when both operand are floats. This nicely crafted if - * statement catches them all. - * - * In the event that the first operand is a vector, two - * possible situations can arise, thus, each element of - * vector A (operand A) is exclusive-ORed with the corresponding - * element of vector B (operand B), If B is scalar, the - * scalar value is first replicated for each element. - * - * The QCVM itself lacks a BITXOR instruction. Thus emulating - * the mathematics of it is required. The following equation - * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the - * QCVM also lacking a BITNEG instruction, we need to emulate - * ~FOO with -1 - FOO, the whole process becoming this nicely - * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)). - * - * When A is not scalar, this process is repeated for all - * components of vector A with the value in operand B, - * only if operand B is scalar. When A is not scalar, and B - * is also not scalar, this process is repeated for all - * components of the vector A with the components of vector B. - * Finally when A is scalar and B is scalar, this process is - * simply used once for A and B being LHS and RHS respectfully. - * - * Yes the semantics are a bit strange (no pun intended). - * But then again BITXOR is strange itself, consdering it's - * commutative, assocative, and elements of the BITXOR operation - * are their own inverse. - */ if ( !(exprs[0]->vtype == TYPE_FLOAT && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR)) @@ -1079,115 +654,84 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } - /* - * IF the first expression is float, the following will be too - * since scalar ^ vector is not allowed. - */ - if (exprs[0]->vtype == TYPE_FLOAT) { - if(CanConstFold(exprs[0], exprs[1])) { - out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1))))); - } else { - ast_binary *expr = ast_binary_new( - ctx, - INSTR_SUB_F, - (ast_expression*)parser_const_float_neg1(parser), - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITAND, - exprs[0], - exprs[1] - ) - ); - expr->refs = AST_REF_NONE; - - out = (ast_expression*) - ast_binary_new( - ctx, - INSTR_BITAND, - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITOR, - exprs[0], - exprs[1] - ), - (ast_expression*)expr - ); - } - } else { + if (!(out = fold_op(parser->fold, op, exprs))) { /* - * The first is a vector: vector is allowed to xor with vector and - * with scalar, branch here for the second operand. + * IF the first expression is float, the following will be too + * since scalar ^ vector is not allowed. */ - if (exprs[1]->vtype == TYPE_VECTOR) { - /* - * Xor all the values of the vector components against the - * vectors components in question. - */ - if (CanConstFold(exprs[0], exprs[1])) { - out = (ast_expression*)parser_const_vector_f( - parser, - (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))), - (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))), - (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z))) - ); - } else { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector"); - return false; - } + if (exprs[0]->vtype == TYPE_FLOAT) { + out = fold_binary(ctx, + (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), + exprs[0], exprs[1]); } else { /* - * Xor all the values of the vector components against the - * scalar in question. + * The first is a vector: vector is allowed to bitop with vector and + * with scalar, branch here for the second operand. */ - if (CanConstFold(exprs[0], exprs[1])) { - out = (ast_expression*)parser_const_vector_f( - parser, - (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))), - (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))), - (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1)))) - ); + if (exprs[1]->vtype == TYPE_VECTOR) { + /* + * Bitop all the values of the vector components against the + * vectors components in question. + */ + out = fold_binary(ctx, + (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V), + exprs[0], exprs[1]); } else { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float"); - return false; + out = fold_binary(ctx, + (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF), + exprs[0], exprs[1]); } } } - break; case opid2('<','<'): case opid2('>','>'): - if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) { - if (op->id == opid2('<','<')) - out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) << (unsigned int)(ConstF(1)))); - else - out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) >> (unsigned int)(ConstF(1)))); - break; + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform shift between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; } + + if (!(out = fold_op(parser->fold, op, exprs))) { + ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift"); + ast_call *call = ast_call_new(parser_ctx(parser), shift); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + out = (ast_expression*)call; + } + break; + case opid3('<','<','='): case opid3('>','>','='): - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); - return false; + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform shift operation between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + + if(!(out = fold_op(parser->fold, op, exprs))) { + ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift"); + ast_call *call = ast_call_new(parser_ctx(parser), shift); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + out = (ast_expression*)ast_store_new( + parser_ctx(parser), + INSTR_STORE_F, + exprs[0], + (ast_expression*)call + ); + } + + break; case opid2('|','|'): generated_op += 1; /* INSTR_OR */ case opid2('&','&'): generated_op += INSTR_AND; - if (CanConstFold(exprs[0], exprs[1])) - { - if (OPTS_FLAG(PERL_LOGIC)) { - if (immediate_is_true(ctx, asvalue[0])) - out = exprs[1]; - } - else - out = (ast_expression*)parser_const_float(parser, - ( (generated_op == INSTR_OR) - ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1])) - : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) ) - ? 1 : 0); - } - else - { + if (!(out = fold_op(parser->fold, op, exprs))) { if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); @@ -1218,7 +762,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } } } - out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + out = fold_binary(ctx, generated_op, exprs[0], exprs[1]); } break; @@ -1234,9 +778,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) compile_error(ctx, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2); return false; } - if (CanConstFold1(exprs[0])) - out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]); - else + if (!(out = fold_op(parser->fold, op, exprs))) out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); break; @@ -1246,20 +788,37 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ast_type_to_string(exprs[1], ty2, sizeof(ty2)); compile_error(ctx, "invalid types used in exponentiation: %s and %s", ty1, ty2); - return false; } - if (CanConstFold(exprs[0], exprs[1])) { - out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1))); - } else { - ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow")); + if (!(out = fold_op(parser->fold, op, exprs))) { + ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser->intrin, "pow")); vec_push(gencall->params, exprs[0]); vec_push(gencall->params, exprs[1]); out = (ast_expression*)gencall; } break; + case opid2('>', '<'): + if (NotSameType(TYPE_VECTOR)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in cross product: %s and %s", + ty1, ty2); + return false; + } + + if (!(out = fold_op(parser->fold, op, exprs))) { + out = fold_binary( + parser_ctx(parser), + VINSTR_CROSS, + exprs[0], + exprs[1] + ); + } + + break; + case opid3('<','=','>'): /* -1, 0, or 1 */ if (NotSameType(TYPE_FLOAT)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1270,14 +829,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } - if (CanConstFold(exprs[0], exprs[1])) { - if (ConstF(0) < ConstF(1)) - out = (ast_expression*)parser_const_float_neg1(parser); - else if (ConstF(0) == ConstF(1)) - out = (ast_expression*)parser_const_float_0(parser); - else if (ConstF(0) > ConstF(1)) - out = (ast_expression*)parser_const_float_1(parser); - } else { + if (!(out = fold_op(parser->fold, op, exprs))) { + /* This whole block is NOT fold_binary safe */ ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); eq->refs = AST_REF_NONE; @@ -1286,15 +839,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_ternary_new(ctx, (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]), /* out = -1 */ - (ast_expression*)parser_const_float_neg1(parser), + (ast_expression*)parser->fold->imm_float[2], /* } else { */ /* if (eq) { */ (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq, /* out = 0 */ - (ast_expression*)parser_const_float_0(parser), + (ast_expression*)parser->fold->imm_float[0], /* } else { */ /* out = 1 */ - (ast_expression*)parser_const_float_1(parser) + (ast_expression*)parser->fold->imm_float[1] /* } */ ) /* } */ @@ -1317,7 +870,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) type_name[exprs[1]->vtype]); return false; } - out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + if (!(out = fold_op(parser->fold, op, exprs))) + out = fold_binary(ctx, generated_op, exprs[0], exprs[1]); break; case opid2('!', '='): if (exprs[0]->vtype != exprs[1]->vtype) { @@ -1326,7 +880,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) type_name[exprs[1]->vtype]); return false; } - out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]); + if (!(out = fold_op(parser->fold, op, exprs))) + out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]); break; case opid2('=', '='): if (exprs[0]->vtype != exprs[1]->vtype) { @@ -1335,7 +890,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) type_name[exprs[1]->vtype]); return false; } - out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]); + if (!(out = fold_op(parser->fold, op, exprs))) + out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]); break; case opid1('='): @@ -1396,9 +952,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); + /* When we're a vector of part of an entity field we use STOREP */ + if (ast_istype(exprs[0], ast_member) && ast_istype(((ast_member*)exprs[0])->owner, ast_entfield)) + assignop = INSTR_STOREP_F; out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); break; case opid3('+','+','P'): @@ -1413,17 +970,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) addop = INSTR_ADD_F; else addop = INSTR_SUB_F; - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ast_ctx(exprs[0]), exprs[0]); if (ast_istype(exprs[0], ast_entfield)) { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, exprs[0], - (ast_expression*)parser_const_float_1(parser)); + (ast_expression*)parser->fold->imm_float[1]); } else { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop, exprs[0], - (ast_expression*)parser_const_float_1(parser)); + (ast_expression*)parser->fold->imm_float[1]); } break; case opid3('S','+','+'): @@ -1441,23 +996,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) addop = INSTR_SUB_F; subop = INSTR_ADD_F; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ast_ctx(exprs[0]), exprs[0]); if (ast_istype(exprs[0], ast_entfield)) { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, exprs[0], - (ast_expression*)parser_const_float_1(parser)); + (ast_expression*)parser->fold->imm_float[1]); } else { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop, exprs[0], - (ast_expression*)parser_const_float_1(parser)); + (ast_expression*)parser->fold->imm_float[1]); } if (!out) return false; - out = (ast_expression*)ast_binary_new(ctx, subop, - out, - (ast_expression*)parser_const_float_1(parser)); + out = fold_binary(ctx, subop, + out, + (ast_expression*)parser->fold->imm_float[1]); + break; case opid2('+','='): case opid2('-','='): @@ -1470,9 +1024,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else @@ -1507,9 +1059,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else @@ -1525,14 +1075,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF, exprs[0], exprs[1]); } else { - /* there's no DIV_VF */ - if (CanConstFold1(exprs[1])) { - out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1)); - } else { - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, - (ast_expression*)parser_const_float_1(parser), - exprs[1]); - } + out = fold_binary(ctx, INSTR_DIV_F, + (ast_expression*)parser->fold->imm_float[1], + exprs[1]); if (!out) { compile_error(ctx, "internal error: failed to generate division"); return false; @@ -1550,30 +1095,34 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid2('&','='): case opid2('|','='): - if (NotSameType(TYPE_FLOAT)) { + case opid2('^','='): + if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); compile_error(ctx, "invalid types used in expression: %s and %s", ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else assignop = type_store_instr[exprs[0]->vtype]; - out = (ast_expression*)ast_binstore_new(ctx, assignop, - (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR), - exprs[0], exprs[1]); + if (exprs[0]->vtype == TYPE_FLOAT) + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR), + exprs[0], exprs[1]); + else + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('^','=') ? VINSTR_BITXOR_V : op->id == opid2('&','=') ? VINSTR_BITAND_V : VINSTR_BITOR_V), + exprs[0], exprs[1]); break; case opid3('&','~','='): /* This is like: a &= ~(b); * But QC has no bitwise-not, so we implement it as * a -= a & (b); */ - if (NotSameType(TYPE_FLOAT)) { + if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); compile_error(ctx, "invalid types used in expression: %s and %s", @@ -1584,33 +1133,53 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) assignop = type_storep_instr[exprs[0]->vtype]; else assignop = type_store_instr[exprs[0]->vtype]; - out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]); + if (exprs[0]->vtype == TYPE_FLOAT) + out = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]); + else + out = fold_binary(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]); if (!out) return false; - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } - asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out); + (void)check_write_to(ctx, exprs[0]); + if (exprs[0]->vtype == TYPE_FLOAT) + asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out); + else + asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_V, exprs[0], out); asbinstore->keep_dest = true; out = (ast_expression*)asbinstore; break; + case opid3('l', 'e', 'n'): + if (exprs[0]->vtype != TYPE_STRING && exprs[0]->vtype != TYPE_ARRAY) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "invalid type for length operator: %s", ty1); + return false; + } + /* strings must be const, arrays are statically sized */ + if (exprs[0]->vtype == TYPE_STRING && + !(((ast_value*)exprs[0])->hasvalue && ((ast_value*)exprs[0])->cvq == CV_CONST)) + { + compile_error(ast_ctx(exprs[0]), "operand of length operator not a valid constant expression"); + return false; + } + out = fold_op(parser->fold, op, exprs); + break; + case opid2('~', 'P'): - if (exprs[0]->vtype != TYPE_FLOAT) { + if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1); return false; } - - if(CanConstFold1(exprs[0])) - out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0)); - else - out = (ast_expression*) - ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); + if (!(out = fold_op(parser->fold, op, exprs))) { + if (exprs[0]->vtype == TYPE_FLOAT) { + out = fold_binary(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]); + } else { + out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]); + } + } break; } #undef NotSameType - if (!out) { compile_error(ctx, "failed to apply operator %s", op->op); return false; @@ -1629,6 +1198,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) size_t fid; size_t paramcount, i; + bool fold = true; fid = vec_last(sy->ops).off; vec_shrinkby(sy->ops, 1); @@ -1652,9 +1222,11 @@ static bool parser_close_call(parser_t *parser, shunt *sy) return false; } - fun = sy->out[fid].out; - - if (fun == intrinsic_debug_typestring) { + /* + * TODO handle this at the intrinsic level with an ast_intrinsic + * node and codegen. + */ + if ((fun = sy->out[fid].out) == intrin_debug_typestring(parser->intrin)) { char ty[1024]; if (fid+2 != vec_size(sy->out) || vec_last(sy->out).block) @@ -1665,18 +1237,63 @@ static bool parser_close_call(parser_t *parser, shunt *sy) ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty)); ast_unref(vec_last(sy->out).out); sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out), - (ast_expression*)parser_const_string(parser, ty, false)); + (ast_expression*)fold_constgen_string(parser->fold, ty, false)); vec_shrinkby(sy->out, 1); return true; } + /* + * Now we need to determine if the function that is being called is + * an intrinsic so we can evaluate if the arguments to it are constant + * and than fruitfully fold them. + */ +#define fold_can_1(X) \ + (ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \ + ((ast_expression*)(X))->vtype != TYPE_FUNCTION) + + if (fid + 1 < vec_size(sy->out)) + ++paramcount; + + for (i = 0; i < paramcount; ++i) { + if (!fold_can_1((ast_value*)sy->out[fid + 1 + i].out)) { + fold = false; + break; + } + } + + /* + * All is well which ends well, if we make it into here we can ignore the + * intrinsic call and just evaluate it i.e constant fold it. + */ + if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->intrinsic) { + ast_expression **exprs = NULL; + ast_expression *foldval = NULL; + + for (i = 0; i < paramcount; i++) + vec_push(exprs, sy->out[fid+1 + i].out); + + if (!(foldval = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) { + vec_free(exprs); + goto fold_leave; + } + + /* + * Blub: what sorts of unreffing and resizing of + * sy->out should I be doing here? + */ + sy->out[fid] = syexp(foldval->node.context, foldval); + vec_shrinkby(sy->out, paramcount); + vec_free(exprs); + + return true; + } + + fold_leave: call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun); + if (!call) return false; - if (fid+1 < vec_size(sy->out)) - ++paramcount; - if (fid+1 + paramcount != vec_size(sy->out)) { parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu", (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out)); @@ -1695,7 +1312,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) if ((fun->flags & AST_FLAG_VARIADIC) && !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin)) { - call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount); + call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount, false); } } @@ -1823,7 +1440,7 @@ static ast_expression* parse_vararg_do(parser_t *parser) ast_expression *idx, *out; ast_value *typevar; ast_value *funtype = parser->function->vtype; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); if (!parser->function->varargs) { parseerror(parser, "function has no variable argument list"); @@ -1860,7 +1477,7 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; } - typevar = parse_typename(parser, NULL, NULL); + typevar = parse_typename(parser, NULL, NULL, NULL); if (!typevar) { ast_unref(idx); return NULL; @@ -1905,8 +1522,7 @@ static ast_expression* parse_vararg(parser_t *parser) } /* not to be exposed */ -extern bool ftepp_predef_exists(const char *name); - +bool ftepp_predef_exists(const char *name); static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) { if (OPTS_FLAG(TRANSLATABLE_STRINGS) && @@ -1926,7 +1542,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) parseerror(parser, "expected a constant string in translatable-string extension"); return false; } - val = parser_const_string(parser, parser_tokval(parser), true); + val = (ast_value*)fold_constgen_string(parser->fold, parser_tokval(parser), true); if (!val) return false; vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); @@ -1951,35 +1567,31 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) return true; } else if (parser->tok == TOKEN_FLOATCONST) { - ast_value *val; - val = parser_const_float(parser, (parser_token(parser)->constval.f)); + ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f), false); if (!val) return false; - vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + vec_push(sy->out, syexp(parser_ctx(parser), val)); return true; } else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) { - ast_value *val; - val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); + ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i), false); if (!val) return false; - vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + vec_push(sy->out, syexp(parser_ctx(parser), val)); return true; } else if (parser->tok == TOKEN_STRINGCONST) { - ast_value *val; - val = parser_const_string(parser, parser_tokval(parser), false); + ast_expression *val = fold_constgen_string(parser->fold, parser_tokval(parser), false); if (!val) return false; - vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + vec_push(sy->out, syexp(parser_ctx(parser), val)); return true; } else if (parser->tok == TOKEN_VECTORCONST) { - ast_value *val; - val = parser_const_vector(parser, parser_token(parser)->constval.v); + ast_expression *val = fold_constgen_vector(parser->fold, parser_token(parser)->constval.v); if (!val) return false; - vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + vec_push(sy->out, syexp(parser_ctx(parser), val)); return true; } else if (parser->tok == TOKEN_IDENT) @@ -1990,9 +1602,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) /* a_vector.{x,y,z} */ if (!vec_size(sy->ops) || !vec_last(sy->ops).etype || - operators[vec_last(sy->ops).etype-1].id != opid1('.') || - (prev >= intrinsic_debug_typestring && - prev <= intrinsic_debug_typestring)) + operators[vec_last(sy->ops).etype-1].id != opid1('.')) { /* When adding more intrinsics, fix the above condition */ prev = NULL; @@ -2014,20 +1624,34 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) } } if (!var && !strcmp(parser_tokval(parser), "__FUNC__")) - var = (ast_expression*)parser_const_string(parser, parser->function->name, false); + var = (ast_expression*)fold_constgen_string(parser->fold, parser->function->name, false); if (!var) { - /* intrinsics */ - if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { - var = (ast_expression*)intrinsic_debug_typestring; - } - /* now we try for the real intrinsic hashtable. If the string + /* + * now we try for the real intrinsic hashtable. If the string * begins with __builtin, we simply skip past it, otherwise we * use the identifier as is. */ - else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { - var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */); + if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { + var = intrin_func(parser->intrin, parser_tokval(parser)); + } + + /* + * Try it again, intrin_func deals with the alias method as well + * the first one masks for __builtin though, we emit warning here. + */ + if (!var) { + if ((var = intrin_func(parser->intrin, parser_tokval(parser)))) { + (void)!compile_warning( + parser_ctx(parser), + WARN_BUILTINS, + "using implicitly defined builtin `__builtin_%s' for `%s'", + parser_tokval(parser), + parser_tokval(parser) + ); + } } + if (!var) { char *correct = NULL; size_t i; @@ -2101,7 +1725,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma /* only warn once about an assignment in a truth value because the current code * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part */ - bool warn_truthvalue = true; + bool warn_parenthesis = true; /* count the parens because an if starts with one, so the * end of a condition is an unmatched closing paren @@ -2136,7 +1760,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } } if (o == operator_count) { - compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser)); + compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser)); goto onerr; } /* found an operator */ @@ -2164,6 +1788,17 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) olast = &operators[vec_last(sy.ops).etype-1]; + /* first only apply higher precedences, assoc_left+equal comes after we warn about precedence rules */ + while (olast && op->prec < olast->prec) + { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) + olast = &operators[vec_last(sy.ops).etype-1]; + else + olast = NULL; + } + #define IsAssignOp(x) (\ (x) == opid1('=') || \ (x) == opid2('+','=') || \ @@ -2175,14 +1810,29 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma (x) == opid2('|','=') || \ (x) == opid3('&','~','=') \ ) - if (warn_truthvalue) { + if (warn_parenthesis) { if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) || (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) || (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id)) ) { (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value"); - warn_truthvalue = false; + warn_parenthesis = false; + } + + if (olast && olast->id != op->id) { + if ((op->id == opid1('&') || op->id == opid1('|') || op->id == opid1('^')) && + (olast->id == opid1('&') || olast->id == opid1('|') || olast->id == opid1('^'))) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around bitwise operations"); + warn_parenthesis = false; + } + else if ((op->id == opid2('&','&') || op->id == opid2('|','|')) && + (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around logical operations"); + warn_parenthesis = false; + } } } @@ -2314,7 +1964,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma { char *newstr = NULL; util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser)); - vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false); + vec_last(sy.out).out = (ast_expression*)fold_constgen_string(parser->fold, newstr, false); mem_d(newstr); concatenated = true; } @@ -2344,7 +1994,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma parser->lex->flags.noops = true; if (vec_size(sy.out) != 1) { - parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out)); + parseerror(parser, "expression expected"); expr = NULL; } else expr = sy.out[0].out; @@ -2538,7 +2188,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) ast_expression *cond, *ontrue = NULL, *onfalse = NULL; bool ifnot = false; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -2677,7 +2327,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression ** bool ifnot = false; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -2778,7 +2428,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression bool ifnot = false; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -2903,7 +2553,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou bool ifnot = false; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); parser_enterblock(parser); @@ -2938,16 +2588,22 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou initexpr = parse_expression_leave(parser, false, false, false); if (!initexpr) goto onerr; - } - /* move on to condition */ - if (parser->tok != ';') { - parseerror(parser, "expected semicolon after for-loop initializer"); - goto onerr; - } - if (!parser_next(parser)) { - parseerror(parser, "expected for-loop condition"); - goto onerr; + /* move on to condition */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + } else { + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } } /* parse the condition */ @@ -2956,7 +2612,6 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou if (!cond) goto onerr; } - /* move on to incrementor */ if (parser->tok != ';') { parseerror(parser, "expected semicolon after for-loop initializer"); @@ -2969,7 +2624,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou /* parse the incrementor */ if (parser->tok != ')') { - lex_ctx condctx = parser_ctx(parser); + lex_ctx_t condctx = parser_ctx(parser); increment = parse_expression_leave(parser, false, false, false); if (!increment) goto onerr; @@ -3021,7 +2676,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou ast_value *retval = parser->function->return_value; ast_value *expected = parser->function->vtype; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -3120,7 +2775,7 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express { size_t i; unsigned int levels = 0; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); const char **loops = (is_continue ? parser->continues : parser->breaks); (void)block; /* not touching */ @@ -3172,6 +2827,11 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express /* returns true when it was a variable qualifier, false otherwise! * on error, cvq is set to CV_WRONG */ +typedef struct { + const char *name; + size_t flag; +} attribute_t; + static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message) { bool had_const = false; @@ -3181,8 +2841,18 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * bool had_static = false; uint32_t flags = 0; - *cvq = CV_NONE; + static attribute_t attributes[] = { + { "noreturn", AST_FLAG_NORETURN }, + { "inline", AST_FLAG_INLINE }, + { "eraseable", AST_FLAG_ERASEABLE }, + { "accumulate", AST_FLAG_ACCUMULATE }, + { "last", AST_FLAG_FINAL_DECL } + }; + + *cvq = CV_NONE; + for (;;) { + size_t i; if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { had_attrib = true; /* parse an attribute */ @@ -3191,15 +2861,25 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * *cvq = CV_WRONG; return false; } - if (!strcmp(parser_tokval(parser), "noreturn")) { - flags |= AST_FLAG_NORETURN; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; + + for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) { + if (!strcmp(parser_tokval(parser), attributes[i].name)) { + flags |= attributes[i].flag; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`%s` attribute has no parameters, expected `]]`", + attributes[i].name); + *cvq = CV_WRONG; + return false; + } + break; } } - else if (!strcmp(parser_tokval(parser), "noref")) { + + if (i != GMQCC_ARRAY_COUNT(attributes)) + goto leave; + + + if (!strcmp(parser_tokval(parser), "noref")) { had_noref = true; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); @@ -3207,14 +2887,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } - else if (!strcmp(parser_tokval(parser), "inline")) { - flags |= AST_FLAG_INLINE; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; - } - } else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { flags |= AST_FLAG_ALIAS; *message = NULL; @@ -3296,6 +2968,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) { + flags |= AST_FLAG_COVERAGE; + if (!parser_next(parser)) { + error_in_coverage: + parseerror(parser, "parse error in coverage attribute"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok == '(') { + if (!parser_next(parser)) { + bad_coverage_arg: + parseerror(parser, "invalid parameter for coverage() attribute\n" + "valid are: block"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok != ')') { + do { + if (parser->tok != TOKEN_IDENT) + goto bad_coverage_arg; + if (!strcmp(parser_tokval(parser), "block")) + flags |= AST_FLAG_BLOCK_COVERAGE; + else if (!strcmp(parser_tokval(parser), "none")) + flags &= ~(AST_FLAG_COVERAGE_MASK); + else + goto bad_coverage_arg; + if (!parser_next(parser)) + goto error_in_coverage; + if (parser->tok == ',') { + if (!parser_next(parser)) + goto error_in_coverage; + } + } while (parser->tok != ')'); + } + if (parser->tok != ')' || !parser_next(parser)) + goto error_in_coverage; + } else { + /* without parameter [[coverage]] equals [[coverage(block)]] */ + flags |= AST_FLAG_BLOCK_COVERAGE; + } + } else { /* Skip tokens until we hit a ]] */ @@ -3324,6 +3037,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * } else break; + + leave: if (!parser_next(parser)) goto onerr; } @@ -3407,7 +3122,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * bool noref, is_static; uint32_t qflags = 0; - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); (void)block; /* not touching */ (void)opval; @@ -3451,7 +3166,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); if (typevar || parser->tok == TOKEN_TYPENAME) { - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) { + if (!parse_variable(parser, block, true, CV_NONE, typevar, false, false, 0, NULL)) { ast_delete(switchnode); return false; } @@ -3463,7 +3178,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) { + if (!parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, NULL)) { ast_delete(switchnode); return false; } @@ -3708,7 +3423,13 @@ static bool parse_pragma_do(parser_t *parser) else { (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); - return false; + + /* skip to eol */ + while (!parse_eol(parser)) { + parser_next(parser); + } + + return true; } return true; @@ -3746,7 +3467,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); - if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { /* local variable */ if (!block) { @@ -3765,7 +3486,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring); + return parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_KEYWORD) { @@ -3919,7 +3640,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else { - lex_ctx ctx = parser_ctx(parser); + lex_ctx_t ctx = parser_ctx(parser); ast_expression *exp = parse_expression(parser, false, false); if (!exp) return false; @@ -3936,7 +3657,7 @@ static bool parse_enum(parser_t *parser) { bool flag = false; bool reverse = false; - qcfloat num = 0; + qcfloat_t num = 0; ast_value **values = NULL; ast_value *var = NULL; ast_value *asvalue; @@ -4318,85 +4039,109 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } if (has_frame_think) { - lex_ctx ctx; - ast_expression *self_frame; - ast_expression *self_nextthink; - ast_expression *self_think; - ast_expression *time_plus_1; - ast_store *store_frame; - ast_store *store_nextthink; - ast_store *store_think; - - ctx = parser_ctx(parser); - self_frame = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame); - self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink); - self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think); - - time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, - gbl_time, (ast_expression*)parser_const_float(parser, 0.1)); - - if (!self_frame || !self_nextthink || !self_think || !time_plus_1) { - if (self_frame) ast_delete(self_frame); - if (self_nextthink) ast_delete(self_nextthink); - if (self_think) ast_delete(self_think); - if (time_plus_1) ast_delete(time_plus_1); - retval = false; - } - - if (retval) - { - store_frame = ast_store_new(ctx, INSTR_STOREP_F, self_frame, framenum); - store_nextthink = ast_store_new(ctx, INSTR_STOREP_F, self_nextthink, time_plus_1); - store_think = ast_store_new(ctx, INSTR_STOREP_FNC, self_think, nextthink); - - if (!store_frame) { - ast_delete(self_frame); - retval = false; - } - if (!store_nextthink) { - ast_delete(self_nextthink); - retval = false; - } - if (!store_think) { - ast_delete(self_think); - retval = false; + if (!OPTS_FLAG(EMULATE_STATE)) { + ast_state *state_op = ast_state_new(parser_ctx(parser), framenum, nextthink); + if (!ast_block_add_expr(block, (ast_expression*)state_op)) { + parseerror(parser, "failed to generate state op for [frame,think]"); + ast_unref(nextthink); + ast_unref(framenum); + ast_delete(block); + return false; } - if (!retval) { - if (store_frame) ast_delete(store_frame); - if (store_nextthink) ast_delete(store_nextthink); - if (store_think) ast_delete(store_think); + } else { + /* emulate OP_STATE in code: */ + lex_ctx_t ctx; + ast_expression *self_frame; + ast_expression *self_nextthink; + ast_expression *self_think; + ast_expression *time_plus_1; + ast_store *store_frame; + ast_store *store_nextthink; + ast_store *store_think; + + float frame_delta = 1.0f / (float)OPTS_OPTION_U32(OPTION_STATE_FPS); + + ctx = parser_ctx(parser); + self_frame = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame); + self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink); + self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think); + + time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, + gbl_time, (ast_expression*)fold_constgen_float(parser->fold, frame_delta, false)); + + if (!self_frame || !self_nextthink || !self_think || !time_plus_1) { + if (self_frame) ast_delete(self_frame); + if (self_nextthink) ast_delete(self_nextthink); + if (self_think) ast_delete(self_think); + if (time_plus_1) ast_delete(time_plus_1); retval = false; } - if (!ast_block_add_expr(block, (ast_expression*)store_frame) || - !ast_block_add_expr(block, (ast_expression*)store_nextthink) || - !ast_block_add_expr(block, (ast_expression*)store_think)) + + if (retval) { - retval = false; + store_frame = ast_store_new(ctx, INSTR_STOREP_F, self_frame, framenum); + store_nextthink = ast_store_new(ctx, INSTR_STOREP_F, self_nextthink, time_plus_1); + store_think = ast_store_new(ctx, INSTR_STOREP_FNC, self_think, nextthink); + + if (!store_frame) { + ast_delete(self_frame); + retval = false; + } + if (!store_nextthink) { + ast_delete(self_nextthink); + retval = false; + } + if (!store_think) { + ast_delete(self_think); + retval = false; + } + if (!retval) { + if (store_frame) ast_delete(store_frame); + if (store_nextthink) ast_delete(store_nextthink); + if (store_think) ast_delete(store_think); + retval = false; + } + if (!ast_block_add_expr(block, (ast_expression*)store_frame) || + !ast_block_add_expr(block, (ast_expression*)store_nextthink) || + !ast_block_add_expr(block, (ast_expression*)store_think)) + { + retval = false; + } } - } - if (!retval) { - parseerror(parser, "failed to generate code for [frame,think]"); - ast_unref(nextthink); - ast_unref(framenum); - ast_delete(block); - return false; + if (!retval) { + parseerror(parser, "failed to generate code for [frame,think]"); + ast_unref(nextthink); + ast_unref(framenum); + ast_delete(block); + return false; + } } } if (var->hasvalue) { - parseerror(parser, "function `%s` declared with multiple bodies", var->name); - ast_block_delete(block); - goto enderr; - } + if (!(var->expression.flags & AST_FLAG_ACCUMULATE)) { + parseerror(parser, "function `%s` declared with multiple bodies", var->name); + ast_block_delete(block); + goto enderr; + } + func = var->constval.vfunc; - func = ast_function_new(ast_ctx(var), var->name, var); - if (!func) { - parseerror(parser, "failed to allocate function for `%s`", var->name); - ast_block_delete(block); - goto enderr; + if (!func) { + parseerror(parser, "internal error: NULL function: `%s`", var->name); + ast_block_delete(block); + goto enderr; + } + } else { + func = ast_function_new(ast_ctx(var), var->name, var); + + if (!func) { + parseerror(parser, "failed to allocate function for `%s`", var->name); + ast_block_delete(block); + goto enderr; + } + vec_push(parser->functions, func); } - vec_push(parser->functions, func); parser_enterblock(parser); @@ -4423,13 +4168,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } - if (var->argcounter) { + if (var->argcounter && !func->argc) { ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT); parser_addlocal(parser, argc->name, (ast_expression*)argc); func->argc = argc; } - if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) { + if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC && !func->varargs) { char name[1024]; ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY); varargs->expression.flags |= AST_FLAG_IS_VARARG; @@ -4447,9 +4192,8 @@ static bool parse_function_body(parser_t *parser, ast_value *var) ast_block_delete(block); goto enderrfn; } - func->varargs = varargs; - - func->fixedparams = parser_const_float(parser, vec_size(var->expression.params)); + func->varargs = varargs; + func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params), false); } parser->function = func; @@ -4460,7 +4204,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var) vec_push(func->blocks, block); - parser->function = old; if (!parser_leaveblock(parser)) retval = false; @@ -4498,7 +4241,7 @@ static ast_expression *array_accessor_split( ast_ifthen *ifthen; ast_binary *cmp; - lex_ctx ctx = ast_ctx(array); + lex_ctx_t ctx = ast_ctx(array); if (!left || !right) { if (left) ast_delete(left); @@ -4508,7 +4251,7 @@ static ast_expression *array_accessor_split( cmp = ast_binary_new(ctx, INSTR_LT, (ast_expression*)index, - (ast_expression*)parser_const_float(parser, middle)); + (ast_expression*)fold_constgen_float(parser->fold, middle, false)); if (!cmp) { ast_delete(left); ast_delete(right); @@ -4528,7 +4271,7 @@ static ast_expression *array_accessor_split( static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast_value *index, ast_value *value, size_t from, size_t afterend) { - lex_ctx ctx = ast_ctx(array); + lex_ctx_t ctx = ast_ctx(array); if (from+1 == afterend) { /* set this value */ @@ -4541,7 +4284,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) assignop = INSTR_STORE_V; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4593,7 +4336,7 @@ static ast_expression *array_field_setter_node( size_t from, size_t afterend) { - lex_ctx ctx = ast_ctx(array); + lex_ctx_t ctx = ast_ctx(array); if (from+1 == afterend) { /* set this value */ @@ -4607,7 +4350,7 @@ static ast_expression *array_field_setter_node( if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) assignop = INSTR_STOREP_V; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4664,13 +4407,13 @@ static ast_expression *array_field_setter_node( static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast_value *index, size_t from, size_t afterend) { - lex_ctx ctx = ast_ctx(array); + lex_ctx_t ctx = ast_ctx(array); if (from+1 == afterend) { ast_return *ret; ast_array_index *subscript; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4702,6 +4445,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con parseerror(parser, "failed to create accessor function value"); return false; } + fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); func = ast_function_new(ast_ctx(array), funcname, fval); if (!func) { @@ -4897,10 +4641,9 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const return parser_create_array_getter_impl(parser, array); } -static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) { - lex_ctx ctx; + lex_ctx_t ctx; size_t i; ast_value **params; ast_value *param; @@ -4923,6 +4666,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) /* parse variables until we hit a closing paren */ while (parser->tok != ')') { + bool is_varargs = false; + if (!first) { /* there must be commas between them */ if (parser->tok != ',') { @@ -4936,10 +4681,13 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } first = false; - if (parser->tok == TOKEN_DOTS) { + param = parse_typename(parser, NULL, NULL, &is_varargs); + if (!param && !is_varargs) + goto on_error; + if (is_varargs) { /* '...' indicates a varargs function */ variadic = true; - if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) { + if (parser->tok != ')' && parser->tok != TOKEN_IDENT) { parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); goto on_error; } @@ -4950,13 +4698,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) goto on_error; } } - } - else - { - /* for anything else just parse a typename */ - param = parse_typename(parser, NULL, NULL); - if (!param) - goto on_error; + } else { vec_push(params, param); if (param->expression.vtype >= TYPE_VARIANT) { char tname[1024]; /* typename is reserved in C++ */ @@ -4975,12 +4717,17 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } if (parser->tok == TOKEN_IDENT) { argcounter = util_strdup(parser_tokval(parser)); + ast_value_set_name(param, argcounter); if (!parser_next(parser) || parser->tok != ')') { parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); goto on_error; } } } + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC && param->name[0] == '<') { + parseerror(parser, "parameter name omitted"); + goto on_error; + } } } @@ -5027,7 +4774,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) { ast_expression *cexp; ast_value *cval, *tmp; - lex_ctx ctx; + lex_ctx_t ctx; ctx = parser_ctx(parser); @@ -5096,6 +4843,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) * The base type makes up every bit of type information which comes *before* the * variable name. * + * NOTE: The value must either be named, have a NULL name, or a name starting + * with '<'. In the first case, this will be the actual variable or type + * name, in the other cases it is assumed that the name will appear + * later, and an error is generated otherwise. + * * The following will be parsed in its entirety: * void() foo() * The 'basetype' in this case is 'void()' @@ -5103,21 +4855,25 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) * void() foo(), bar * then the type-information 'void()' can be stored in 'storebase' */ -static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef) +static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg) { ast_value *var, *tmp; - lex_ctx ctx; + lex_ctx_t ctx; const char *name = NULL; bool isfield = false; bool wasarray = false; size_t morefields = 0; + bool vararg = (parser->tok == TOKEN_DOTS); + ctx = parser_ctx(parser); /* types may start with a dot */ - if (parser->tok == '.') { + if (parser->tok == '.' || parser->tok == TOKEN_DOTS) { isfield = true; + if (parser->tok == TOKEN_DOTS) + morefields += 2; /* if we parsed a dot we need a typename now */ if (!parser_next(parser)) { parseerror(parser, "expected typename for field definition"); @@ -5127,8 +4883,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va /* Further dots are handled seperately because they won't be part of the * basetype */ - while (parser->tok == '.') { - ++morefields; + while (true) { + if (parser->tok == '.') + ++morefields; + else if (parser->tok == TOKEN_DOTS) + morefields += 3; + else + break; + vararg = false; if (!parser_next(parser)) { parseerror(parser, "expected typename for field definition"); return NULL; @@ -5138,6 +4900,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va if (parser->tok == TOKEN_IDENT) cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0); if (!cached_typedef && parser->tok != TOKEN_TYPENAME) { + if (vararg && is_vararg) { + *is_vararg = true; + return NULL; + } parseerror(parser, "expected typename"); return NULL; } @@ -5189,8 +4955,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } /* there may be a name now */ - if (parser->tok == TOKEN_IDENT) { + if (parser->tok == TOKEN_IDENT || parser->tok == TOKEN_KEYWORD) { + if (!strcmp(parser_tokval(parser), "break")) + (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); + else if (parser->tok == TOKEN_KEYWORD) + goto leave; + name = util_strdup(parser_tokval(parser)); + /* parse on */ if (!parser_next(parser)) { ast_delete(var); @@ -5200,6 +4972,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } } + leave: /* now this may be an array */ if (parser->tok == '[') { wasarray = true; @@ -5251,11 +5024,18 @@ static bool parse_typedef(parser_t *parser) ast_value *typevar, *oldtype; ast_expression *old; - typevar = parse_typename(parser, NULL, NULL); + typevar = parse_typename(parser, NULL, NULL, NULL); if (!typevar) return false; + /* while parsing types, the ast_value's get named '' */ + if (!typevar->name || typevar->name[0] == '<') { + parseerror(parser, "missing name in typedef"); + ast_delete(typevar); + return false; + } + if ( (old = parser_find_var(parser, typevar->name)) ) { parseerror(parser, "cannot define a type with the same name as a variable: %s\n" " -> `%s` has been declared here: %s:%i", @@ -5409,18 +5189,27 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield bool wasarray = false; ast_member *me[3] = { NULL, NULL, NULL }; + ast_member *last_me[3] = { NULL, NULL, NULL }; if (!localblock && is_static) parseerror(parser, "`static` qualifier is not supported in global scope"); /* get the first complete variable */ - var = parse_typename(parser, &basetype, cached_typedef); + var = parse_typename(parser, &basetype, cached_typedef, NULL); if (!var) { if (basetype) ast_delete(basetype); return false; } + /* while parsing types, the ast_value's get named '' */ + if (!var->name || var->name[0] == '<') { + parseerror(parser, "declaration does not declare anything"); + if (basetype) + ast_delete(basetype); + return false; + } + while (true) { proto = NULL; wasarray = false; @@ -5460,6 +5249,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } var->cvq = qualifier; + if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */ + var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); var->expression.flags |= qflags; /* @@ -5470,6 +5261,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield var->expression.flags & AST_FLAG_ALIAS) var->desc = vstring; + if (parser_find_global(parser, var->name) && var->expression.flags & AST_FLAG_ALIAS) { + parseerror(parser, "function aliases cannot be forward declared"); + retval = false; + goto cleanup; + } + + /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) * Also: if there was a prototype, `var` will be deleted and set to `proto` which @@ -5582,6 +5380,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + if (old->flags & AST_FLAG_FINAL_DECL) { + parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } proto = (ast_value*)old; if (!ast_istype(old, ast_value)) { parseerror(parser, "internal error: not an ast_value"); @@ -5595,6 +5399,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto->expression.flags |= var->expression.flags; + /* copy the context for finals, + * so the error can show where it was actually made 'final' + */ + if (proto->expression.flags & AST_FLAG_FINAL_DECL) + ast_ctx(old) = ast_ctx(var); ast_delete(var); var = proto; } @@ -5619,6 +5428,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + /* doing this here as the above is just for a single scope */ old = parser_find_local(parser, var->name, 0, &isparam); if (old && isparam) { if (parsewarning(parser, WARN_LOCAL_SHADOWS, @@ -5631,8 +5441,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); - var = NULL; - goto skipvar; + if (ast_istype(old, ast_value)) + var = proto = (ast_value*)old; + else { + var = NULL; + goto skipvar; + } } } } @@ -5687,7 +5501,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield return false; } - if (var->expression.vtype != find->vtype) { + if (!ast_compare_type((ast_expression*)var, find)) { char ty1[1024]; char ty2[1024]; @@ -5761,6 +5575,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield */ char *defname = NULL; size_t prefix_len, ln; + size_t sn, sn_size; ln = strlen(parser->function->name); vec_append(defname, ln, parser->function->name); @@ -5782,6 +5597,24 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* now rename the global */ ln = strlen(var->name); vec_append(defname, ln, var->name); + /* if a variable of that name already existed, add the + * counter value. + * The counter is incremented either way. + */ + sn_size = vec_size(parser->function->static_names); + for (sn = 0; sn != sn_size; ++sn) { + if (strcmp(parser->function->static_names[sn], var->name) == 0) + break; + } + if (sn != sn_size) { + char *num = NULL; + int len = util_asprintf(&num, "#%u", parser->function->static_count); + vec_append(defname, len, num); + mem_d(num); + } + else + vec_push(parser->function->static_names, util_strdup(var->name)); + parser->function->static_count++; ast_value_set_name(var, defname); /* push it to the to-be-generated globals */ @@ -5820,6 +5653,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } } + memcpy(last_me, me, sizeof(me)); me[0] = me[1] = me[2] = NULL; cleanvar = false; /* Part 2.2 @@ -5892,16 +5726,8 @@ skipvar: if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) { if (parser->tok != '=') { - if (!strcmp(parser_tokval(parser), "break")) { - if (!parser_next(parser)) { - parseerror(parser, "error parsing break definition"); - break; - } - (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); - } else { - parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); - break; - } + parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); + break; } if (!parser_next(parser)) { @@ -6035,22 +5861,52 @@ skipvar: } else { ast_expression *cexp; ast_value *cval; + bool folded_const = false; cexp = parse_expression_leave(parser, true, false, false); if (!cexp) break; + cval = ast_istype(cexp, ast_value) ? (ast_value*)cexp : NULL; - if (!localblock) { - cval = (ast_value*)cexp; + /* deal with foldable constants: */ + if (localblock && + var->cvq == CV_CONST && cval && cval->hasvalue && cval->cvq == CV_CONST && !cval->isfield) + { + /* remove it from the current locals */ + if (isvector) { + for (i = 0; i < 3; ++i) { + vec_pop(parser->_locals); + vec_pop(localblock->collect); + } + } + /* do sanity checking, this function really needs refactoring */ + if (vec_last(parser->_locals) != (ast_expression*)var) + parseerror(parser, "internal error: unexpected change in local variable handling"); + else + vec_pop(parser->_locals); + if (vec_last(localblock->locals) != var) + parseerror(parser, "internal error: unexpected change in local variable handling (2)"); + else + vec_pop(localblock->locals); + /* push it to the to-be-generated globals */ + vec_push(parser->globals, (ast_expression*)var); + if (isvector) + for (i = 0; i < 3; ++i) + vec_push(parser->globals, (ast_expression*)last_me[i]); + folded_const = true; + } + + if (folded_const || !localblock || is_static) { if (cval != parser->nil && - (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) + (!cval || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) ) { - parseerror(parser, "cannot initialize a global constant variable with a non-constant expression"); + parseerror(parser, "initializer is non constant"); } else { - if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + if (!is_static && + !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && qualifier != CV_VAR) { var->cvq = CV_CONST; @@ -6090,6 +5946,13 @@ skipvar: vec_free(sy.argc); var->cvq = cvq; } + /* a constant initialized to an inexact value should be marked inexact: + * const float x = ; should propagate the inexact flag + */ + if (var->cvq == CV_CONST && var->expression.vtype == TYPE_FLOAT) { + if (cval && cval->hasvalue && cval->cvq == CV_CONST) + var->inexact = cval->inexact; + } } another: @@ -6154,7 +6017,7 @@ static bool parser_global_statement(parser_t *parser) if (parser->tok == TOKEN_IDENT) istype = parser_find_typedef(parser, parser_tokval(parser), 0); - if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL); } @@ -6162,7 +6025,7 @@ static bool parser_global_statement(parser_t *parser) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring); + return parse_variable(parser, NULL, false, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum")) { @@ -6277,7 +6140,7 @@ static void generate_checksum(parser_t *parser, ir_builder *ir) parser_t *parser_create() { parser_t *parser; - lex_ctx empty_ctx; + lex_ctx_t empty_ctx; size_t i; parser = (parser_t*)mem_a(sizeof(parser_t)); @@ -6293,7 +6156,7 @@ parser_t *parser_create() } } if (!parser->assign_op) { - printf("internal error: initializing parser: failed to find assign operator\n"); + con_err("internal error: initializing parser: failed to find assign operator\n"); mem_d(parser); return NULL; } @@ -6305,9 +6168,6 @@ parser_t *parser_create() parser->aliases = util_htnew(PARSER_HT_SIZE); - parser->ht_imm_string = util_htnew(512); - parser->ht_imm_string_dotranslate = util_htnew(512); - /* corrector */ vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); @@ -6336,6 +6196,8 @@ parser_t *parser_create() parser->reserved_version = NULL; } + parser->fold = fold_init (parser); + parser->intrin = intrin_init(parser); return parser; } @@ -6405,15 +6267,6 @@ static void parser_remove_ast(parser_t *parser) for (i = 0; i < vec_size(parser->functions); ++i) { ast_delete(parser->functions[i]); } - for (i = 0; i < vec_size(parser->imm_vector); ++i) { - ast_delete(parser->imm_vector[i]); - } - for (i = 0; i < vec_size(parser->imm_string); ++i) { - ast_delete(parser->imm_string[i]); - } - for (i = 0; i < vec_size(parser->imm_float); ++i) { - ast_delete(parser->imm_float[i]); - } for (i = 0; i < vec_size(parser->fields); ++i) { ast_delete(parser->fields[i]); } @@ -6422,11 +6275,6 @@ static void parser_remove_ast(parser_t *parser) } vec_free(parser->accessors); vec_free(parser->functions); - vec_free(parser->imm_vector); - vec_free(parser->imm_string); - util_htdel(parser->ht_imm_string_dotranslate); - util_htdel(parser->ht_imm_string); - vec_free(parser->imm_float); vec_free(parser->globals); vec_free(parser->fields); @@ -6463,12 +6311,13 @@ static void parser_remove_ast(parser_t *parser) ast_value_delete(parser->const_vec[0]); ast_value_delete(parser->const_vec[1]); ast_value_delete(parser->const_vec[2]); - + if (parser->reserved_version) ast_value_delete(parser->reserved_version); util_htdel(parser->aliases); - intrin_intrinsics_destroy(parser); + fold_cleanup(parser->fold); + intrin_cleanup(parser->intrin); } void parser_cleanup(parser_t *parser) @@ -6477,11 +6326,51 @@ void parser_cleanup(parser_t *parser) mem_d(parser); } +static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) { + size_t i; + ast_expression *expr; + ast_value *cov; + ast_function *func; + + if (!OPTS_OPTION_BOOL(OPTION_COVERAGE)) + return true; + + func = NULL; + for (i = 0; i != vec_size(parser->functions); ++i) { + if (!strcmp(parser->functions[i]->name, "coverage")) { + func = parser->functions[i]; + break; + } + } + if (!func) { + if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) { + con_out("coverage support requested but no coverage() builtin declared\n"); + ir_builder_delete(ir); + return false; + } + return true; + } + + cov = func->vtype; + expr = (ast_expression*)cov; + + if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) { + char ty[1024]; + ast_type_to_string(expr, ty, sizeof(ty)); + con_out("invalid type for coverage(): %s\n", ty); + ir_builder_delete(ir); + return false; + } + + ir->coverage_func = func->ir_func->value; + return true; +} + bool parser_finish(parser_t *parser, const char *output) { - size_t i; - ir_builder *ir; - bool retval = true; + size_t i; + ir_builder *ir; + bool retval = true; if (compile_errors) { con_out("*** there were compile errors\n"); @@ -6560,27 +6449,13 @@ bool parser_finish(parser_t *parser, const char *output) } } /* Now we can generate immediates */ - for (i = 0; i < vec_size(parser->imm_float); ++i) { - if (!ast_global_codegen(parser->imm_float[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_float[i]->name); - ir_builder_delete(ir); - return false; - } - } - for (i = 0; i < vec_size(parser->imm_string); ++i) { - if (!ast_global_codegen(parser->imm_string[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_string[i]->name); - ir_builder_delete(ir); - return false; - } - } - for (i = 0; i < vec_size(parser->imm_vector); ++i) { - if (!ast_global_codegen(parser->imm_vector[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_vector[i]->name); - ir_builder_delete(ir); - return false; - } - } + if (!fold_generate(parser->fold, ir)) + return false; + + /* before generating any functions we need to set the coverage_func */ + if (!parser_set_coverage_func(parser, ir)) + return false; + for (i = 0; i < vec_size(parser->globals); ++i) { ast_value *asvalue; if (!ast_istype(parser->globals[i], ast_value))