X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=d374af7329a2a272ddc1cc3a0ce31e1a2fe305ee;hb=f4d1ef47401551062b5c46ae7de534a496f52525;hp=a191c535c4ddf94d8dd3a036526466c1f9a93198;hpb=921877e8a4d7b5221762490d3f6e3b3666e4381b;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index a191c53..d374af7 100644 --- a/parser.c +++ b/parser.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2012 + * Copyright (C) 2012, 2013 * Wolfgang Bumiller - * + * Dale Weiler + * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to @@ -25,6 +26,7 @@ #include "gmqcc.h" #include "lexer.h" +#include "ast.h" /* beginning of locals */ #define PARSER_HT_LOCALS 2 @@ -72,6 +74,10 @@ typedef struct { ht htglobals; ht *typedefs; + /* same as above but for the spelling corrector */ + correct_trie_t **correct_variables; + size_t ***correct_variables_score; /* vector of vector of size_t* */ + /* not to be used directly, we use the hash table */ ast_expression **_locals; size_t *_blocklocals; @@ -104,13 +110,13 @@ static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); static bool parse_typedef(parser_t *parser); -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring); static ast_block* parse_block(parser_t *parser); static bool parse_block_into(parser_t *parser, ast_block *block); static bool parse_statement_or_block(parser_t *parser, ast_expression **out); static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases); -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma); -static ast_expression* parse_expression(parser_t *parser, bool stopatcomma); +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); +static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels); static void parseerror(parser_t *parser, const char *fmt, ...) { @@ -298,6 +304,15 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name) return ( ast_expression*)util_htget(parser->htfields, name); } +static ast_expression* parser_find_label(parser_t *parser, const char *name) +{ + size_t i; + for(i = 0; i < vec_size(parser->labels); i++) + if (!strcmp(parser->labels[i]->name, name)) + return (ast_expression*)parser->labels[i]; + return NULL; +} + static ast_expression* parser_find_global(parser_t *parser, const char *name) { return (ast_expression*)util_htget(parser->htglobals, name); @@ -538,6 +553,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) 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]->expression.vtype == TYPE_NOEXPR && + !(i != 0 && op->id == opid2('?',':'))) + { + if (ast_istype(exprs[i], ast_label)) + compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier"); + else + compile_error(ast_ctx(exprs[i]), "not an expression"); + (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i); + } } if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) { @@ -938,7 +963,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ( (generated_op == INSTR_OR) ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1])) : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) ) - ? 0 : 1); + ? 1 : 0); } else { @@ -982,14 +1007,14 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } vec_pop(parser->pot); - if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) { + 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)); parseerror(parser, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2); return false; } if (CanConstFold1(exprs[0])) - out = (ConstF(0) ? exprs[1] : exprs[2]); + out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]); else out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); break; @@ -1040,8 +1065,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 == AINSTR_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)); @@ -1073,8 +1097,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ast_type_to_string(exprs[1], ty2, sizeof(ty2)); parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } - else if (exprs[1]->expression.vtype != TYPE_NIL && - !ast_compare_type(exprs[0], exprs[1])) + else if (!ast_compare_type(exprs[0], exprs[1])) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); @@ -1373,7 +1396,6 @@ static bool parser_close_call(parser_t *parser, shunt *sy) return false; } - if (!fun->expression.next) { parseerror(parser, "could not determine function return type"); return false; @@ -1381,14 +1403,23 @@ static bool parser_close_call(parser_t *parser, shunt *sy) ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL); if (fun->expression.flags & AST_FLAG_DEPRECATED) { - if (!fval) - return !parsewarning(parser, WARN_DEPRECATED, "call to function (which is marked deprecated)\n" - "-> it has been declared here: %s:%i", - ast_ctx(fun).file, ast_ctx(fun).line); - else - return !parsewarning(parser, WARN_DEPRECATED, "call to `%s` (which is marked deprecated)\n" - "-> `%s` declared here: %s:%i", - fval->name, fval->name, ast_ctx(fun).file, ast_ctx(fun).line); + if (!fval) { + return !parsewarning(parser, WARN_DEPRECATED, + "call to function (which is marked deprecated)\n", + "-> it has been declared here: %s:%i", + ast_ctx(fun).file, ast_ctx(fun).line); + } + if (!fval->desc) { + return !parsewarning(parser, WARN_DEPRECATED, + "call to `%s` (which is marked deprecated)\n" + "-> `%s` declared here: %s:%i", + fval->name, fval->name, ast_ctx(fun).file, ast_ctx(fun).line); + } + return !parsewarning(parser, WARN_DEPRECATED, + "call to `%s` (deprecated: %s)\n" + "-> `%s` declared here: %s:%i", + fval->name, fval->desc, fval->name, ast_ctx(fun).file, + ast_ctx(fun).line); } if (vec_size(fun->expression.params) != paramcount && @@ -1396,35 +1427,18 @@ static bool parser_close_call(parser_t *parser, shunt *sy) vec_size(fun->expression.params) < paramcount)) { const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many"; - if (opts.standard == COMPILER_GMQCC) - { - if (fval) - parseerror(parser, "too %s parameters for call to %s: expected %i, got %i\n" - " -> `%s` has been declared here: %s:%i", - fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount, - fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); - else - parseerror(parser, "too %s parameters for function call: expected %i, got %i\n" - " -> it has been declared here: %s:%i", - fewmany, (int)vec_size(fun->expression.params), (int)paramcount, - ast_ctx(fun).file, (int)ast_ctx(fun).line); - return false; - } + if (fval) + return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT, + "too %s parameters for call to %s: expected %i, got %i\n" + " -> `%s` has been declared here: %s:%i", + fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount, + fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); else - { - if (fval) - return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS, - "too %s parameters for call to %s: expected %i, got %i\n" - " -> `%s` has been declared here: %s:%i", - fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount, - fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); - else - return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS, - "too %s parameters for function call: expected %i, got %i\n" - " -> it has been declared here: %s:%i", - fewmany, (int)vec_size(fun->expression.params), (int)paramcount, - ast_ctx(fun).file, (int)ast_ctx(fun).line); - } + return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT, + "too %s parameters for function call: expected %i, got %i\n" + " -> it has been declared here: %s:%i", + fewmany, (int)vec_size(fun->expression.params), (int)paramcount, + ast_ctx(fun).file, (int)ast_ctx(fun).line); } } @@ -1492,12 +1506,16 @@ static void parser_reclassify_token(parser_t *parser) } } -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma) +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels) { ast_expression *expr = NULL; shunt sy; bool wantop = false; bool gotmemberof = 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 @@ -1585,14 +1603,61 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma 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; + } + } + } + + /* + * 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_FLAG(ENHANCED_DIAGNOSTICS)) { + for (i = 0; i < vec_size(parser->correct_variables); i++) { + correct = correct_str(parser->correct_variables[i], parser_tokval(parser)); + if (strcmp(correct, parser_tokval(parser))) { + break; + } else if (correct) { + mem_d(correct); + } + } + + if (correct) { + parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + mem_d(correct); + goto onerr; + } + } parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); goto onerr; } @@ -1774,6 +1839,28 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (vec_size(sy.ops) && !vec_last(sy.ops).paren) olast = &operators[vec_last(sy.ops).etype-1]; +#define IsAssignOp(x) (\ + (x) == opid1('=') || \ + (x) == opid2('+','=') || \ + (x) == opid2('-','=') || \ + (x) == opid2('*','=') || \ + (x) == opid2('/','=') || \ + (x) == opid2('%','=') || \ + (x) == opid2('&','=') || \ + (x) == opid2('|','=') || \ + (x) == opid3('&','~','=') \ + ) + 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)) + ) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value"); + warn_truthvalue = false; + } + } + while (olast && ( (op->prec < olast->prec) || (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) ) @@ -1891,9 +1978,9 @@ onerr: return NULL; } -static ast_expression* parse_expression(parser_t *parser, bool stopatcomma) +static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels) { - ast_expression *e = parse_expression_leave(parser, stopatcomma); + ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels); if (!e) return NULL; if (!parser_next(parser)) { @@ -1910,6 +1997,10 @@ static void parser_enterblock(parser_t *parser) vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs)); vec_push(parser->_block_ctx, parser_ctx(parser)); + + /* corrector */ + vec_push(parser->correct_variables, correct_trie_new()); + vec_push(parser->correct_variables_score, NULL); } static bool parser_leaveblock(parser_t *parser) @@ -1923,7 +2014,11 @@ static bool parser_leaveblock(parser_t *parser) } util_htdel(vec_last(parser->variables)); + correct_del(vec_last(parser->correct_variables), vec_last(parser->correct_variables_score)); + vec_pop(parser->variables); + vec_pop(parser->correct_variables); + vec_pop(parser->correct_variables_score); if (!vec_size(parser->_blocklocals)) { parseerror(parser, "internal error: parser_leaveblock with no block (2)"); return false; @@ -1950,6 +2045,7 @@ static bool parser_leaveblock(parser_t *parser) vec_pop(parser->typedefs); vec_pop(parser->_block_ctx); + return rv; } @@ -1957,6 +2053,13 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression * { vec_push(parser->_locals, e); util_htset(vec_last(parser->variables), name, (void*)e); + + /* corrector */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + name + ); } static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot) @@ -2043,7 +2146,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) return false; } /* parse the condition */ - cond = parse_expression_leave(parser, false); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2062,6 +2165,8 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) ast_delete(cond); return false; } + if (!ontrue) + ontrue = (ast_expression*)ast_block_new(parser_ctx(parser)); /* check for an else */ if (!strcmp(parser_tokval(parser), "else")) { /* parse into the 'else' branch */ @@ -2164,7 +2269,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression ** return false; } /* parse the condition */ - cond = parse_expression_leave(parser, false); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2279,7 +2384,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression return false; } /* parse the condition */ - cond = parse_expression_leave(parser, false); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2396,17 +2501,19 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou typevar = parser_find_typedef(parser, parser_tokval(parser), 0); if (typevar || parser->tok == TOKEN_TYPENAME) { +#if 0 if (opts.standard != COMPILER_GMQCC) { if (parsewarning(parser, WARN_EXTENSIONS, "current standard does not allow variable declarations in for-loop initializers")) goto onerr; } - if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0)) +#endif + if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL)) goto onerr; } else if (parser->tok != ';') { - initexpr = parse_expression_leave(parser, false); + initexpr = parse_expression_leave(parser, false, false, false); if (!initexpr) goto onerr; } @@ -2423,7 +2530,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou /* parse the condition */ if (parser->tok != ';') { - cond = parse_expression_leave(parser, false); + cond = parse_expression_leave(parser, false, true, false); if (!cond) goto onerr; } @@ -2440,7 +2547,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou /* parse the incrementor */ if (parser->tok != ')') { - increment = parse_expression_leave(parser, false); + increment = parse_expression_leave(parser, false, false, false); if (!increment) goto onerr; if (!ast_side_effects(increment)) { @@ -2497,11 +2604,13 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou } if (parser->tok != ';') { - exp = parse_expression(parser, false); + exp = parse_expression(parser, false, false); if (!exp) return false; - if (exp->expression.vtype != expected->expression.next->expression.vtype) { + if (exp->expression.vtype != TYPE_NIL && + exp->expression.vtype != expected->expression.next->expression.vtype) + { parseerror(parser, "return with invalid expression"); } @@ -2514,10 +2623,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou if (!parser_next(parser)) parseerror(parser, "parse error"); if (expected->expression.next->expression.vtype != TYPE_VOID) { - if (opts.standard != COMPILER_GMQCC) - (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); - else - parseerror(parser, "return without value"); + (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); } ret = ast_return_new(ctx, NULL); } @@ -2574,7 +2680,7 @@ 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 */ -static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags) +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; bool had_var = false; @@ -2617,11 +2723,48 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } - else if (!strcmp(parser_tokval(parser), "deprecated")) { - flags |= AST_FLAG_DEPRECATED; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`deprecated` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; + + + else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) { + flags |= AST_FLAG_DEPRECATED; + *message = NULL; + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if (parser->tok == '(') { + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "`deprecated` attribute missing parameter"); + goto argerr; + } + + *message = util_strdup(parser_tokval(parser)); + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if(parser->tok != ')') { + parseerror(parser, "`deprecated` attribute expected `)` after parameter"); + goto argerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + } + /* no message */ + if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`deprecated` attribute expected `]]`"); + + argerr: /* ugly */ + if (*message) mem_d(*message); + *message = NULL; + *cvq = CV_WRONG; return false; } } @@ -2747,7 +2890,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return false; } /* parse the operand */ - operand = parse_expression_leave(parser, false); + operand = parse_expression_leave(parser, false, false, false); if (!operand) return false; @@ -2780,19 +2923,19 @@ 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)) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) { ast_delete(switchnode); return false; } continue; } - if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags)) + if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags, NULL)) { if (cvq == CV_WRONG) { ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags)) { + if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) { ast_delete(switchnode); return false; } @@ -2811,7 +2954,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * parseerror(parser, "expected expression for case"); return false; } - swcase.value = parse_expression_leave(parser, false); + swcase.value = parse_expression_leave(parser, false, false, false); if (!swcase.value) { ast_delete(switchnode); parseerror(parser, "expected expression for case"); @@ -2902,25 +3045,81 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return true; } +/* parse computed goto sides */ +static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { + ast_expression *on_true; + ast_expression *on_false; + ast_expression *cond; + + if (!*side) + return NULL; + + if (ast_istype(*side, ast_ternary)) { + ast_ternary *tern = (ast_ternary*)*side; + on_true = parse_goto_computed(parser, &tern->on_true); + on_false = parse_goto_computed(parser, &tern->on_false); + + if (!on_true || !on_false) { + parseerror(parser, "expected label or expression in ternary"); + if (on_true) ast_unref(on_true); + if (on_false) ast_unref(on_false); + return NULL; + } + + cond = tern->cond; + tern->cond = NULL; + ast_delete(tern); + *side = NULL; + return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false); + } else if (ast_istype(*side, ast_label)) { + ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name); + ast_goto_set_label(gt, ((ast_label*)*side)); + *side = NULL; + return (ast_expression*)gt; + } + return NULL; +} + static bool parse_goto(parser_t *parser, ast_expression **out) { - size_t i; - ast_goto *gt; + ast_goto *gt = NULL; + ast_expression *lbl; - if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { - parseerror(parser, "expected label name after `goto`"); + if (!parser_next(parser)) return false; + + if (parser->tok != TOKEN_IDENT) { + ast_expression *expression; + + /* could be an expression i.e computed goto :-) */ + if (parser->tok != '(') { + parseerror(parser, "expected label name after `goto`"); + return false; + } + + /* failed to parse expression for goto */ + if (!(expression = parse_expression(parser, false, true)) || + !(*out = parse_goto_computed(parser, &expression))) { + parseerror(parser, "invalid goto expression"); + ast_unref(expression); + return false; + } + + return true; } + /* not computed goto */ gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser)); - - for (i = 0; i < vec_size(parser->labels); ++i) { - if (!strcmp(parser->labels[i]->name, parser_tokval(parser))) { - ast_goto_set_label(gt, parser->labels[i]); - break; + lbl = parser_find_label(parser, gt->name); + if (lbl) { + if (!ast_istype(lbl, ast_label)) { + parseerror(parser, "internal error: label is not an ast_label"); + ast_delete(gt); + return false; } + ast_goto_set_label(gt, (ast_label*)lbl); } - if (i == vec_size(parser->labels)) + else vec_push(parser->gotos, gt); if (!parser_next(parser) || parser->tok != ';') { @@ -3008,9 +3207,10 @@ static bool parse_pragma(parser_t *parser) static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { bool noref, is_static; - int cvq = CV_NONE; - uint32_t qflags = 0; + int cvq = CV_NONE; + uint32_t qflags = 0; ast_value *typevar = NULL; + char *vstring = NULL; *out = NULL; @@ -3028,15 +3228,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) return false; return true; } - else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags)) + else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags, &vstring)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags); + return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_KEYWORD) { @@ -3154,10 +3354,18 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * parseerror(parser, "label must be an identifier"); return false; } - label = ast_label_new(parser_ctx(parser), parser_tokval(parser)); - if (!label) - return false; - vec_push(parser->labels, label); + label = (ast_label*)parser_find_label(parser, parser_tokval(parser)); + if (label) { + if (!label->undefined) { + parseerror(parser, "label `%s` already defined", label->name); + return false; + } + label->undefined = false; + } + else { + label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false); + vec_push(parser->labels, label); + } *out = (ast_expression*)label; if (!parser_next(parser)) { parseerror(parser, "parse error after label"); @@ -3182,7 +3390,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else { - ast_expression *exp = parse_expression(parser, false); + ast_expression *exp = parse_expression(parser, false, false); if (!exp) return false; *out = exp; @@ -3343,7 +3551,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (!parser_next(parser)) return false; - framenum = parse_expression_leave(parser, true); + framenum = parse_expression_leave(parser, true, false, false); if (!framenum) { parseerror(parser, "expected a framenumber constant in[frame,think] notation"); return false; @@ -3388,10 +3596,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var) vec_push(parser->globals, (ast_expression*)thinkfunc); util_htset(parser->htglobals, thinkfunc->name, thinkfunc); + nextthink = (ast_expression*)thinkfunc; } else { - nextthink = parse_expression_leave(parser, true); + nextthink = parse_expression_leave(parser, true, false, false); if (!nextthink) { ast_unref(framenum); parseerror(parser, "expected a think-function in [frame,think] notation"); @@ -4057,7 +4266,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) return NULL; } - cexp = parse_expression_leave(parser, true); + cexp = parse_expression_leave(parser, true, false, false); if (!cexp || !ast_istype(cexp, ast_value)) { if (cexp) @@ -4319,7 +4528,7 @@ static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, cons } av = (var ->expression.flags & AST_FLAG_NORETURN); ao = (proto->expression.flags & AST_FLAG_NORETURN); - if (av != ao) { + if (!av != !ao) { return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES, "`%s` declared with different attributes%s\n" " -> previous declaration here: %s:%i", @@ -4330,7 +4539,7 @@ static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, cons return true; } -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags) +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring) { ast_value *var; ast_value *proto; @@ -4398,6 +4607,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield var->cvq = qualifier; var->expression.flags |= qflags; + if (var->expression.flags & AST_FLAG_DEPRECATED) + var->desc = vstring; /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) @@ -4478,6 +4689,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto = (ast_value*)old; + proto->desc = var->desc; if (!ast_compare_type((ast_expression*)proto, (ast_expression*)var)) { parseerror(parser, "conflicting types for `%s`, previous declaration was here: %s:%i", proto->name, @@ -4490,6 +4702,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name); if (!parser_check_qualifiers(parser, var, proto)) { retval = false; + if (proto->desc) + mem_d(proto->desc); proto = NULL; goto cleanup; } @@ -4501,35 +4715,28 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield { /* other globals */ if (old) { - if (opts.standard == COMPILER_GMQCC) { - parseerror(parser, "global `%s` already declared here: %s:%i", - var->name, ast_ctx(old).file, ast_ctx(old).line); + if (parsewarning(parser, WARN_DOUBLE_DECLARATION, + "global `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line)) + { retval = false; goto cleanup; - } else { - if (parsewarning(parser, WARN_DOUBLE_DECLARATION, - "global `%s` already declared 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"); - retval = false; - proto = NULL; - goto cleanup; - } - if (!parser_check_qualifiers(parser, var, proto)) { - retval = false; - proto = NULL; - goto cleanup; - } - proto->expression.flags |= var->expression.flags; - ast_delete(var); - var = proto; } + proto = (ast_value*)old; + if (!ast_istype(old, ast_value)) { + parseerror(parser, "internal error: not an ast_value"); + retval = false; + proto = NULL; + goto cleanup; + } + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; + ast_delete(var); + var = proto; } if (opts.standard == COMPILER_QCC && (old = parser_find_field(parser, var->name))) @@ -4631,6 +4838,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Add it to the local scope */ util_htset(vec_last(parser->variables), var->name, (void*)var); + + /* corrector */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + var->name + ); + /* now rename the global */ ln = strlen(var->name); vec_append(defname, ln, var->name); @@ -4644,6 +4859,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield for (i = 0; i < 3; ++i) { util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i])); + /* corrector */ + correct_add( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[i]->name + ); + vec_shrinkto(defname, prefix_len); ln = strlen(me[i]->name); vec_append(defname, ln, me[i]->name); @@ -4825,7 +5047,7 @@ skipvar: ast_expression *cexp; ast_value *cval; - cexp = parse_expression_leave(parser, true); + cexp = parse_expression_leave(parser, true, false, false); if (!cexp) break; @@ -4839,8 +5061,7 @@ skipvar: } else { - if (opts.standard != COMPILER_GMQCC && - !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) && qualifier != CV_VAR) { var->cvq = CV_CONST; @@ -4938,19 +5159,20 @@ static bool parser_global_statement(parser_t *parser) bool is_static = false; uint32_t qflags = 0; ast_value *istype = NULL; + char *vstring = NULL; if (parser->tok == TOKEN_IDENT) istype = parser_find_typedef(parser, parser_tokval(parser), 0); if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') { - return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0); + return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL); } - else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags)) + else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags, &vstring)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags); + return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring); } else if (parser->tok == TOKEN_KEYWORD) { @@ -5190,6 +5412,17 @@ void parser_cleanup() vec_free(parser->_blocklocals); vec_free(parser->_locals); + /* corrector */ + for (i = 0; i < vec_size(parser->correct_variables); ++i) { + correct_del(parser->correct_variables[i], parser->correct_variables_score[i]); + } + for (i = 0; i < vec_size(parser->correct_variables_score); ++i) { + vec_free(parser->correct_variables_score[i]); + } + vec_free(parser->correct_variables); + vec_free(parser->correct_variables_score); + + for (i = 0; i < vec_size(parser->_typedefs); ++i) ast_delete(parser->_typedefs[i]); vec_free(parser->_typedefs); @@ -5205,6 +5438,8 @@ void parser_cleanup() vec_free(parser->breaks); vec_free(parser->continues); + ast_value_delete(parser->nil); + mem_d(parser); }