X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=99b7eb7bace4f651eb2fbf37d221cd18a14444cb;hb=be8981a5c3715f5032902ba4d8f8a84715984b07;hp=600b088008d33e9472db1c57988cebb31d7b9f57;hpb=e8d1e5dbc0fdbc87d5ce3a0285a0e3df4a2a4728;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index 600b088..99b7eb7 100644 --- a/parser.c +++ b/parser.c @@ -51,6 +51,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield static ast_block* parse_block(parser_t *parser, bool warnreturn); static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn); static ast_expression* parse_statement_or_block(parser_t *parser); +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); @@ -1331,7 +1332,10 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (!parser_next(parser)) { goto onerr; } - if (parser->tok == ';' || (!parens && parser->tok == ']')) { + if (parser->tok == ';' || + (!parens && parser->tok == ']') || + parser->tok == ':') + { break; } } @@ -1678,7 +1682,208 @@ onerr: return false; } -static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out) +static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *exp = NULL; + ast_return *ret = NULL; + ast_value *expected = parser->function->vtype; + + if (!parser_next(parser)) { + parseerror(parser, "expected return expression"); + return false; + } + + if (parser->tok != ';') { + exp = parse_expression(parser, false); + if (!exp) + return false; + + if (exp->expression.vtype != expected->expression.next->expression.vtype) { + parseerror(parser, "return with invalid expression"); + } + + ret = ast_return_new(exp->expression.node.context, exp); + if (!ret) { + ast_delete(exp); + return false; + } + } else { + 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"); + } + ret = ast_return_new(parser_ctx(parser), NULL); + } + *out = (ast_expression*)ret; + return true; +} + +static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue) +{ + lex_ctx ctx = parser_ctx(parser); + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon"); + return false; + } + + if (!parser_next(parser)) + parseerror(parser, "parse error"); + + *out = (ast_expression*)ast_breakcont_new(ctx, is_continue); + return true; +} + +static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *operand; + ast_value *opval; + ast_switch *switchnode; + ast_switch_case swcase; + + lex_ctx ctx = parser_ctx(parser); + + /* parse over the opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected switch operand in parenthesis"); + return false; + } + + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected switch operand"); + return false; + } + /* parse the operand */ + operand = parse_expression_leave(parser, false); + if (!operand) + return false; + + if (!OPTS_FLAG(RELAXED_SWITCH)) { + opval = (ast_value*)operand; + if (!ast_istype(operand, ast_value) || !opval->isconst) { + parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch"); + ast_unref(operand); + return false; + } + } + + switchnode = ast_switch_new(ctx, operand); + + /* closing paren */ + if (parser->tok != ')') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren after 'switch' operand"); + return false; + } + + /* parse over the opening paren */ + if (!parser_next(parser) || parser->tok != '{') { + ast_delete(switchnode); + parseerror(parser, "expected list of cases"); + return false; + } + + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } + + /* case list! */ + while (parser->tok != '}') { + ast_block *block; + + if (parser->tok != TOKEN_KEYWORD) { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } + if (!strcmp(parser_tokval(parser), "case")) { + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected expression for case"); + return false; + } + swcase.value = parse_expression_leave(parser, false); + if (!swcase.value) { + ast_delete(switchnode); + parseerror(parser, "expected expression for case"); + return false; + } + } + else if (!strcmp(parser_tokval(parser), "default")) { + swcase.value = NULL; + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + } + + /* Now the colon and body */ + if (parser->tok != ':') { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + + if (!parser_next(parser)) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected statements or case"); + return false; + } + block = ast_block_new(parser_ctx(parser)); + if (!block) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + return false; + } + swcase.code = (ast_expression*)block; + vec_push(switchnode->cases, swcase); + while (true) { + ast_expression *expr; + if (parser->tok == '}') + break; + if (parser->tok == TOKEN_KEYWORD) { + if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + break; + } + } + if (!parse_statement(parser, block, &expr, true)) { + ast_delete(switchnode); + return false; + } + if (!expr) + continue; + vec_push(block->exprs, expr); + } + } + + /* closing paren */ + if (parser->tok != '}') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren of case list"); + return false; + } + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "parse error after switch"); + return false; + } + *out = (ast_expression*)switchnode; + return true; +} + +static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { if (parser->tok == TOKEN_TYPENAME || parser->tok == '.') { @@ -1715,42 +1920,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else if (!strcmp(parser_tokval(parser), "return")) { - ast_expression *exp = NULL; - ast_return *ret = NULL; - ast_value *expected = parser->function->vtype; - - if (!parser_next(parser)) { - parseerror(parser, "expected return expression"); - return false; - } - - if (parser->tok != ';') { - exp = parse_expression(parser, false); - if (!exp) - return false; - - if (exp->expression.vtype != expected->expression.next->expression.vtype) { - parseerror(parser, "return with invalid expression"); - } - - ret = ast_return_new(exp->expression.node.context, exp); - if (!ret) { - ast_delete(exp); - return false; - } - } else { - 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"); - } - ret = ast_return_new(parser_ctx(parser), NULL); - } - *out = (ast_expression*)ret; - return true; + return parse_return(parser, block, out); } else if (!strcmp(parser_tokval(parser), "if")) { @@ -1772,6 +1942,27 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } return parse_for(parser, block, out); } + else if (!strcmp(parser_tokval(parser), "break")) + { + return parse_break_continue(parser, block, out, false); + } + else if (!strcmp(parser_tokval(parser), "continue")) + { + return parse_break_continue(parser, block, out, true); + } + else if (!strcmp(parser_tokval(parser), "switch")) + { + return parse_switch(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + if (!allow_cases) { + parseerror(parser, "unexpected 'case' label"); + return false; + } + return true; + } parseerror(parser, "Unexpected keyword"); return false; } @@ -1807,9 +1998,11 @@ static bool GMQCC_WARN parser_pop_local(parser_t *parser) varentry_t *ve; ve = &vec_last(parser->locals); - if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) { - if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name)) - rv = false; + if (!parser->errors) { + if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) { + if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name)) + rv = false; + } } mem_d(ve->name); vec_pop(parser->locals); @@ -1835,7 +2028,7 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn if (parser->tok == '}') break; - if (!parse_statement(parser, block, &expr)) { + if (!parse_statement(parser, block, &expr, false)) { /* parseerror(parser, "parse error"); */ block = NULL; goto cleanup; @@ -1887,7 +2080,7 @@ static ast_expression* parse_statement_or_block(parser_t *parser) ast_expression *expr = NULL; if (parser->tok == '{') return (ast_expression*)parse_block(parser, false); - if (!parse_statement(parser, NULL, &expr)) + if (!parse_statement(parser, NULL, &expr, false)) return NULL; return expr; } @@ -2638,7 +2831,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) /* sanity check */ if (vec_size(params) > 8 && opts_standard == COMPILER_QCC) - parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); + (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ if (!parser_next(parser)) {