X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=672ce86799044024dfc955d3dfc5aec31d5e4f28;hp=e0864493eab449bff48a727478933df9a48e4744;hb=41a76ab91dc6f872b03cd64f51aba86578f330c0;hpb=fa468e06733d2860082f7c3652aa464e96b13586 diff --git a/parser.c b/parser.c index e086449..672ce86 100644 --- a/parser.c +++ b/parser.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 + * Copyright (C) 2012, 2013, 2014, 2015 * Wolfgang Bumiller * Dale Weiler * @@ -23,6 +23,7 @@ */ #include #include + #include "parser.h" #define PARSER_HT_LOCALS 2 @@ -43,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, ...) { @@ -282,6 +283,29 @@ 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"); + return false; + } + /* + * 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; @@ -289,7 +313,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; @@ -311,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; } @@ -326,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('?',':')) && @@ -348,6 +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) + switch (op->id) { default: @@ -442,24 +467,19 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = exprs[0]; break; case opid2('-','P'): - if (!(out = fold_op(parser->fold, op, exprs))) { - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, - (ast_expression*)parser->fold->imm_float[0], - exprs[0]); - break; - case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, - (ast_expression*)parser->fold->imm_vector[0], - exprs[0]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot negate type %s", + 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; - } + 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'): @@ -504,10 +524,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) if (!(out = fold_op(parser->fold, op, exprs))) { switch (exprs[0]->vtype) { case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); + out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]); break; case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); + 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", @@ -529,10 +549,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) if (!(out = fold_op(parser->fold, op, exprs))) { switch (exprs[0]->vtype) { case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]); + out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]); break; case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]); + 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", @@ -559,15 +579,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) switch (exprs[0]->vtype) { case TYPE_FLOAT: if (exprs[1]->vtype == TYPE_VECTOR) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); + out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, 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 = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); + 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]); + 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", @@ -587,7 +607,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } if (!(out = fold_op(parser->fold, op, exprs))) { if (exprs[0]->vtype == TYPE_FLOAT) - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]); + out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[1]); else { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); @@ -640,7 +660,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) * since scalar ^ vector is not allowed. */ if (exprs[0]->vtype == TYPE_FLOAT) { - out = (ast_expression*)ast_binary_new(ctx, + out = fold_binary(ctx, (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), exprs[0], exprs[1]); } else { @@ -653,11 +673,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) * Bitop all the values of the vector components against the * vectors components in question. */ - out = (ast_expression*)ast_binary_new(ctx, + 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 = (ast_expression*)ast_binary_new(ctx, + out = fold_binary(ctx, (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF), exprs[0], exprs[1]); } @@ -667,12 +687,44 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid2('<','<'): case opid2('>','>'): + 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('>','>','='): - if(!(out = fold_op(parser->fold, op, exprs))) { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); + 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('|','|'): @@ -710,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; @@ -747,6 +799,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } 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)); @@ -758,6 +830,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } if (!(out = fold_op(parser->fold, op, exprs))) { + /* This whole block is NOT fold_binary safe */ ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); eq->refs = AST_REF_NONE; @@ -798,7 +871,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } if (!(out = fold_op(parser->fold, op, exprs))) - out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + out = fold_binary(ctx, generated_op, exprs[0], exprs[1]); break; case opid2('!', '='): if (exprs[0]->vtype != exprs[1]->vtype) { @@ -808,7 +881,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } if (!(out = fold_op(parser->fold, op, exprs))) - out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]); + out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]); break; case opid2('=', '='): if (exprs[0]->vtype != exprs[1]->vtype) { @@ -818,7 +891,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } if (!(out = fold_op(parser->fold, op, exprs))) - out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]); + out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]); break; case opid1('='): @@ -879,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'): @@ -896,9 +970,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], @@ -924,9 +996,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], @@ -938,9 +1008,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } if (!out) return false; - out = (ast_expression*)ast_binary_new(ctx, subop, - out, - (ast_expression*)parser->fold->imm_float[1]); + out = fold_binary(ctx, subop, + out, + (ast_expression*)parser->fold->imm_float[1]); + break; case opid2('+','='): case opid2('-','='): @@ -953,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 @@ -990,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 @@ -1008,9 +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 { - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, - (ast_expression*)parser->fold->imm_float[1], - 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; @@ -1036,9 +1103,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,14 +1134,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) else assignop = type_store_instr[exprs[0]->vtype]; if (exprs[0]->vtype == TYPE_FLOAT) - out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]); + out = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]); else - out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]); + 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); - } + (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 @@ -1085,6 +1148,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) 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 && exprs[0]->vtype != TYPE_VECTOR) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1093,9 +1172,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } 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]); + out = fold_binary(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]); + out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]); } } break; @@ -1203,7 +1282,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) * sy->out should I be doing here? */ sy->out[fid] = syexp(foldval->node.context, foldval); - vec_shrinkby(sy->out, 1); + vec_shrinkby(sy->out, paramcount); vec_free(exprs); return true; @@ -1233,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*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount); + call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount, false); } } @@ -1398,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; @@ -1488,14 +1567,14 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) return true; } else if (parser->tok == TOKEN_FLOATCONST) { - ast_expression *val = fold_constgen_float(parser->fold, (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), val)); return true; } else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) { - ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(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), val)); @@ -1556,6 +1635,23 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) 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; @@ -1664,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 */ @@ -1692,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('+','=') || \ @@ -1887,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; @@ -2481,16 +2588,22 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou initexpr = parse_expression_leave(parser, false, false, false); if (!initexpr) goto onerr; - } - /* move on to condition */ - if (parser->tok != ';') { - parseerror(parser, "expected semicolon after for-loop initializer"); - goto onerr; - } - if (!parser_next(parser)) { - parseerror(parser, "expected for-loop condition"); - goto onerr; + /* move on to condition */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + } else { + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } } /* parse the condition */ @@ -2499,7 +2612,6 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou if (!cond) goto onerr; } - /* move on to incrementor */ if (parser->tok != ';') { parseerror(parser, "expected semicolon after for-loop initializer"); @@ -2715,6 +2827,11 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express /* returns true when it was a variable qualifier, false otherwise! * on error, cvq is set to CV_WRONG */ +typedef struct { + const char *name; + size_t flag; +} attribute_t; + static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message) { bool had_const = false; @@ -2724,8 +2841,18 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * bool had_static = false; uint32_t flags = 0; - *cvq = CV_NONE; + static attribute_t attributes[] = { + { "noreturn", AST_FLAG_NORETURN }, + { "inline", AST_FLAG_INLINE }, + { "eraseable", AST_FLAG_ERASEABLE }, + { "accumulate", AST_FLAG_ACCUMULATE }, + { "last", AST_FLAG_FINAL_DECL } + }; + + *cvq = CV_NONE; + for (;;) { + size_t i; if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { had_attrib = true; /* parse an attribute */ @@ -2734,15 +2861,25 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * *cvq = CV_WRONG; return false; } - if (!strcmp(parser_tokval(parser), "noreturn")) { - flags |= AST_FLAG_NORETURN; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; + + for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) { + if (!strcmp(parser_tokval(parser), attributes[i].name)) { + flags |= attributes[i].flag; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`%s` attribute has no parameters, expected `]]`", + attributes[i].name); + *cvq = CV_WRONG; + return false; + } + break; } } - else if (!strcmp(parser_tokval(parser), "noref")) { + + if (i != GMQCC_ARRAY_COUNT(attributes)) + goto leave; + + + if (!strcmp(parser_tokval(parser), "noref")) { had_noref = true; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); @@ -2750,22 +2887,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } - else if (!strcmp(parser_tokval(parser), "inline")) { - flags |= AST_FLAG_INLINE; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`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; - } - } else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { flags |= AST_FLAG_ALIAS; *message = NULL; @@ -2847,6 +2968,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) { + flags |= AST_FLAG_COVERAGE; + if (!parser_next(parser)) { + error_in_coverage: + parseerror(parser, "parse error in coverage attribute"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok == '(') { + if (!parser_next(parser)) { + bad_coverage_arg: + parseerror(parser, "invalid parameter for coverage() attribute\n" + "valid are: block"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok != ')') { + do { + if (parser->tok != TOKEN_IDENT) + goto bad_coverage_arg; + if (!strcmp(parser_tokval(parser), "block")) + flags |= AST_FLAG_BLOCK_COVERAGE; + else if (!strcmp(parser_tokval(parser), "none")) + flags &= ~(AST_FLAG_COVERAGE_MASK); + else + goto bad_coverage_arg; + if (!parser_next(parser)) + goto error_in_coverage; + if (parser->tok == ',') { + if (!parser_next(parser)) + goto error_in_coverage; + } + } while (parser->tok != ')'); + } + if (parser->tok != ')' || !parser_next(parser)) + goto error_in_coverage; + } else { + /* without parameter [[coverage]] equals [[coverage(block)]] */ + flags |= AST_FLAG_BLOCK_COVERAGE; + } + } else { /* Skip tokens until we hit a ]] */ @@ -2875,6 +3037,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * } else break; + + leave: if (!parser_next(parser)) goto onerr; } @@ -3002,7 +3166,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); if (typevar || parser->tok == TOKEN_TYPENAME) { - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) { + if (!parse_variable(parser, block, true, CV_NONE, typevar, false, false, 0, NULL)) { ast_delete(switchnode); return false; } @@ -3014,7 +3178,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) { + if (!parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, NULL)) { ast_delete(switchnode); return false; } @@ -3259,7 +3423,13 @@ static bool parse_pragma_do(parser_t *parser) else { (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); - return false; + + /* skip to eol */ + while (!parse_eol(parser)) { + parser_next(parser); + } + + return true; } return true; @@ -3297,7 +3467,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); - if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { /* local variable */ if (!block) { @@ -3316,7 +3486,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring); + return parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_KEYWORD) { @@ -3869,85 +4039,109 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } if (has_frame_think) { - 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; - - 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, 0.1f)); - - 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); @@ -3974,13 +4168,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } - if (var->argcounter) { + if (var->argcounter && !func->argc) { ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT); parser_addlocal(parser, argc->name, (ast_expression*)argc); func->argc = argc; } - if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) { + if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC && !func->varargs) { char name[1024]; ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY); varargs->expression.flags |= AST_FLAG_IS_VARARG; @@ -3999,7 +4193,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) goto enderrfn; } func->varargs = varargs; - func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params)); + func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params), false); } parser->function = func; @@ -4010,7 +4204,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var) vec_push(func->blocks, block); - parser->function = old; if (!parser_leaveblock(parser)) retval = false; @@ -4058,7 +4251,7 @@ static ast_expression *array_accessor_split( cmp = ast_binary_new(ctx, INSTR_LT, (ast_expression*)index, - (ast_expression*)fold_constgen_float(parser->fold, middle)); + (ast_expression*)fold_constgen_float(parser->fold, middle, false)); if (!cmp) { ast_delete(left); ast_delete(right); @@ -4091,7 +4284,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) assignop = INSTR_STORE_V; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4157,7 +4350,7 @@ static ast_expression *array_field_setter_node( if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) assignop = INSTR_STOREP_V; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4220,7 +4413,7 @@ static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast ast_return *ret; ast_array_index *subscript; - subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from)); + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false)); if (!subscript) return NULL; @@ -4252,6 +4445,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con parseerror(parser, "failed to create accessor function value"); return false; } + fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); func = ast_function_new(ast_ctx(array), funcname, fval); if (!func) { @@ -4447,7 +4641,6 @@ 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_t ctx; @@ -4473,6 +4666,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) /* parse variables until we hit a closing paren */ while (parser->tok != ')') { + bool is_varargs = false; + if (!first) { /* there must be commas between them */ if (parser->tok != ',') { @@ -4486,10 +4681,13 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } first = false; - if (parser->tok == TOKEN_DOTS) { + param = parse_typename(parser, NULL, NULL, &is_varargs); + if (!param && !is_varargs) + goto on_error; + if (is_varargs) { /* '...' indicates a varargs function */ variadic = true; - if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) { + if (parser->tok != ')' && parser->tok != TOKEN_IDENT) { parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); goto on_error; } @@ -4500,13 +4698,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) goto on_error; } } - } - else - { - /* for anything else just parse a typename */ - param = parse_typename(parser, NULL, NULL); - if (!param) - goto on_error; + } else { vec_push(params, param); if (param->expression.vtype >= TYPE_VARIANT) { char tname[1024]; /* typename is reserved in C++ */ @@ -4525,12 +4717,17 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } if (parser->tok == TOKEN_IDENT) { argcounter = util_strdup(parser_tokval(parser)); + ast_value_set_name(param, argcounter); if (!parser_next(parser) || parser->tok != ')') { parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); goto on_error; } } } + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC && param->name[0] == '<') { + parseerror(parser, "parameter name omitted"); + goto on_error; + } } } @@ -4646,6 +4843,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) * The base type makes up every bit of type information which comes *before* the * variable name. * + * NOTE: The value must either be named, have a NULL name, or a name starting + * with '<'. In the first case, this will be the actual variable or type + * name, in the other cases it is assumed that the name will appear + * later, and an error is generated otherwise. + * * The following will be parsed in its entirety: * void() foo() * The 'basetype' in this case is 'void()' @@ -4653,7 +4855,7 @@ 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_t ctx; @@ -4663,11 +4865,15 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va 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"); @@ -4677,8 +4883,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va /* Further dots are handled seperately because they won't be part of the * basetype */ - while (parser->tok == '.') { - ++morefields; + while (true) { + if (parser->tok == '.') + ++morefields; + else if (parser->tok == TOKEN_DOTS) + morefields += 3; + else + break; + vararg = false; if (!parser_next(parser)) { parseerror(parser, "expected typename for field definition"); return NULL; @@ -4688,6 +4900,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va if (parser->tok == TOKEN_IDENT) cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0); if (!cached_typedef && parser->tok != TOKEN_TYPENAME) { + if (vararg && is_vararg) { + *is_vararg = true; + return NULL; + } parseerror(parser, "expected typename"); return NULL; } @@ -4739,8 +4955,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } /* there may be a name now */ - if (parser->tok == TOKEN_IDENT) { + if (parser->tok == TOKEN_IDENT || parser->tok == TOKEN_KEYWORD) { + if (!strcmp(parser_tokval(parser), "break")) + (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); + else if (parser->tok == TOKEN_KEYWORD) + goto leave; + name = util_strdup(parser_tokval(parser)); + /* parse on */ if (!parser_next(parser)) { ast_delete(var); @@ -4750,6 +4972,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } } + leave: /* now this may be an array */ if (parser->tok == '[') { wasarray = true; @@ -4801,11 +5024,18 @@ static bool parse_typedef(parser_t *parser) ast_value *typevar, *oldtype; ast_expression *old; - typevar = parse_typename(parser, NULL, NULL); + typevar = parse_typename(parser, NULL, NULL, NULL); if (!typevar) return false; + /* while parsing types, the ast_value's get named '' */ + if (!typevar->name || typevar->name[0] == '<') { + parseerror(parser, "missing name in typedef"); + ast_delete(typevar); + return false; + } + if ( (old = parser_find_var(parser, typevar->name)) ) { parseerror(parser, "cannot define a type with the same name as a variable: %s\n" " -> `%s` has been declared here: %s:%i", @@ -4959,18 +5189,27 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield bool wasarray = false; ast_member *me[3] = { NULL, NULL, NULL }; + ast_member *last_me[3] = { NULL, NULL, NULL }; if (!localblock && is_static) parseerror(parser, "`static` qualifier is not supported in global scope"); /* get the first complete variable */ - var = parse_typename(parser, &basetype, cached_typedef); + var = parse_typename(parser, &basetype, cached_typedef, NULL); if (!var) { if (basetype) ast_delete(basetype); return false; } + /* while parsing types, the ast_value's get named '' */ + if (!var->name || var->name[0] == '<') { + parseerror(parser, "declaration does not declare anything"); + if (basetype) + ast_delete(basetype); + return false; + } + while (true) { proto = NULL; wasarray = false; @@ -5010,6 +5249,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } var->cvq = qualifier; + if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */ + var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); var->expression.flags |= qflags; /* @@ -5020,6 +5261,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield var->expression.flags & AST_FLAG_ALIAS) var->desc = vstring; + if (parser_find_global(parser, var->name) && var->expression.flags & AST_FLAG_ALIAS) { + parseerror(parser, "function aliases cannot be forward declared"); + retval = false; + goto cleanup; + } + + /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) * Also: if there was a prototype, `var` will be deleted and set to `proto` which @@ -5132,6 +5380,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + if (old->flags & AST_FLAG_FINAL_DECL) { + parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } proto = (ast_value*)old; if (!ast_istype(old, ast_value)) { parseerror(parser, "internal error: not an ast_value"); @@ -5145,6 +5399,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto->expression.flags |= var->expression.flags; + /* copy the context for finals, + * so the error can show where it was actually made 'final' + */ + if (proto->expression.flags & AST_FLAG_FINAL_DECL) + ast_ctx(old) = ast_ctx(var); ast_delete(var); var = proto; } @@ -5169,6 +5428,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + /* doing this here as the above is just for a single scope */ old = parser_find_local(parser, var->name, 0, &isparam); if (old && isparam) { if (parsewarning(parser, WARN_LOCAL_SHADOWS, @@ -5182,7 +5442,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); if (ast_istype(old, ast_value)) - var = (ast_value*)old; + var = proto = (ast_value*)old; else { var = NULL; goto skipvar; @@ -5241,7 +5501,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield return false; } - if (var->expression.vtype != find->vtype) { + if (!ast_compare_type((ast_expression*)var, find)) { char ty1[1024]; char ty2[1024]; @@ -5315,6 +5575,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield */ char *defname = NULL; size_t prefix_len, ln; + size_t sn, sn_size; ln = strlen(parser->function->name); vec_append(defname, ln, parser->function->name); @@ -5336,6 +5597,24 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* now rename the global */ ln = strlen(var->name); vec_append(defname, ln, var->name); + /* if a variable of that name already existed, add the + * counter value. + * The counter is incremented either way. + */ + sn_size = vec_size(parser->function->static_names); + for (sn = 0; sn != sn_size; ++sn) { + if (strcmp(parser->function->static_names[sn], var->name) == 0) + break; + } + if (sn != sn_size) { + char *num = NULL; + int len = util_asprintf(&num, "#%u", parser->function->static_count); + vec_append(defname, len, num); + mem_d(num); + } + else + vec_push(parser->function->static_names, util_strdup(var->name)); + parser->function->static_count++; ast_value_set_name(var, defname); /* push it to the to-be-generated globals */ @@ -5374,6 +5653,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } } + memcpy(last_me, me, sizeof(me)); me[0] = me[1] = me[2] = NULL; cleanvar = false; /* Part 2.2 @@ -5446,16 +5726,8 @@ skipvar: if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) { if (parser->tok != '=') { - if (!strcmp(parser_tokval(parser), "break")) { - if (!parser_next(parser)) { - parseerror(parser, "error parsing break definition"); - break; - } - (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); - } else { - parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); - break; - } + parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); + break; } if (!parser_next(parser)) { @@ -5589,22 +5861,52 @@ skipvar: } else { ast_expression *cexp; ast_value *cval; + bool folded_const = false; cexp = parse_expression_leave(parser, true, false, false); if (!cexp) break; + cval = ast_istype(cexp, ast_value) ? (ast_value*)cexp : NULL; - if (!localblock) { - cval = (ast_value*)cexp; + /* deal with foldable constants: */ + if (localblock && + var->cvq == CV_CONST && cval && cval->hasvalue && cval->cvq == CV_CONST && !cval->isfield) + { + /* remove it from the current locals */ + if (isvector) { + for (i = 0; i < 3; ++i) { + vec_pop(parser->_locals); + vec_pop(localblock->collect); + } + } + /* do sanity checking, this function really needs refactoring */ + if (vec_last(parser->_locals) != (ast_expression*)var) + parseerror(parser, "internal error: unexpected change in local variable handling"); + else + vec_pop(parser->_locals); + if (vec_last(localblock->locals) != var) + parseerror(parser, "internal error: unexpected change in local variable handling (2)"); + else + vec_pop(localblock->locals); + /* push it to the to-be-generated globals */ + vec_push(parser->globals, (ast_expression*)var); + if (isvector) + for (i = 0; i < 3; ++i) + vec_push(parser->globals, (ast_expression*)last_me[i]); + folded_const = true; + } + + if (folded_const || !localblock || is_static) { if (cval != parser->nil && - (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) + (!cval || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) ) { - parseerror(parser, "cannot initialize a global constant variable with a non-constant expression"); + parseerror(parser, "initializer is non constant"); } else { - if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + if (!is_static && + !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && qualifier != CV_VAR) { var->cvq = CV_CONST; @@ -5644,6 +5946,13 @@ skipvar: vec_free(sy.argc); var->cvq = cvq; } + /* a constant initialized to an inexact value should be marked inexact: + * const float x = ; should propagate the inexact flag + */ + if (var->cvq == CV_CONST && var->expression.vtype == TYPE_FLOAT) { + if (cval && cval->hasvalue && cval->cvq == CV_CONST) + var->inexact = cval->inexact; + } } another: @@ -5708,7 +6017,7 @@ static bool parser_global_statement(parser_t *parser) if (parser->tok == TOKEN_IDENT) istype = parser_find_typedef(parser, parser_tokval(parser), 0); - if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL); } @@ -5716,7 +6025,7 @@ static bool parser_global_statement(parser_t *parser) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring); + return parse_variable(parser, NULL, false, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum")) { @@ -5847,7 +6156,7 @@ parser_t *parser_create() } } if (!parser->assign_op) { - printf("internal error: initializing parser: failed to find assign operator\n"); + con_err("internal error: initializing parser: failed to find assign operator\n"); mem_d(parser); return NULL; } @@ -6017,11 +6326,51 @@ void parser_cleanup(parser_t *parser) mem_d(parser); } +static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) { + size_t i; + ast_expression *expr; + ast_value *cov; + ast_function *func; + + if (!OPTS_OPTION_BOOL(OPTION_COVERAGE)) + return true; + + func = NULL; + for (i = 0; i != vec_size(parser->functions); ++i) { + if (!strcmp(parser->functions[i]->name, "coverage")) { + func = parser->functions[i]; + break; + } + } + if (!func) { + if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) { + con_out("coverage support requested but no coverage() builtin declared\n"); + ir_builder_delete(ir); + return false; + } + return true; + } + + cov = func->vtype; + expr = (ast_expression*)cov; + + if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) { + char ty[1024]; + ast_type_to_string(expr, ty, sizeof(ty)); + con_out("invalid type for coverage(): %s\n", ty); + ir_builder_delete(ir); + return false; + } + + ir->coverage_func = func->ir_func->value; + return true; +} + bool parser_finish(parser_t *parser, const char *output) { - size_t i; - ir_builder *ir; - bool retval = true; + size_t i; + ir_builder *ir; + bool retval = true; if (compile_errors) { con_out("*** there were compile errors\n"); @@ -6103,6 +6452,10 @@ bool parser_finish(parser_t *parser, const char *output) 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))