X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=cb02e742b5a117acc87bf7dc5d5e084fbb7cb477;hp=5bb7652627f9a4ff248accc82aab60cc86379550;hb=7d14fdf530e49cb04b13572a790178c4c4bf75dc;hpb=d8254cede0368482090009c21e7e49c7257a783e diff --git a/parser.c b/parser.c index 5bb7652..cb02e74 100644 --- a/parser.c +++ b/parser.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 + * Copyright (C) 2012, 2013 * Wolfgang Bumiller * Dale Weiler * @@ -26,6 +26,7 @@ #include "gmqcc.h" #include "lexer.h" +#include "ast.h" /* beginning of locals */ #define PARSER_HT_LOCALS 2 @@ -110,8 +111,8 @@ 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, bool truthvalue); -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, ...) { @@ -299,6 +300,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); @@ -539,6 +549,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(',')) { @@ -1482,7 +1502,7 @@ static void parser_reclassify_token(parser_t *parser) } } -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue) +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels) { ast_expression *expr = NULL; shunt sy; @@ -1579,14 +1599,36 @@ 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 { + /* + * 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)) { + size_t i; + 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; + } + } + } + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); goto onerr; } @@ -1907,9 +1949,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, false); + ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels); if (!e) return NULL; if (!parser_next(parser)) { @@ -2059,7 +2101,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, true); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2180,7 +2222,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, true); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2295,7 +2337,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, true); + cond = parse_expression_leave(parser, false, true, false); if (!cond) return false; /* closing paren */ @@ -2424,7 +2466,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou } else if (parser->tok != ';') { - initexpr = parse_expression_leave(parser, false, false); + initexpr = parse_expression_leave(parser, false, false, false); if (!initexpr) goto onerr; } @@ -2441,7 +2483,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, true); + cond = parse_expression_leave(parser, false, true, false); if (!cond) goto onerr; } @@ -2458,7 +2500,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, false); + increment = parse_expression_leave(parser, false, false, false); if (!increment) goto onerr; if (!ast_side_effects(increment)) { @@ -2515,7 +2557,7 @@ 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; @@ -2801,7 +2843,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, false); + operand = parse_expression_leave(parser, false, false, false); if (!operand) return false; @@ -2865,7 +2907,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, false); + swcase.value = parse_expression_leave(parser, false, false, false); if (!swcase.value) { ast_delete(switchnode); parseerror(parser, "expected expression for case"); @@ -2956,25 +2998,74 @@ 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; + + if (!side) + return NULL; + + if (ast_istype(side, ast_ternary)) { + on_true = parse_goto_computed(parser, ((ast_ternary*)side)->on_true); + on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false); + + if (!on_true || !on_false) { + parseerror(parser, "expected label or expression in ternary"); + if (((ast_ternary*)side)->on_false) ast_unref(((ast_ternary*)side)->on_false); + if (((ast_ternary*)side)->on_true) ast_unref(((ast_ternary*)side)->on_true); + return NULL; + } + + return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->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)); + 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 != ';') { @@ -3209,10 +3300,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"); @@ -3237,7 +3336,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; @@ -3398,7 +3497,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (!parser_next(parser)) return false; - framenum = parse_expression_leave(parser, true, false); + framenum = parse_expression_leave(parser, true, false, false); if (!framenum) { parseerror(parser, "expected a framenumber constant in[frame,think] notation"); return false; @@ -3446,7 +3545,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) nextthink = (ast_expression*)thinkfunc; } else { - nextthink = parse_expression_leave(parser, true, false); + nextthink = parse_expression_leave(parser, true, false, false); if (!nextthink) { ast_unref(framenum); parseerror(parser, "expected a think-function in [frame,think] notation"); @@ -4112,7 +4211,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) return NULL; } - cexp = parse_expression_leave(parser, true, false); + cexp = parse_expression_leave(parser, true, false, false); if (!cexp || !ast_istype(cexp, ast_value)) { if (cexp) @@ -4561,35 +4660,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))) @@ -4885,7 +4977,7 @@ skipvar: ast_expression *cexp; ast_value *cval; - cexp = parse_expression_leave(parser, true, false); + cexp = parse_expression_leave(parser, true, false, false); if (!cexp) break; @@ -4899,8 +4991,7 @@ skipvar: } else { - if (opts.standard != COMPILER_GMQCC && - !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) && qualifier != CV_VAR) { var->cvq = CV_CONST;