X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=49e965a097f0e5813023824e85062b38e3100da8;hp=f8805e24f43c0d481560d1cb9a8b18d7c734e981;hb=5d5b9a379ac6c4c2f6855b6a155216ab375795e4;hpb=23cb7f4e096e5cf6e08636a808820d480079db96 diff --git a/parser.c b/parser.c index f8805e2..4a09583 100644 --- a/parser.c +++ b/parser.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 + * Copyright (C) 2012, 2013, 2014 * Wolfgang Bumiller * Dale Weiler * @@ -24,92 +24,11 @@ #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 128 -#define TYPEDEF_HT_SIZE 16 - -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; - - /* 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; - - /* code generator */ - code_t *code; -} parser_t; - -static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; +#define PARSER_HT_SIZE 512 +#define TYPEDEF_HT_SIZE 512 static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); @@ -125,7 +44,7 @@ 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, ...) { @@ -146,42 +65,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char * 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 */ @@ -201,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 */ @@ -258,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) -{ - size_t hash = util_hthash(parser->ht_imm_string, str); - ast_value *out; - if ( (out = (ast_value*)util_htgeth(parser->ht_imm_string, str, hash)) ) { - if (dotranslate && out->name[0] == '#') { - char name[32]; - util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); - ast_value_set_name(out, name); - out->expression.flags |= AST_FLAG_INCLUDE_DEF; - } - 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(parser->ht_imm_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); @@ -340,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) @@ -402,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 */ @@ -412,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 { @@ -430,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; @@ -441,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; @@ -452,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; @@ -463,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; @@ -486,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; @@ -516,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]; @@ -571,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; } @@ -586,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('?',':')) && @@ -608,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: @@ -710,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 && @@ -850,142 +575,46 @@ 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)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); - compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2); + 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 tyeps %s and %s", ty1, ty2); - return false; - } break; case opid1('%'): @@ -994,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 */ @@ -1018,58 +643,95 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('|'): case opid1('&'): - if (NotSameType(TYPE_FLOAT)) { + case opid1('^'): + 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)) + { 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]); + + if (!(out = fold_op(parser->fold, op, exprs))) { + /* + * IF the first expression is float, the following will be too + * since scalar ^ vector is not allowed. + */ + 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 { + /* + * The first is a vector: vector is allowed to bitop with vector and + * with scalar, branch here for the second operand. + */ + 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 { + 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 opid1('^'): - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^"); - return false; 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)); @@ -1100,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; @@ -1116,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; @@ -1128,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)); @@ -1152,31 +829,25 @@ 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_binary_ref)false; /* references nothing */ + eq->refs = AST_REF_NONE; /* if (lt) { */ 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] /* } */ ) /* } */ @@ -1199,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) { @@ -1208,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) { @@ -1217,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('='): @@ -1278,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'): @@ -1295,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','+','+'): @@ -1323,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('-','='): @@ -1352,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 @@ -1389,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 @@ -1407,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; @@ -1432,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", @@ -1466,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; @@ -1511,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); @@ -1534,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) @@ -1547,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)); @@ -1568,7 +1303,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) for (i = 0; i < paramcount; ++i) vec_push(call->params, sy->out[fid+1 + i].out); vec_shrinkby(sy->out, paramcount); - (void)!ast_call_check_types(call); + (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam); if (parser->max_param_count < paramcount) parser->max_param_count = paramcount; @@ -1577,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); } } @@ -1690,6 +1425,8 @@ static bool parser_close_paren(parser_t *parser, shunt *sy) static void parser_reclassify_token(parser_t *parser) { size_t i; + if (parser->tok >= TOKEN_START) + return; for (i = 0; i < operator_count; ++i) { if (!strcmp(parser_tokval(parser), operators[i].op)) { parser->tok = TOKEN_OPERATOR; @@ -1703,8 +1440,12 @@ static ast_expression* parse_vararg_do(parser_t *parser) ast_expression *idx, *out; ast_value *typevar; ast_value *funtype = parser->function->vtype; + lex_ctx_t ctx = parser_ctx(parser); - lex_ctx ctx = parser_ctx(parser); + if (!parser->function->varargs) { + parseerror(parser, "function has no variable argument list"); + return NULL; + } if (!parser_next(parser) || parser->tok != '(') { parseerror(parser, "expected parameter index and type in parenthesis"); @@ -1720,9 +1461,14 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; if (parser->tok != ',') { - ast_unref(idx); - parseerror(parser, "expected comma after parameter index"); - return NULL; + if (parser->tok != ')') { + ast_unref(idx); + parseerror(parser, "expected comma after parameter index"); + return NULL; + } + /* vararg piping: ...(start) */ + out = (ast_expression*)ast_argpipe_new(ctx, idx); + return out; } if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) { @@ -1731,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; @@ -1744,22 +1490,6 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; } -#if 0 - if (!parser_next(parser)) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "parse error after vararg"); - return NULL; - } -#endif - - if (!parser->function->varargs) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "function has no variable argument list"); - return NULL; - } - if (funtype->expression.varparam && !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam)) { @@ -1792,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) && @@ -1813,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)); @@ -1838,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) @@ -1877,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; @@ -1901,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; @@ -1925,7 +1662,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * it in the predef table. And diagnose it better :) */ if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) { - parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); + parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); return false; } @@ -1944,7 +1681,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); if (strcmp(correct, parser_tokval(parser))) { break; - } else if (correct) { + } else { mem_d(correct); correct = NULL; } @@ -1952,12 +1689,12 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) correct_free(&corr); if (correct) { - parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + parseerror(parser, "unexpected identifier: %s (did you mean %s?)", parser_tokval(parser), correct); mem_d(correct); return false; } } - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + parseerror(parser, "unexpected identifier: %s", parser_tokval(parser)); return false; } } @@ -1988,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 @@ -2004,7 +1741,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma while (true) { if (parser->tok == TOKEN_TYPENAME) { - parseerror(parser, "unexpected typename"); + parseerror(parser, "unexpected typename `%s`", parser_tokval(parser)); goto onerr; } @@ -2023,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 */ @@ -2051,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('+','=') || \ @@ -2062,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; + } } } @@ -2201,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; } @@ -2231,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; @@ -2402,7 +2165,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, } unary = (ast_unary*)cond; - while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) + /* ast_istype dereferences cond, should test here for safety */ + while (cond && ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) { cond = unary->operand; unary->operand = NULL; @@ -2424,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 */ @@ -2563,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 */ @@ -2642,7 +2406,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { parseerror(parser, "internal error: label stack corrupted"); rv = false; - ast_delete(*out); + /* + * Test for NULL otherwise ast_delete dereferences null pointer + * and boom. + */ + if (*out) + ast_delete(*out); *out = NULL; } else { @@ -2659,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 */ @@ -2784,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); @@ -2850,7 +2619,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; @@ -2902,7 +2671,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 */ @@ -3001,7 +2770,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 */ @@ -3053,6 +2822,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; @@ -3062,8 +2836,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 */ @@ -3072,24 +2856,26 @@ 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; - } - } - else 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 `]]`"); - *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), "inline")) { - flags |= AST_FLAG_INLINE; + + 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 `]]`"); *cvq = CV_WRONG; @@ -3177,6 +2963,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 ]] */ @@ -3205,6 +3032,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * } else break; + + leave: if (!parser_next(parser)) goto onerr; } @@ -3288,7 +3117,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; @@ -3332,7 +3161,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; } @@ -3344,7 +3173,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; } @@ -3510,7 +3339,8 @@ static bool parse_goto(parser_t *parser, ast_expression **out) if (!(expression = parse_expression(parser, false, true)) || !(*out = parse_goto_computed(parser, &expression))) { parseerror(parser, "invalid goto expression"); - ast_unref(expression); + if(expression) + ast_unref(expression); return false; } @@ -3588,7 +3418,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; @@ -3626,7 +3462,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) { @@ -3645,7 +3481,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) { @@ -3799,7 +3635,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; @@ -3816,7 +3652,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; @@ -4198,85 +4034,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); @@ -4303,13 +4163,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; @@ -4327,9 +4187,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; @@ -4339,7 +4198,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } vec_push(func->blocks, block); - parser->function = old; if (!parser_leaveblock(parser)) @@ -4378,7 +4236,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); @@ -4388,7 +4246,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); @@ -4408,7 +4266,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 */ @@ -4421,7 +4279,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; @@ -4473,7 +4331,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 */ @@ -4487,7 +4345,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; @@ -4544,13 +4402,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; @@ -4582,6 +4440,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) { @@ -4777,10 +4636,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; @@ -4803,6 +4661,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 != ',') { @@ -4816,10 +4676,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; } @@ -4830,13 +4693,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++ */ @@ -4855,12 +4712,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; + } } } @@ -4907,7 +4769,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); @@ -4917,32 +4779,44 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) return NULL; } - cexp = parse_expression_leave(parser, true, false, false); + if (parser->tok != ']') { + cexp = parse_expression_leave(parser, true, false, false); - if (!cexp || !ast_istype(cexp, ast_value)) { - if (cexp) - ast_unref(cexp); - ast_delete(var); - parseerror(parser, "expected array-size as constant positive integer"); - return NULL; + if (!cexp || !ast_istype(cexp, ast_value)) { + if (cexp) + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "expected array-size as constant positive integer"); + return NULL; + } + cval = (ast_value*)cexp; + } + else { + cexp = NULL; + cval = NULL; } - cval = (ast_value*)cexp; tmp = ast_value_new(ctx, "", TYPE_ARRAY); tmp->expression.next = (ast_expression*)var; var = tmp; - if (cval->expression.vtype == TYPE_INTEGER) - tmp->expression.count = cval->constval.vint; - else if (cval->expression.vtype == TYPE_FLOAT) - tmp->expression.count = cval->constval.vfloat; - else { + if (cval) { + if (cval->expression.vtype == TYPE_INTEGER) + tmp->expression.count = cval->constval.vint; + else if (cval->expression.vtype == TYPE_FLOAT) + tmp->expression.count = cval->constval.vfloat; + else { + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "array-size must be a positive integer constant"); + return NULL; + } + ast_unref(cexp); - ast_delete(var); - parseerror(parser, "array-size must be a positive integer constant"); - return NULL; + } else { + var->expression.count = -1; + var->expression.flags |= AST_FLAG_ARRAY_INIT; } - ast_unref(cexp); if (parser->tok != ']') { ast_delete(var); @@ -4964,6 +4838,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()' @@ -4971,21 +4850,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"); @@ -4995,8 +4878,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; @@ -5006,6 +4895,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; } @@ -5057,8 +4950,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); @@ -5068,6 +4967,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; @@ -5119,11 +5019,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", @@ -5192,6 +5099,75 @@ static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, cons return true; } +static bool create_array_accessors(parser_t *parser, ast_value *var) +{ + char name[1024]; + util_snprintf(name, sizeof(name), "%s##SET", var->name); + if (!parser_create_array_setter(parser, var, name)) + return false; + util_snprintf(name, sizeof(name), "%s##GET", var->name); + if (!parser_create_array_getter(parser, var, var->expression.next, name)) + return false; + return true; +} + +static bool parse_array(parser_t *parser, ast_value *array) +{ + size_t i; + if (array->initlist) { + parseerror(parser, "array already initialized elsewhere"); + return false; + } + if (!parser_next(parser)) { + parseerror(parser, "parse error in array initializer"); + return false; + } + i = 0; + while (parser->tok != '}') { + ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false); + if (!v) + return false; + if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) { + ast_unref(v); + parseerror(parser, "initializing element must be a compile time constant"); + return false; + } + vec_push(array->initlist, v->constval); + if (v->expression.vtype == TYPE_STRING) { + array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring); + ++i; + } + ast_unref(v); + if (parser->tok == '}') + break; + if (parser->tok != ',' || !parser_next(parser)) { + parseerror(parser, "expected comma or '}' in element list"); + return false; + } + } + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after initializer, got %s"); + return false; + } + /* + if (!parser_next(parser)) { + parseerror(parser, "parse error after initializer"); + return false; + } + */ + + if (array->expression.flags & AST_FLAG_ARRAY_INIT) { + if (array->expression.count != (size_t)-1) { + parseerror(parser, "array `%s' has already been initialized with %u elements", + array->name, (unsigned)array->expression.count); + } + array->expression.count = vec_size(array->initlist); + if (!create_array_accessors(parser, array)) + return false; + } + return true; +} + static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring) { ast_value *var; @@ -5208,18 +5184,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; @@ -5259,6 +5244,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; /* @@ -5269,6 +5256,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 @@ -5381,6 +5375,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"); @@ -5394,6 +5394,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; } @@ -5418,6 +5423,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, @@ -5430,8 +5436,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; + } } } } @@ -5486,7 +5496,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]; @@ -5560,6 +5570,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); @@ -5581,6 +5592,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 */ @@ -5619,19 +5648,17 @@ 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 * deal with arrays */ if (var->expression.vtype == TYPE_ARRAY) { - char name[1024]; - util_snprintf(name, sizeof(name), "%s##SET", var->name); - if (!parser_create_array_setter(parser, var, name)) - goto cleanup; - util_snprintf(name, sizeof(name), "%s##GET", var->name); - if (!parser_create_array_getter(parser, var, var->expression.next, name)) - goto cleanup; + if (var->expression.count != (size_t)-1) { + if (!create_array_accessors(parser, var)) + goto cleanup; + } } else if (!localblock && !nofields && var->expression.vtype == TYPE_FIELD && @@ -5694,16 +5721,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)) { @@ -5811,11 +5830,10 @@ skipvar: parseerror(parser, "TODO: initializers for local arrays"); break; } - /* -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); -*/ - parseerror(parser, "TODO: initializing global arrays is not supported yet!"); - break; + + var->hasvalue = true; + if (!parse_array(parser, var)) + break; } else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '[')) { @@ -5838,22 +5856,52 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } 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; @@ -5893,6 +5941,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma 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: @@ -5957,7 +6012,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); } @@ -5965,7 +6020,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")) { @@ -6020,7 +6075,7 @@ static uint16_t progdefs_crc_both(uint16_t old, const char *str) return old; } -static void generate_checksum(parser_t *parser) +static void generate_checksum(parser_t *parser, ir_builder *ir) { uint16_t crc = 0xFFFF; size_t i; @@ -6074,14 +6129,13 @@ static void generate_checksum(parser_t *parser) crc = progdefs_crc_both(crc, ";\n"); } crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); - - parser->code->crc = crc; + ir->code->crc = crc; } 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)); @@ -6090,11 +6144,6 @@ parser_t *parser_create() memset(parser, 0, sizeof(*parser)); - if (!(parser->code = code_init())) { - mem_d(parser); - return NULL; - } - for (i = 0; i < operator_count; ++i) { if (operators[i].id == opid1('=')) { parser->assign_op = operators+i; @@ -6102,7 +6151,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; } @@ -6114,14 +6163,13 @@ parser_t *parser_create() parser->aliases = util_htnew(PARSER_HT_SIZE); - parser->ht_imm_string = util_htnew(512); - /* corrector */ vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); - empty_ctx.file = ""; - empty_ctx.line = 0; + empty_ctx.file = ""; + empty_ctx.line = 0; + empty_ctx.column = 0; parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); parser->nil->cvq = CV_CONST; if (OPTS_FLAG(UNTYPED_NIL)) @@ -6143,6 +6191,8 @@ parser_t *parser_create() parser->reserved_version = NULL; } + parser->fold = fold_init (parser); + parser->intrin = intrin_init(parser); return parser; } @@ -6157,7 +6207,7 @@ static bool parser_compile(parser_t *parser) { if (!parser_global_statement(parser)) { if (parser->tok == TOKEN_EOF) - parseerror(parser, "unexpected eof"); + parseerror(parser, "unexpected end of file"); else if (compile_errors) parseerror(parser, "there have been errors, bailing out"); lex_close(parser->lex); @@ -6212,15 +6262,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]); } @@ -6229,10 +6270,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); - vec_free(parser->imm_float); vec_free(parser->globals); vec_free(parser->fields); @@ -6249,7 +6286,6 @@ static void parser_remove_ast(parser_t *parser) vec_free(parser->correct_variables); vec_free(parser->correct_variables_score); - for (i = 0; i < vec_size(parser->_typedefs); ++i) ast_delete(parser->_typedefs[i]); vec_free(parser->_typedefs); @@ -6271,23 +6307,65 @@ static void parser_remove_ast(parser_t *parser) 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) { parser_remove_ast(parser); - code_cleanup(parser->code); - 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"); @@ -6366,27 +6444,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)) @@ -6437,7 +6501,8 @@ bool parser_finish(parser_t *parser, const char *output) } } - generate_checksum(parser); + generate_checksum(parser, ir); + if (OPTS_OPTION_BOOL(OPTION_DUMP)) ir_builder_dump(ir, con_out); for (i = 0; i < vec_size(parser->functions); ++i) { @@ -6459,7 +6524,7 @@ bool parser_finish(parser_t *parser, const char *output) if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) ir_builder_dump(ir, con_out); - if (!ir_builder_generate(parser->code, ir, output)) { + if (!ir_builder_generate(ir, output)) { con_out("*** failed to generate output file\n"); ir_builder_delete(ir); return false;