break and continue support

This commit is contained in:
Wolfgang (Blub) Bumiller 2012-11-19 19:39:52 +01:00
parent 2d8bf20d43
commit ea75003cf4
4 changed files with 93 additions and 6 deletions

41
ast.c
View file

@ -700,6 +700,22 @@ void ast_loop_delete(ast_loop *self)
mem_d(self);
}
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont)
{
ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
self->is_continue = iscont;
return self;
}
void ast_breakcont_delete(ast_breakcont *self)
{
ast_expression_delete((ast_expression*)self);
mem_d(self);
}
ast_call* ast_call_new(lex_ctx ctx,
ast_expression *funcexpr)
{
@ -2184,6 +2200,31 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
return true;
}
bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, ir_value **out)
{
ir_block *target;
if (lvalue) {
asterror(ast_ctx(self), "break/continue expression is not an l-value");
return false;
}
if (self->expression.outr) {
asterror(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
return false;
}
self->expression.outr = (ir_value*)1;
if (self->is_continue)
target = func->continueblock;
else
target = func->breakblock;
if (!ir_block_create_jump(func->curblock, target))
return false;
return true;
}
bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
{
ast_expression_codegen *cgen;

16
ast.h
View file

@ -46,6 +46,7 @@ typedef struct ast_unary_s ast_unary;
typedef struct ast_return_s ast_return;
typedef struct ast_member_s ast_member;
typedef struct ast_array_index_s ast_array_index;
typedef struct ast_breakcont_s ast_breakcont;
enum {
TYPE_ast_node,
@ -64,7 +65,8 @@ enum {
TYPE_ast_unary,
TYPE_ast_return,
TYPE_ast_member,
TYPE_ast_array_index
TYPE_ast_array_index,
TYPE_ast_breakcont
};
#define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
@ -445,6 +447,18 @@ void ast_loop_delete(ast_loop*);
bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
/* Break/Continue
*/
struct ast_breakcont_s
{
ast_expression_common expression;
bool is_continue;
};
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont);
void ast_breakcont_delete(ast_breakcont*);
bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
/* CALL node
*
* Contains an ast_expression as target, rather than an ast_function/value.

15
ir.c
View file

@ -1133,7 +1133,12 @@ bool ir_values_overlap(const ir_value *a, const ir_value *b)
bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what)
{
ir_instr *in = ir_instr_new(self, op);
ir_instr *in;
if (self->final) {
irerror(self->context, "unreachable statement (%s)", self->label);
return false;
}
in = ir_instr_new(self, op);
if (!in)
return false;
@ -1206,7 +1211,7 @@ bool ir_block_create_return(ir_block *self, ir_value *v)
{
ir_instr *in;
if (self->final) {
irerror(self->context, "block already ended (%s)", self->label);
irerror(self->context, "unreachable statement (%s)", self->label);
return false;
}
self->final = true;
@ -1227,7 +1232,7 @@ bool ir_block_create_if(ir_block *self, ir_value *v,
{
ir_instr *in;
if (self->final) {
irerror(self->context, "block already ended (%s)", self->label);
irerror(self->context, "unreachable statement (%s)", self->label);
return false;
}
self->final = true;
@ -1257,7 +1262,7 @@ bool ir_block_create_jump(ir_block *self, ir_block *to)
{
ir_instr *in;
if (self->final) {
irerror(self->context, "block already ended (%s)", self->label);
irerror(self->context, "unreachable statement (%s)", self->label);
return false;
}
self->final = true;
@ -1277,7 +1282,7 @@ bool ir_block_create_goto(ir_block *self, ir_block *to)
{
ir_instr *in;
if (self->final) {
irerror(self->context, "block already ended (%s)", self->label);
irerror(self->context, "unreachable statement (%s)", self->label);
return false;
}
self->final = true;

View file

@ -1718,6 +1718,25 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
return true;
}
static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
{
ast_expression *exp = NULL;
ast_return *ret = NULL;
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_breakcont_new(ctx, is_continue);
return true;
}
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
{
if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
@ -1777,6 +1796,14 @@ 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);
}
parseerror(parser, "Unexpected keyword");
return false;
}