From: Wolfgang (Blub) Bumiller Date: Sun, 25 Nov 2012 20:27:14 +0000 (+0100) Subject: ast_label, and labels later used for goto X-Git-Tag: 0.1.9~236 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=474d8bd6f042d21f01f3e28b9f53cb0e0181291f;p=xonotic%2Fgmqcc.git ast_label, and labels later used for goto --- diff --git a/ast.c b/ast.c index 9aca0e9..16141b8 100644 --- a/ast.c +++ b/ast.c @@ -800,6 +800,24 @@ void ast_switch_delete(ast_switch *self) mem_d(self); } +ast_label* ast_label_new(lex_ctx ctx, const char *name) +{ + ast_instantiate(ast_label, ctx, ast_label_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen); + + self->name = util_strdup(name); + self->irblock = NULL; + + return self; +} + +void ast_label_delete(ast_label *self) +{ + mem_d((void*)self->name); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + ast_call* ast_call_new(lex_ctx ctx, ast_expression *funcexpr) { @@ -2635,6 +2653,30 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va return true; } +bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out) +{ + *out = NULL; + if (lvalue) { + asterror(ast_ctx(self), "internal error: ast_label cannot be an lvalue"); + return false; + } + + /* simply create a new block and jump to it */ + self->irblock = ir_function_create_block(func->ir_func, self->name); + if (!self->irblock) { + asterror(ast_ctx(self), "failed to allocate label block `%s`", self->name); + return false; + } + if (!func->curblock->final) { + if (!ir_block_create_jump(func->curblock, self->irblock)) + return false; + } + + /* enter the new block */ + func->curblock = self->irblock; + return true; +} + bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out) { ast_expression_codegen *cgen; diff --git a/ast.h b/ast.h index 4b88955..62287fe 100644 --- a/ast.h +++ b/ast.h @@ -48,6 +48,7 @@ typedef struct ast_member_s ast_member; typedef struct ast_array_index_s ast_array_index; typedef struct ast_breakcont_s ast_breakcont; typedef struct ast_switch_s ast_switch; +typedef struct ast_label_s ast_label; enum { TYPE_ast_node, @@ -68,7 +69,8 @@ enum { TYPE_ast_member, TYPE_ast_array_index, TYPE_ast_breakcont, - TYPE_ast_switch + TYPE_ast_switch, + TYPE_ast_label }; #define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) ) @@ -489,6 +491,22 @@ void ast_switch_delete(ast_switch*); bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**); +/* Label nodes + * + * Introduce a label which can be used together with 'goto' + */ +struct ast_label_s +{ + ast_expression_common expression; + const char *name; + ir_block *irblock; +}; + +ast_label* ast_label_new(lex_ctx ctx, const char *name); +void ast_label_delete(ast_label*); + +bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**); + /* CALL node * * Contains an ast_expression as target, rather than an ast_function/value. diff --git a/parser.c b/parser.c index 945ddbf..1d752c4 100644 --- a/parser.c +++ b/parser.c @@ -2336,6 +2336,27 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * *out = (ast_expression*)inner; return true; } + else if (parser->tok == ':') + { + ast_label *label; + if (!parser_next(parser)) { + parseerror(parser, "expected label name"); + return false; + } + if (parser->tok != TOKEN_IDENT) { + parseerror(parser, "label must be an identifier"); + return false; + } + label = ast_label_new(parser_ctx(parser), parser_tokval(parser)); + if (!label) + return false; + *out = (ast_expression*)label; + if (!parser_next(parser)) { + parseerror(parser, "parse error after label"); + return false; + } + return true; + } else if (parser->tok == ';') { if (!parser_next(parser)) {