X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=aa536c50ac8161f4cba91617781cfb2603c4aa7e;hp=d629e13be7e34fd43d8dde3176193ccb6c2b0cf2;hb=e11a17b40805b93e1e2ae80129c9d235b6d6119b;hpb=0f190026e8a0c3612dc619d83cb40d27b1ddea23 diff --git a/parser.c b/parser.c index d629e13..aa536c5 100644 --- a/parser.c +++ b/parser.c @@ -31,10 +31,9 @@ /* beginning of locals */ #define PARSER_HT_LOCALS 2 -#define PARSER_HT_SIZE 1024 +#define PARSER_HT_SIZE 128 #define TYPEDEF_HT_SIZE 16 -enum parser_pot { POT_PAREN, POT_TERNARY1, POT_TERNARY2 }; typedef struct { lex_file *lex; int tok; @@ -52,7 +51,10 @@ typedef struct { 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; @@ -89,12 +91,6 @@ typedef struct { /* we store the '=' operator info */ const oper_info *assign_op; - /* Keep track of our ternary vs parenthesis nesting state. - * If we reach a 'comma' operator in a ternary without a paren, - * we shall trigger -Wternary-precedence. - */ - enum parser_pot *pot; - /* magic values */ ast_value *const_vec[3]; @@ -142,16 +138,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char * return r; } -static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...) -{ - bool r; - va_list ap; - va_start(ap, fmt); - r = vcompile_warning(ctx, warntype, fmt, ap); - va_end(ap); - return r; -} - /********************************************************************** * some maths used for constant folding */ @@ -239,6 +225,12 @@ static ast_value* parser_const_float_0(parser_t *parser) 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) @@ -386,23 +378,28 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t typedef struct { size_t etype; /* 0 = expression, others are operators */ - int paren; + bool isparen; size_t off; ast_expression *out; ast_block *block; /* for commas and function calls */ lex_ctx ctx; } sy_elem; + +enum { + PAREN_EXPR, + PAREN_FUNC, + PAREN_INDEX, + PAREN_TERNARY1, + PAREN_TERNARY2 +}; typedef struct { - sy_elem *out; - sy_elem *ops; + sy_elem *out; + sy_elem *ops; + size_t *argc; + unsigned int *paren; } shunt; -#define SY_PAREN_EXPR '(' -#define SY_PAREN_FUNC 'f' -#define SY_PAREN_INDEX '[' -#define SY_PAREN_TERNARY '?' - static sy_elem syexp(lex_ctx ctx, ast_expression *v) { sy_elem e; e.etype = 0; @@ -410,7 +407,7 @@ static sy_elem syexp(lex_ctx ctx, ast_expression *v) { e.out = v; e.block = NULL; e.ctx = ctx; - e.paren = 0; + e.isparen = false; return e; } @@ -421,7 +418,7 @@ static sy_elem syblock(lex_ctx ctx, ast_block *v) { e.out = (ast_expression*)v; e.block = v; e.ctx = ctx; - e.paren = 0; + e.isparen = false; return e; } @@ -432,33 +429,27 @@ static sy_elem syop(lex_ctx ctx, const oper_info *op) { e.out = NULL; e.block = NULL; e.ctx = ctx; - e.paren = 0; + e.isparen = false; return e; } -static sy_elem syparen(lex_ctx ctx, int p, size_t off) { +static sy_elem syparen(lex_ctx ctx, size_t off) { sy_elem e; e.etype = 0; e.off = off; e.out = NULL; e.block = NULL; e.ctx = ctx; - e.paren = p; + e.isparen = true; return e; } -#ifdef DEBUGSHUNT -# define DEBUGSHUNTDO(x) x -#else -# define DEBUGSHUNTDO(x) -#endif - /* With regular precedence rules, ent.foo[n] is the same as (ent.foo)[n], * so we need to rotate it to become ent.(foo[n]). */ static bool rotate_entfield_array_index_nodes(ast_expression **out) { - ast_array_index *index; + ast_array_index *index, *oldindex; ast_entfield *entfield; ast_value *field; @@ -482,12 +473,16 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) sub = index->index; entity = entfield->entity; - ast_delete(index); + oldindex = index; index = ast_array_index_new(ctx, (ast_expression*)field, sub); entfield = ast_entfield_new(ctx, entity, (ast_expression*)index); *out = (ast_expression*)entfield; + oldindex->array = NULL; + oldindex->index = NULL; + ast_delete(oldindex); + return true; } @@ -537,7 +532,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } - if (vec_last(sy->ops).paren) { + if (vec_last(sy->ops).isparen) { parseerror(parser, "unmatched parenthesis"); return false; } @@ -545,8 +540,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) op = &operators[vec_last(sy->ops).etype - 1]; ctx = vec_last(sy->ops).ctx; - DEBUGSHUNTDO(con_out("apply %s\n", op->op)); - 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); @@ -650,7 +643,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { #if 0 /* This is not broken in fteqcc anymore */ - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { /* this error doesn't need to make us bail out */ (void)!parsewarning(parser, WARN_EXTENSIONS, "accessing array-field members of an entity without parenthesis\n" @@ -661,6 +654,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid1(','): + if (vec_size(sy->paren) && vec_last(sy->paren) == PAREN_FUNC) { + vec_push(sy->out, syexp(ctx, exprs[0])); + vec_push(sy->out, syexp(ctx, exprs[1])); + vec_last(sy->argc)++; + return true; + } if (blocks[0]) { if (!ast_block_add_expr(blocks[0], exprs[1])) return false; @@ -1052,11 +1051,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid2('?',':'): - if (vec_last(parser->pot) != POT_TERNARY2) { + if (vec_last(sy->paren) != PAREN_TERNARY2) { compile_error(ctx, "mismatched parenthesis/ternary"); return false; } - vec_pop(parser->pot); + vec_pop(sy->paren); if (!ast_compare_type(exprs[1], exprs[2])) { ast_type_to_string(exprs[1], ty1, sizeof(ty1)); ast_type_to_string(exprs[2], ty2, sizeof(ty2)); @@ -1115,7 +1114,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } else assignop = type_storep_instr[exprs[0]->expression.vtype]; - if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1])) + if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1])) { ast_type_to_string(field->expression.next, ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); @@ -1142,7 +1141,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) assignop = type_store_instr[exprs[0]->expression.vtype]; } - if (assignop == AINSTR_END) { + if (assignop == VINSTR_END) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); @@ -1360,15 +1359,28 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) asbinstore->keep_dest = true; out = (ast_expression*)asbinstore; break; + + case opid2('~', 'P'): + if (exprs[0]->expression.vtype != TYPE_FLOAT) { + 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]); + break; + } #undef NotSameType if (!out) { - compile_error(ctx, "failed to apply operand %s", op->op); + compile_error(ctx, "failed to apply operator %s", op->op); return false; } - DEBUGSHUNTDO(con_out("applied %s\n", op->op)); vec_push(sy->out, syexp(ctx, out)); return true; } @@ -1381,20 +1393,27 @@ static bool parser_close_call(parser_t *parser, shunt *sy) ast_call *call; size_t fid; - size_t paramcount; + size_t paramcount, i; + fid = vec_last(sy->ops).off; vec_shrinkby(sy->ops, 1); - fid = sy->ops[vec_size(sy->ops)].off; /* out[fid] is the function * everything above is parameters... - * 0 params = nothing - * 1 params = ast_expression - * more = ast_block */ + if (!vec_size(sy->argc)) { + parseerror(parser, "internal error: no argument counter available"); + return false; + } - if (vec_size(sy->out) < 1 || vec_size(sy->out) <= fid) { - parseerror(parser, "internal error: function call needs function and parameter list..."); + paramcount = vec_last(sy->argc); + vec_pop(sy->argc); + + if (vec_size(sy->out) < fid) { + parseerror(parser, "internal error: broken function call%lu < %lu+%lu\n", + (unsigned long)vec_size(sy->out), + (unsigned long)fid, + (unsigned long)paramcount); return false; } @@ -1417,47 +1436,31 @@ static bool parser_close_call(parser_t *parser, shunt *sy) } call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun); - if (!call) { - parseerror(parser, "internal error: failed to create ast_call node"); + if (!call) return false; - } - if (fid+1 == vec_size(sy->out)) { - /* no arguments */ - paramcount = 0; - } else if (fid+2 == vec_size(sy->out)) { - ast_block *params; - vec_shrinkby(sy->out, 1); - params = sy->out[vec_size(sy->out)].block; - if (!params) { - /* 1 param */ - paramcount = 1; - vec_push(call->params, sy->out[vec_size(sy->out)].out); - } else { - paramcount = vec_size(params->exprs); - call->params = params->exprs; - params->exprs = NULL; - ast_delete(params); - } - if (parser->max_param_count < paramcount) - parser->max_param_count = paramcount; - (void)!ast_call_check_types(call); - } else { - parseerror(parser, "invalid function call"); + 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)); return false; } + 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); + if (parser->max_param_count < paramcount) + parser->max_param_count = paramcount; + if (ast_istype(fun, ast_value)) { funval = (ast_value*)fun; if ((fun->expression.flags & AST_FLAG_VARIADIC) && !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin)) { - size_t va_count; - if (paramcount < vec_size(fun->expression.params)) - va_count = 0; - else - va_count = paramcount - vec_size(fun->expression.params); - call->va_count = (ast_expression*)parser_const_float(parser, (double)va_count); + call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount); } } @@ -1518,54 +1521,48 @@ static bool parser_close_call(parser_t *parser, shunt *sy) return true; } -static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only) +static bool parser_close_paren(parser_t *parser, shunt *sy) { if (!vec_size(sy->ops)) { parseerror(parser, "unmatched closing paren"); return false; } - /* this would for bit a + (x) because there are no operators inside (x) - if (sy->ops[vec_size(sy->ops)-1].paren == 1) { - parseerror(parser, "empty parenthesis expression"); - return false; - } - */ + while (vec_size(sy->ops)) { - if (vec_last(sy->ops).paren == SY_PAREN_FUNC) { - if (!parser_close_call(parser, sy)) - return false; - break; - } - if (vec_last(sy->ops).paren == SY_PAREN_EXPR) { - if (!vec_size(sy->out)) { - compile_error(vec_last(sy->ops).ctx, "empty paren expression"); + if (vec_last(sy->ops).isparen) { + if (vec_last(sy->paren) == PAREN_FUNC) { + vec_pop(sy->paren); + if (!parser_close_call(parser, sy)) + return false; + break; + } + if (vec_last(sy->paren) == PAREN_EXPR) { + vec_pop(sy->paren); + if (!vec_size(sy->out)) { + compile_error(vec_last(sy->ops).ctx, "empty paren expression"); + vec_shrinkby(sy->ops, 1); + return false; + } vec_shrinkby(sy->ops, 1); - return false; + break; } - vec_shrinkby(sy->ops, 1); - return !functions_only; - } - if (vec_last(sy->ops).paren == SY_PAREN_INDEX) { - if (functions_only) - return false; - /* pop off the parenthesis */ - vec_shrinkby(sy->ops, 1); - /* then apply the index operator */ - if (!parser_sy_apply_operator(parser, sy)) - return false; - return true; - } - if (vec_last(sy->ops).paren == SY_PAREN_TERNARY) { - if (functions_only) - return false; - if (vec_last(parser->pot) != POT_TERNARY1) { - parseerror(parser, "mismatched colon in ternary expression (missing closing paren?)"); - return false; + if (vec_last(sy->paren) == PAREN_INDEX) { + vec_pop(sy->paren); + /* pop off the parenthesis */ + vec_shrinkby(sy->ops, 1); + /* then apply the index operator */ + if (!parser_sy_apply_operator(parser, sy)) + return false; + break; } - vec_last(parser->pot) = POT_TERNARY2; - /* pop off the parenthesis */ - vec_shrinkby(sy->ops, 1); - return true; + if (vec_last(sy->paren) == PAREN_TERNARY1) { + vec_last(sy->paren) = PAREN_TERNARY2; + /* pop off the parenthesis */ + vec_shrinkby(sy->ops, 1); + break; + } + compile_error(vec_last(sy->ops).ctx, "invalid parenthesis"); + return false; } if (!parser_sy_apply_operator(parser, sy)) return false; @@ -1666,323 +1663,227 @@ static ast_expression* parse_vararg_do(parser_t *parser) static ast_expression* parse_vararg(parser_t *parser) { - bool old_noops = parser->lex->flags.noops; - enum parser_pot *old_pot = parser->pot; + bool old_noops = parser->lex->flags.noops; ast_expression *out; - parser->pot = NULL; parser->lex->flags.noops = true; out = parse_vararg_do(parser); - parser->pot = old_pot; parser->lex->flags.noops = old_noops; return out; } -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels) +static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) { - ast_expression *expr = NULL; - shunt sy; - bool wantop = false; - /* 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; - - /* count the parens because an if starts with one, so the - * end of a condition is an unmatched closing paren - */ - int parens = 0; - int ternaries = 0; - - sy.out = NULL; - sy.ops = NULL; - - parser->lex->flags.noops = false; - - parser_reclassify_token(parser); - - while (true) + if (OPTS_FLAG(TRANSLATABLE_STRINGS) && + parser->tok == TOKEN_IDENT && + !strcmp(parser_tokval(parser), "_")) { - if (OPTS_FLAG(TRANSLATABLE_STRINGS) && - parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_")) - { - /* a translatable string */ - ast_value *val; - - if (wantop) { - parseerror(parser, "expected operator or end of statement, got constant"); - goto onerr; - } + /* a translatable string */ + ast_value *val; - parser->lex->flags.noops = true; - if (!parser_next(parser) || parser->tok != '(') { - parseerror(parser, "use _(\"string\") to create a translatable string constant"); - goto onerr; - } - parser->lex->flags.noops = false; - if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { - parseerror(parser, "expected a constant string in translatable-string extension"); - goto onerr; - } - val = parser_const_string(parser, parser_tokval(parser), true); - wantop = true; - if (!val) - return NULL; - vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); - DEBUGSHUNTDO(con_out("push string\n")); + parser->lex->flags.noops = true; + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "use _(\"string\") to create a translatable string constant"); + return false; + } + parser->lex->flags.noops = false; + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "expected a constant string in translatable-string extension"); + return false; + } + val = parser_const_string(parser, parser_tokval(parser), true); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); - if (!parser_next(parser) || parser->tok != ')') { - parseerror(parser, "expected closing paren after translatable string"); - goto onerr; - } + if (!parser_next(parser) || parser->tok != ')') { + parseerror(parser, "expected closing paren after translatable string"); + return false; + } + return true; + } + else if (parser->tok == TOKEN_DOTS) + { + ast_expression *va; + if (!OPTS_FLAG(VARIADIC_ARGS)) { + parseerror(parser, "cannot access varargs (try -fvariadic-args)"); + return false; } - else if (parser->tok == TOKEN_DOTS) + va = parse_vararg(parser); + if (!va) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), va)); + return true; + } + else if (parser->tok == TOKEN_FLOATCONST) { + ast_value *val; + val = parser_const_float(parser, (parser_token(parser)->constval.f)); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)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)); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_STRINGCONST) { + ast_value *val; + val = parser_const_string(parser, parser_tokval(parser), false); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_VECTORCONST) { + ast_value *val; + val = parser_const_vector(parser, parser_token(parser)->constval.v); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_IDENT) + { + const char *ctoken = parser_tokval(parser); + ast_expression *prev = vec_size(sy->out) ? vec_last(sy->out).out : NULL; + ast_expression *var; + /* 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)) { - ast_expression *va; - if (!OPTS_FLAG(VARIADIC_ARGS)) { - parseerror(parser, "cannot access varargs (try -fvariadic-args)"); - goto onerr; - } - if (wantop) { - parseerror(parser, "expected operator or end of statement"); - goto onerr; - } - wantop = true; - va = parse_vararg(parser); - if (!va) - goto onerr; - vec_push(sy.out, syexp(parser_ctx(parser), va)); - DEBUGSHUNTDO(con_out("push `...`\n")); + /* When adding more intrinsics, fix the above condition */ + prev = NULL; } - else if (parser->tok == TOKEN_IDENT) + if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1]) { - const char *ctoken = parser_tokval(parser); - ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL; - ast_expression *var; - if (wantop) { - parseerror(parser, "expected operator or end of statement"); - goto onerr; + var = (ast_expression*)parser->const_vec[ctoken[0]-'x']; + } else { + var = parser_find_var(parser, parser_tokval(parser)); + if (!var) + var = parser_find_field(parser, parser_tokval(parser)); + } + if (!var && with_labels) { + var = (ast_expression*)parser_find_label(parser, parser_tokval(parser)); + if (!with_labels) { + ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true); + var = (ast_expression*)lbl; + vec_push(parser->labels, lbl); } - wantop = true; - /* 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)) - { - /* When adding more intrinsics, fix the above condition */ - prev = NULL; + } + if (!var) { + /* intrinsics */ + if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { + var = (ast_expression*)intrinsic_debug_typestring; } - if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1]) + else { - var = (ast_expression*)parser->const_vec[ctoken[0]-'x']; - } else { - var = parser_find_var(parser, parser_tokval(parser)); - if (!var) - var = parser_find_field(parser, parser_tokval(parser)); - } - if (!var && with_labels) { - var = (ast_expression*)parser_find_label(parser, parser_tokval(parser)); - if (!with_labels) { - ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true); - var = (ast_expression*)lbl; - vec_push(parser->labels, lbl); - } - } - if (!var) { - /* intrinsics */ - if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { - var = (ast_expression*)intrinsic_debug_typestring; - } - else - { - size_t i; - char *correct = NULL; - - /* - * sometimes people use preprocessing predefs without enabling them - * i've done this thousands of times already myself. Lets check for - * it in the predef table. And diagnose it better :) - */ - if (!OPTS_FLAG(FTEPP_PREDEFS)) { - for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) { - if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) { - parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); - goto onerr; - } + char *correct = NULL; + size_t i; + + /* + * sometimes people use preprocessing predefs without enabling them + * i've done this thousands of times already myself. Lets check for + * it in the predef table. And diagnose it better :) + */ + if (!OPTS_FLAG(FTEPP_PREDEFS)) { + for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) { + if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) { + parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); + return false; } } + } - /* - * TODO: determine the best score for the identifier: be it - * a variable, a field. - * - * We should also consider adding correction tables for - * other things as well. - */ - if (opts.correction) { - correction_t corr; - correct_init(&corr); - - for (i = 0; i < vec_size(parser->correct_variables); i++) { - correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); - if (strcmp(correct, parser_tokval(parser))) { - break; - } else if (correct) { - mem_d(correct); - correct = NULL; - } - } - correct_free(&corr); - - if (correct) { - parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + /* + * TODO: determine the best score for the identifier: be it + * a variable, a field. + * + * We should also consider adding correction tables for + * other things as well. + */ + if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) { + correction_t corr; + correct_init(&corr); + + for (i = 0; i < vec_size(parser->correct_variables); i++) { + correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); + if (strcmp(correct, parser_tokval(parser))) { + break; + } else if (correct) { mem_d(correct); - goto onerr; + correct = NULL; } } - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); - goto onerr; - } - } - else - { - if (ast_istype(var, ast_value)) { - ((ast_value*)var)->uses++; - } - else if (ast_istype(var, ast_member)) { - ast_member *mem = (ast_member*)var; - if (ast_istype(mem->owner, ast_value)) - ((ast_value*)(mem->owner))->uses++; + correct_free(&corr); + + if (correct) { + parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + mem_d(correct); + return false; + } } + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + return false; } - vec_push(sy.out, syexp(parser_ctx(parser), var)); - DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser))); - } - else if (parser->tok == TOKEN_FLOATCONST) { - ast_value *val; - if (wantop) { - parseerror(parser, "expected operator or end of statement, got constant"); - goto onerr; - } - wantop = true; - val = parser_const_float(parser, (parser_token(parser)->constval.f)); - if (!val) - return NULL; - vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); - DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f)); - } - else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) { - ast_value *val; - if (wantop) { - parseerror(parser, "expected operator or end of statement, got constant"); - goto onerr; - } - wantop = true; - val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); - if (!val) - return NULL; - vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); - DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i)); - } - else if (parser->tok == TOKEN_STRINGCONST) { - ast_value *val; - if (wantop) { - parseerror(parser, "expected operator or end of statement, got constant"); - goto onerr; - } - wantop = true; - val = parser_const_string(parser, parser_tokval(parser), false); - if (!val) - return NULL; - vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); - DEBUGSHUNTDO(con_out("push string\n")); - } - else if (parser->tok == TOKEN_VECTORCONST) { - ast_value *val; - if (wantop) { - parseerror(parser, "expected operator or end of statement, got constant"); - goto onerr; - } - wantop = true; - val = parser_const_vector(parser, parser_token(parser)->constval.v); - if (!val) - return NULL; - vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); - DEBUGSHUNTDO(con_out("push '%g %g %g'\n", - parser_token(parser)->constval.v.x, - parser_token(parser)->constval.v.y, - parser_token(parser)->constval.v.z)); - } - else if (parser->tok == '(') { - parseerror(parser, "internal error: '(' should be classified as operator"); - goto onerr; - } - else if (parser->tok == '[') { - parseerror(parser, "internal error: '[' should be classified as operator"); - goto onerr; } - else if (parser->tok == ')') { - if (wantop) { - DEBUGSHUNTDO(con_out("do[op] )\n")); - --parens; - if (parens < 0) - break; - /* we do expect an operator next */ - /* closing an opening paren */ - if (!parser_close_paren(parser, &sy, false)) - goto onerr; - if (vec_last(parser->pot) != POT_PAREN) { - parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)"); - goto onerr; - } - vec_pop(parser->pot); - } else { - DEBUGSHUNTDO(con_out("do[nop] )\n")); - --parens; - if (parens < 0) - break; - /* allowed for function calls */ - if (!parser_close_paren(parser, &sy, true)) - goto onerr; - if (vec_last(parser->pot) != POT_PAREN) { - parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)"); - goto onerr; - } - vec_pop(parser->pot); + else + { + if (ast_istype(var, ast_value)) { + ((ast_value*)var)->uses++; } - wantop = true; - } - else if (parser->tok == ']') { - if (!wantop) - parseerror(parser, "operand expected"); - --parens; - if (parens < 0) - break; - if (!parser_close_paren(parser, &sy, false)) - goto onerr; - if (vec_last(parser->pot) != POT_PAREN) { - parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)"); - goto onerr; + else if (ast_istype(var, ast_member)) { + ast_member *mem = (ast_member*)var; + if (ast_istype(mem->owner, ast_value)) + ((ast_value*)(mem->owner))->uses++; } - vec_pop(parser->pot); - wantop = true; } - else if (parser->tok == TOKEN_TYPENAME) { + vec_push(sy->out, syexp(parser_ctx(parser), var)); + return true; + } + parseerror(parser, "unexpected token `%s`", parser_tokval(parser)); + return false; +} + +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels) +{ + ast_expression *expr = NULL; + shunt sy; + size_t i; + bool wantop = false; + /* 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; + + /* count the parens because an if starts with one, so the + * end of a condition is an unmatched closing paren + */ + int ternaries = 0; + + memset(&sy, 0, sizeof(sy)); + + parser->lex->flags.noops = false; + + parser_reclassify_token(parser); + + while (true) + { + if (parser->tok == TOKEN_TYPENAME) { parseerror(parser, "unexpected typename"); goto onerr; } - else if (parser->tok != TOKEN_OPERATOR) { - if (wantop) { - parseerror(parser, "expected operator or end of statement"); - goto onerr; - } - break; - } - else + + if (parser->tok == TOKEN_OPERATOR) { /* classify the operator */ const oper_info *op; @@ -2004,7 +1905,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma op = &operators[o]; /* when declaring variables, a comma starts a new variable */ - if (op->id == opid1(',') && !parens && stopatcomma) { + if (op->id == opid1(',') && !vec_size(sy.paren) && stopatcomma) { /* fixup the token */ parser->tok = ','; break; @@ -2017,12 +1918,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } if (op->id == opid1(',')) { - if (vec_size(parser->pot) && vec_last(parser->pot) == POT_TERNARY2) { + if (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { (void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression"); } } - if (vec_size(sy.ops) && !vec_last(sy.ops).paren) + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) olast = &operators[vec_last(sy.ops).etype-1]; #define IsAssignOp(x) (\ @@ -2039,7 +1940,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (warn_truthvalue) { if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) || (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) || - (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id)) + (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id)) ) { (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value"); @@ -2053,7 +1954,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma { if (!parser_sy_apply_operator(parser, &sy)) goto onerr; - if (vec_size(sy.ops) && !vec_last(sy.ops).paren) + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) olast = &operators[vec_last(sy.ops).etype-1]; else olast = NULL; @@ -2062,14 +1963,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (op->id == opid1('(')) { if (wantop) { size_t sycount = vec_size(sy.out); - DEBUGSHUNTDO(con_out("push [op] (\n")); - ++parens; vec_push(parser->pot, POT_PAREN); /* we expected an operator, this is the function-call operator */ - vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_FUNC, sycount-1)); + vec_push(sy.paren, PAREN_FUNC); + vec_push(sy.ops, syparen(parser_ctx(parser), sycount-1)); + vec_push(sy.argc, 0); } else { - ++parens; vec_push(parser->pot, POT_PAREN); - vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0)); - DEBUGSHUNTDO(con_out("push [nop] (\n")); + vec_push(sy.paren, PAREN_EXPR); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); } wantop = false; } else if (op->id == opid1('[')) { @@ -2077,42 +1977,104 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma parseerror(parser, "unexpected array subscript"); goto onerr; } - ++parens; vec_push(parser->pot, POT_PAREN); + vec_push(sy.paren, PAREN_INDEX); /* push both the operator and the paren, this makes life easier */ vec_push(sy.ops, syop(parser_ctx(parser), op)); - vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0)); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); wantop = false; } else if (op->id == opid2('?',':')) { vec_push(sy.ops, syop(parser_ctx(parser), op)); - vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0)); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); wantop = false; ++ternaries; - vec_push(parser->pot, POT_TERNARY1); + vec_push(sy.paren, PAREN_TERNARY1); } else if (op->id == opid2(':','?')) { - if (!vec_size(parser->pot)) { + if (!vec_size(sy.paren)) { parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)"); goto onerr; } - if (vec_last(parser->pot) != POT_TERNARY1) { + if (vec_last(sy.paren) != PAREN_TERNARY1) { parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)"); goto onerr; } - if (!parser_close_paren(parser, &sy, false)) + if (!parser_close_paren(parser, &sy)) goto onerr; vec_push(sy.ops, syop(parser_ctx(parser), op)); wantop = false; --ternaries; } else { - DEBUGSHUNTDO(con_out("push operator %s\n", op->op)); vec_push(sy.ops, syop(parser_ctx(parser), op)); wantop = !!(op->flags & OP_SUFFIX); } } + else if (parser->tok == ')') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + if (!vec_size(sy.paren)) + break; + if (wantop) { + if (vec_last(sy.paren) == PAREN_TERNARY1) { + parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + } else { + /* must be a function call without parameters */ + if (vec_last(sy.paren) != PAREN_FUNC) { + parseerror(parser, "closing paren in invalid position"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + } + wantop = true; + } + else if (parser->tok == '(') { + parseerror(parser, "internal error: '(' should be classified as operator"); + goto onerr; + } + else if (parser->tok == '[') { + parseerror(parser, "internal error: '[' should be classified as operator"); + goto onerr; + } + else if (parser->tok == ']') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + if (!vec_size(sy.paren)) + break; + if (vec_last(sy.paren) != PAREN_INDEX) { + parseerror(parser, "mismatched parentheses, unexpected ']'"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + wantop = true; + } + else if (!wantop) { + if (!parse_sya_operand(parser, &sy, with_labels)) + goto onerr; +#if 0 + if (vec_size(sy.paren) && vec_last(sy.ops).isparen && vec_last(sy.paren) == PAREN_FUNC) + vec_last(sy.argc)++; +#endif + wantop = true; + } + else { + parseerror(parser, "expected operator or end of statement"); + goto onerr; + } + if (!parser_next(parser)) { goto onerr; } if (parser->tok == ';' || - (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}'))) + ((!vec_size(sy.paren) || (vec_size(sy.paren) == 1 && vec_last(sy.paren) == PAREN_TERNARY2)) && + (parser->tok == ']' || parser->tok == ')' || parser->tok == '}'))) { break; } @@ -2131,18 +2093,24 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma expr = sy.out[0].out; vec_free(sy.out); vec_free(sy.ops); - DEBUGSHUNTDO(con_out("shunt done\n")); - if (vec_size(parser->pot)) { - parseerror(parser, "internal error: vec_size(parser->pot) = %lu", (unsigned long)vec_size(parser->pot)); + if (vec_size(sy.paren)) { + parseerror(parser, "internal error: vec_size(sy.paren) = %lu", (unsigned long)vec_size(sy.paren)); return NULL; } - vec_free(parser->pot); + vec_free(sy.paren); + vec_free(sy.argc); return expr; onerr: parser->lex->flags.noops = true; + for (i = 0; i < vec_size(sy.out); ++i) { + if (sy.out[i].out) + ast_unref(sy.out[i].out); + } vec_free(sy.out); vec_free(sy.ops); + vec_free(sy.paren); + vec_free(sy.argc); return NULL; } @@ -2254,6 +2222,12 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, ast_unary *unary; ast_expression *prev; + if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) { + char ty[1024]; + ast_type_to_string(cond, ty, sizeof(ty)); + compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty); + } + if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) { prev = cond; @@ -2688,7 +2662,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou if (typevar || parser->tok == TOKEN_TYPENAME) { #if 0 - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { if (parsewarning(parser, WARN_EXTENSIONS, "current standard does not allow variable declarations in for-loop initializers")) goto onerr; @@ -2733,11 +2707,12 @@ 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); increment = parse_expression_leave(parser, false, false, false); if (!increment) goto onerr; if (!ast_side_effects(increment)) { - if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) goto onerr; } } @@ -3417,7 +3392,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * parseerror(parser, "cannot declare a variable from here"); return false; } - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } @@ -3484,7 +3459,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else if (!strcmp(parser_tokval(parser), "for")) { - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?")) return false; } @@ -3583,12 +3558,13 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else { + lex_ctx ctx = parser_ctx(parser); ast_expression *exp = parse_expression(parser, false, false); if (!exp) return false; *out = exp; if (!ast_side_effects(exp)) { - if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) return false; } return true; @@ -4005,6 +3981,14 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } + 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); + parser_enterblock(parser); for (parami = 0; parami < vec_size(var->expression.params); ++parami) { @@ -4021,7 +4005,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (!create_vector_members(param, me)) { ast_block_delete(block); - return false; + goto enderrfn; } for (e = 0; e < 3; ++e) { @@ -4030,14 +4014,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } - 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); - if (var->argcounter) { ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT); parser_addlocal(parser, argc->name, (ast_expression*)argc); @@ -4054,15 +4030,17 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (!parser_create_array_setter_proto(parser, varargs, name)) { ast_delete(varargs); ast_block_delete(block); - goto enderr; + goto enderrfn; } snprintf(name, sizeof(name), "%s##va##GET", var->name); if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) { ast_delete(varargs); ast_block_delete(block); - goto enderr; + goto enderrfn; } func->varargs = varargs; + + func->fixedparams = parser_const_float(parser, vec_size(var->expression.params)); } parser->function = func; @@ -4083,17 +4061,17 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (parser->tok == ';') return parser_next(parser); - else if (opts.standard == COMPILER_QCC) + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)"); return retval; enderrfn: + (void)!parser_leaveblock(parser); vec_pop(parser->functions); ast_function_delete(func); var->constval.vfunc = NULL; enderr: - (void)!parser_leaveblock(parser); parser->function = old; return false; } @@ -4599,7 +4577,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) vec_free(params); /* sanity check */ - if (vec_size(params) > 8 && opts.standard == COMPILER_QCC) + if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ @@ -4813,7 +4791,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } /* now there may be function parens again */ - if (parser->tok == '(' && opts.standard == COMPILER_QCC) + if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); if (parser->tok == '(' && wasarray) parseerror(parser, "arrays as part of a return type is not supported"); @@ -4952,7 +4930,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Part 0: finish the type */ if (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -4975,7 +4953,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } /* for functions returning functions */ while (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -5045,7 +5023,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; */ } - if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) && + if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) && (old = parser_find_global(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5117,7 +5095,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield ast_delete(var); var = proto; } - if (opts.standard == COMPILER_QCC && + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC && (old = parser_find_field(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5148,7 +5126,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); var = NULL; goto skipvar; @@ -5328,7 +5306,7 @@ skipvar: break; } - if (localblock && opts.standard == COMPILER_QCC) { + if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_LOCAL_CONSTANTS, "initializing expression turns variable `%s` into a constant in this standard", var->name) ) @@ -5348,7 +5326,7 @@ skipvar: break; } } - else if (opts.standard == COMPILER_QCC) { + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { parseerror(parser, "expected '=' before function body in this standard"); } @@ -5459,7 +5437,7 @@ skipvar: } } else { int cvq; - shunt sy = { NULL, NULL }; + shunt sy = { NULL, NULL, NULL, NULL }; cvq = var->cvq; var->cvq = CV_NONE; vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var)); @@ -5580,7 +5558,7 @@ static bool parser_global_statement(parser_t *parser) } else { - parseerror(parser, "unexpected token: %s", parser->lex->tok.value); + parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value); return false; } return true; @@ -5703,11 +5681,13 @@ bool parser_init() if (OPTS_FLAG(UNTYPED_NIL)) util_htset(parser->htglobals, "nil", (void*)parser->nil); + parser->max_param_count = 1; + parser->const_vec[0] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); parser->const_vec[1] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); parser->const_vec[2] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); - if (opts.add_info) { + if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING); parser->reserved_version->cvq = CV_CONST; parser->reserved_version->hasvalue = true; @@ -5892,8 +5872,8 @@ bool parser_finish(const char *output) continue; asvalue = (ast_value*)(parser->globals[i]); if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) { - retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, - "unused global: `%s`", asvalue->name); + retval = retval && !compile_warning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, + "unused global: `%s`", asvalue->name); } if (!ast_global_codegen(asvalue, ir, false)) { con_out("failed to generate global %s\n", asvalue->name); @@ -5996,7 +5976,7 @@ bool parser_finish(const char *output) return false; } } - if (opts.dump) + if (OPTS_OPTION_BOOL(OPTION_DUMP)) ir_builder_dump(ir, con_out); for (i = 0; i < vec_size(parser->functions); ++i) { if (!ir_function_finalize(parser->functions[i]->ir_func)) { @@ -6013,7 +5993,7 @@ bool parser_finish(const char *output) } if (retval) { - if (opts.dumpfin) + if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) ir_builder_dump(ir, con_out); generate_checksum(parser);