mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-03-23 03:01:30 +00:00
break and continue support
This commit is contained in:
parent
2d8bf20d43
commit
ea75003cf4
4 changed files with 93 additions and 6 deletions
41
ast.c
41
ast.c
|
@ -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
16
ast.h
|
@ -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
15
ir.c
|
@ -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;
|
||||
|
|
27
parser.c
27
parser.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue