+ *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);
+
+ (void)block; /* not touching */
+
+ 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);
+
+ (void)block; /* not touching */
+
+ /* 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;
+
+ 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 *caseblock;
+
+ 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;
+ }
+ if (!OPTS_FLAG(RELAXED_SWITCH)) {
+ opval = (ast_value*)swcase.value;
+ if (!ast_istype(swcase.value, ast_value)) { /* || !opval->constant) { */
+ parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
+ ast_unref(operand);
+ 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;
+ }
+ caseblock = ast_block_new(parser_ctx(parser));
+ if (!caseblock) {
+ if (swcase.value) ast_unref(swcase.value);
+ ast_delete(switchnode);
+ return false;
+ }
+ swcase.code = (ast_expression*)caseblock;
+ 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, caseblock, &expr, true)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ if (!expr)
+ continue;
+ ast_block_add_expr(caseblock, 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_goto(parser_t *parser, ast_expression **out)
+{
+ size_t i;
+ ast_goto *gt;
+
+ if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+ parseerror(parser, "expected label name after `goto`");
+ return false;
+ }
+
+ 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;
+ }
+ }
+ if (i == vec_size(parser->labels))
+ vec_push(parser->gotos, gt);
+
+ if (!parser_next(parser) || parser->tok != ';') {
+ parseerror(parser, "semicolon expected after goto label");
+ return false;
+ }
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error after goto");
+ return false;
+ }
+
+ *out = (ast_expression*)gt;
+ return true;
+}
+
+static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
+{
+ int cvq;
+ ast_value *typevar = NULL;
+ *out = NULL;
+
+ if (parser->tok == TOKEN_IDENT)
+ typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
+
+ if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+ {
+ /* local variable */
+ if (!block) {
+ parseerror(parser, "cannot declare a variable from here");
+ return false;
+ }
+ if (opts_standard == COMPILER_QCC) {
+ if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
+ return false;
+ }
+ if (!parse_variable(parser, block, false, CV_NONE, typevar))
+ return false;
+ *out = NULL;
+ return true;
+ }
+ else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var"))
+ {
+ goto ident_var;
+ }
+ else if (parser->tok == TOKEN_KEYWORD)
+ {
+ if (!strcmp(parser_tokval(parser), "local") ||
+ !strcmp(parser_tokval(parser), "const") ||
+ !strcmp(parser_tokval(parser), "var"))
+ {
+ident_var:
+ if (parser_tokval(parser)[0] == 'c')
+ cvq = CV_CONST;
+ else if (parser_tokval(parser)[0] == 'v')
+ cvq = CV_VAR;
+ else
+ cvq = CV_NONE;
+
+ if (!block) {
+ parseerror(parser, "cannot declare a local variable here");