X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=d65eafe714eb6c8a78a752e896642cb95eb7b2cb;hp=e8760bb08344a5e07f2333f3e6eba5fe83f9c360;hb=46fa12cb265706673927319d07651096e10c5b80;hpb=960cb7034af7240036a969a59f21e28e63a86df2 diff --git a/parser.c b/parser.c index e8760bb..d65eafe 100644 --- a/parser.c +++ b/parser.c @@ -29,8 +29,6 @@ #define PARSER_HT_SIZE 512 #define TYPEDEF_HT_SIZE 512 -static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; - static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); @@ -111,7 +109,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) @@ -173,9 +171,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 */ @@ -287,6 +282,21 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } +static bool check_write_to(lex_ctx_t ctx, ast_expression *expr) +{ + 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"); + else + compile_error(ctx, "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; @@ -294,7 +304,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) 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_t generated_op = 0; @@ -316,8 +325,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; } @@ -331,7 +342,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('?',':')) && @@ -353,11 +363,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) #define NotSameType(T) \ (exprs[0]->vtype != exprs[1]->vtype || \ exprs[0]->vtype != T) - - /* preform any constant folding on operator usage first */ - /*if ((out = fold_op(parser->fold, op, exprs)))*/ - /*goto complete;*/ - switch (op->id) { default: @@ -506,11 +511,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) 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 add type %s and %s (%s %s)", + compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", type_name[exprs[0]->vtype], - type_name[exprs[1]->vtype], - asvalue[0]->name, - asvalue[1]->name); + type_name[exprs[1]->vtype]); return false; } if (!(out = fold_op(parser->fold, op, exprs))) { @@ -617,7 +620,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } 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 */ @@ -635,55 +638,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('|'): case opid1('&'): - if (NotSameType(TYPE_FLOAT)) { - compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s", - type_name[exprs[0]->vtype], - type_name[exprs[1]->vtype]); - return false; - } - if (!(out = fold_op(parser->fold, op, exprs))) - out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), - exprs[0], exprs[1]); - break; case opid1('^'): - /* - * Okay lets designate what the hell is an acceptable use - * of the ^ operator. In many vector processing units, XOR - * is allowed to be used on vectors, but only if the first - * operand is a vector, the second operand can be a float - * or vector. It's never legal for the first operand to be - * a float, and then the following operand to be a vector. - * Further more, the only time it is legal to do XOR otherwise - * is when both operand are floats. This nicely crafted if - * statement catches them all. - * - * In the event that the first operand is a vector, two - * possible situations can arise, thus, each element of - * vector A (operand A) is exclusive-ORed with the corresponding - * element of vector B (operand B), If B is scalar, the - * scalar value is first replicated for each element. - * - * The QCVM itself lacks a BITXOR instruction. Thus emulating - * the mathematics of it is required. The following equation - * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the - * QCVM also lacking a BITNEG instruction, we need to emulate - * ~FOO with -1 - FOO, the whole process becoming this nicely - * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)). - * - * When A is not scalar, this process is repeated for all - * components of vector A with the value in operand B, - * only if operand B is scalar. When A is not scalar, and B - * is also not scalar, this process is repeated for all - * components of the vector A with the components of vector B. - * Finally when A is scalar and B is scalar, this process is - * simply used once for A and B being LHS and RHS respectfully. - * - * Yes the semantics are a bit strange (no pun intended). - * But then again BITXOR is strange itself, consdering it's - * commutative, assocative, and elements of the BITXOR operation - * are their own inverse. - */ if ( !(exprs[0]->vtype == TYPE_FLOAT && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR)) @@ -700,46 +655,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) * since scalar ^ vector is not allowed. */ if (exprs[0]->vtype == TYPE_FLOAT) { - ast_binary *expr = ast_binary_new( - ctx, - INSTR_SUB_F, - (ast_expression*)parser->fold->imm_float[2], - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITAND, - exprs[0], - exprs[1] - ) - ); - expr->refs = AST_REF_NONE; - - out = (ast_expression*) - ast_binary_new( - ctx, - INSTR_BITAND, - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITOR, - exprs[0], - exprs[1] - ), - (ast_expression*)expr - ); + out = (ast_expression*)ast_binary_new(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 xor with vector and + * 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) { /* - * Xor all the values of the vector components against the + * Bitop all the values of the vector components against the * vectors components in question. */ - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector"); - return false; + out = (ast_expression*)ast_binary_new(ctx, + (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V), + exprs[0], exprs[1]); } else { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float"); - return false; + out = (ast_expression*)ast_binary_new(ctx, + (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF), + exprs[0], exprs[1]); } } } @@ -753,6 +688,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); return false; } + break; case opid2('|','|'): generated_op += 1; /* INSTR_OR */ @@ -817,15 +753,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - + if (!(out = fold_op(parser->fold, op, exprs))) { - ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow")); + 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 = (ast_expression*)ast_binary_new( + 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)); @@ -834,7 +790,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; - } + } if (!(out = fold_op(parser->fold, op, exprs))) { ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); @@ -958,9 +914,7 @@ 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]); out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); break; case opid3('+','+','P'): @@ -975,9 +929,7 @@ 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], @@ -1003,9 +955,7 @@ 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], @@ -1032,9 +982,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 @@ -1069,9 +1017,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 @@ -1107,30 +1053,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", @@ -1141,29 +1091,37 @@ 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 = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]); + else + out = (ast_expression*)ast_binary_new(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 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 (!(out = fold_op(parser->fold, op, exprs))) - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]); + if (!(out = fold_op(parser->fold, op, exprs))) { + if (exprs[0]->vtype == TYPE_FLOAT) { + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]); + } else { + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]); + } + } break; } #undef NotSameType -/*complete:*/ if (!out) { compile_error(ctx, "failed to apply operator %s", op->op); return false; @@ -1182,6 +1140,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); @@ -1205,9 +1164,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) @@ -1223,13 +1184,58 @@ static bool parser_close_call(parser_t *parser, shunt *sy) 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, 1); + 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)); @@ -1458,8 +1464,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) && @@ -1539,9 +1544,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; @@ -1565,16 +1568,13 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) if (!var && !strcmp(parser_tokval(parser), "__FUNC__")) 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)); } if (!var) { @@ -1650,7 +1650,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 @@ -1685,7 +1685,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 */ @@ -1713,6 +1713,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('+','=') || \ @@ -1724,14 +1735,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; + } } } @@ -1893,7 +1919,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; @@ -2759,7 +2785,15 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * else if (!strcmp(parser_tokval(parser), "inline")) { flags |= AST_FLAG_INLINE; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + parseerror(parser, "`inline` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "eraseable")) { + flags |= AST_FLAG_ERASEABLE; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`"); *cvq = CV_WRONG; return false; } @@ -3882,7 +3916,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) 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, 0.1)); + gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1f)); if (!self_frame || !self_nextthink || !self_think || !time_plus_1) { if (self_frame) ast_delete(self_frame); @@ -4644,6 +4678,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()' @@ -4804,6 +4843,13 @@ static bool parse_typedef(parser_t *parser) 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", @@ -4969,6 +5015,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield 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; @@ -5179,8 +5233,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 = (ast_value*)old; + else { + var = NULL; + goto skipvar; + } } } } @@ -5445,7 +5503,7 @@ skipvar: parseerror(parser, "error parsing break definition"); break; } - (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); + (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); } else { parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); break; @@ -5881,7 +5939,8 @@ parser_t *parser_create() parser->reserved_version = NULL; } - parser->fold = fold_init(parser); + parser->fold = fold_init (parser); + parser->intrin = intrin_init(parser); return parser; } @@ -6000,8 +6059,8 @@ static void parser_remove_ast(parser_t *parser) 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)