✨ while loop.
parent
3b7b0b4295
commit
6642b6613e
|
@ -13,10 +13,13 @@ void compiler_init(struct compiler* self, struct syms* syms)
|
||||||
self->syms = syms;
|
self->syms = syms;
|
||||||
memset(self->error_msg, 0, GUX_STR_SIZE);
|
memset(self->error_msg, 0, GUX_STR_SIZE);
|
||||||
self->stack_size = 0;
|
self->stack_size = 0;
|
||||||
|
vec_init(&self->loops, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compiler_free(struct compiler* self)
|
void compiler_free(struct compiler* self)
|
||||||
{
|
{
|
||||||
|
vec_free_elements(&self->loops);
|
||||||
|
vec_free(&self->loops);
|
||||||
assert(self);
|
assert(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +33,65 @@ int compiler_compile(struct compiler* self,
|
||||||
|
|
||||||
switch (node->type)
|
switch (node->type)
|
||||||
{
|
{
|
||||||
|
case NODE_BREAK: {
|
||||||
|
size_t* addr = malloc(sizeof(size_t));
|
||||||
|
*addr = compiler_gen_instr(self, program, OP_BR, NO_PARAM);
|
||||||
|
struct loop_info* loop = self->loops.data[self->loops.size - 1];
|
||||||
|
vec_push(&loop->to_end, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_CONTINUE: {
|
||||||
|
struct loop_info* loop = self->loops.data[self->loops.size - 1];
|
||||||
|
compiler_gen_instr(self, program, OP_BR, loop->to_cond);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_WHILE: {
|
||||||
|
struct node* expr = node->children.data[0];
|
||||||
|
struct node* body = node->children.data[1];
|
||||||
|
|
||||||
|
struct loop_info* loop = malloc(sizeof(struct loop_info));
|
||||||
|
vec_init(&loop->to_end, 1);
|
||||||
|
vec_push(&self->loops, loop);
|
||||||
|
|
||||||
|
size_t const cond = program->instructions.size;
|
||||||
|
loop->to_cond = cond;
|
||||||
|
|
||||||
|
if (compiler_compile(self, expr, program) != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t goto_end = compiler_gen_instr(self, program, OP_BRF, 0);
|
||||||
|
|
||||||
|
if (compiler_compile(self, body, program) != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t goto_cond = compiler_gen_instr(self, program, OP_BR, 0);
|
||||||
|
|
||||||
|
struct instruction* instr = program->instructions.data[goto_end];
|
||||||
|
instr->param = program->instructions.size;
|
||||||
|
|
||||||
|
instr = program->instructions.data[goto_cond];
|
||||||
|
instr->param = cond;
|
||||||
|
|
||||||
|
for (size_t i=0; i<loop->to_end.size; i++)
|
||||||
|
{
|
||||||
|
size_t goto_end = *(size_t*)loop->to_end.data[i];
|
||||||
|
instr = program->instructions.data[goto_end];
|
||||||
|
instr->param = program->instructions.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec_free_elements(&loop->to_end);
|
||||||
|
vec_free(&loop->to_end);
|
||||||
|
free(loop);
|
||||||
|
|
||||||
|
vec_pop(&self->loops);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_IF: {
|
case NODE_IF: {
|
||||||
struct vec to_end;
|
struct vec to_end;
|
||||||
vec_init(&to_end, 1);
|
vec_init(&to_end, 1);
|
||||||
|
|
|
@ -6,11 +6,17 @@
|
||||||
#include "program.h"
|
#include "program.h"
|
||||||
#include "syms.h"
|
#include "syms.h"
|
||||||
|
|
||||||
|
struct loop_info {
|
||||||
|
size_t to_cond;
|
||||||
|
struct vec to_end;
|
||||||
|
};
|
||||||
|
|
||||||
struct compiler {
|
struct compiler {
|
||||||
char error_msg[GUX_STR_SIZE];
|
char error_msg[GUX_STR_SIZE];
|
||||||
struct syms* syms;
|
struct syms* syms;
|
||||||
int error_line;
|
int error_line;
|
||||||
int stack_size;
|
int stack_size;
|
||||||
|
struct vec loops;
|
||||||
};
|
};
|
||||||
|
|
||||||
void compiler_init(struct compiler* self, struct syms* syms);
|
void compiler_init(struct compiler* self, struct syms* syms);
|
||||||
|
|
|
@ -12,16 +12,21 @@ LEXPR ::=
|
||||||
| VARDECL
|
| VARDECL
|
||||||
| CONSTDECL
|
| CONSTDECL
|
||||||
| ASSIGN
|
| ASSIGN
|
||||||
|
| break
|
||||||
|
| continue
|
||||||
|
|
||||||
BEXPR ::=
|
BEXPR ::=
|
||||||
| BLOCK
|
| BLOCK
|
||||||
| IF
|
| IF
|
||||||
|
| WHILE
|
||||||
|
|
||||||
IF ::=
|
IF ::=
|
||||||
| if EXPR BLOCK
|
| if EXPR BLOCK
|
||||||
| if EXPR BLOCK else BLOCK
|
| if EXPR BLOCK else BLOCK
|
||||||
| if EXPR BLOCK else IF
|
| if EXPR BLOCK else IF
|
||||||
|
|
||||||
|
WHILE ::= while EXPR BLOCK
|
||||||
|
|
||||||
BLOCK ::= obrace INSTR* cbrace
|
BLOCK ::= obrace INSTR* cbrace
|
||||||
VARDECL ::= var ident colon type? assign EXPR
|
VARDECL ::= var ident colon type? assign EXPR
|
||||||
CONSTDECL ::= ident colon type? assign EXPR
|
CONSTDECL ::= ident colon type? assign EXPR
|
||||||
|
|
|
@ -12,6 +12,8 @@ void lexer_init(struct lexer* self, char const* source)
|
||||||
|
|
||||||
vec_init(&self->toks, 1);
|
vec_init(&self->toks, 1);
|
||||||
|
|
||||||
|
lexer_add_tok(self, "continue", NODE_CONTINUE, "", 1);
|
||||||
|
lexer_add_tok(self, "break", NODE_BREAK, "", 1);
|
||||||
lexer_add_tok(self, "if", NODE_IF, "", 1);
|
lexer_add_tok(self, "if", NODE_IF, "", 1);
|
||||||
lexer_add_tok(self, "else", NODE_ELSE, "", 1);
|
lexer_add_tok(self, "else", NODE_ELSE, "", 1);
|
||||||
lexer_add_tok(self, "cond", NODE_COND, "", 1);
|
lexer_add_tok(self, "cond", NODE_COND, "", 1);
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
G(NODE_GE), G(NODE_COLON), G(NODE_ASSIGN), G(NODE_IDENT), \
|
G(NODE_GE), G(NODE_COLON), G(NODE_ASSIGN), G(NODE_IDENT), \
|
||||||
G(NODE_TYPE), G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_VAR), \
|
G(NODE_TYPE), G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_VAR), \
|
||||||
G(NODE_OBRACE), G(NODE_CBRACE), G(NODE_BLOCK), \
|
G(NODE_OBRACE), G(NODE_CBRACE), G(NODE_BLOCK), \
|
||||||
G(NODE_IF), G(NODE_ELSE), G(NODE_COND), G(NODE_WHILE), G(NODE_FOR)
|
G(NODE_IF), G(NODE_ELSE), G(NODE_COND), G(NODE_WHILE), G(NODE_FOR), \
|
||||||
|
G(NODE_CONTINUE), G(NODE_BREAK)
|
||||||
|
|
||||||
|
|
||||||
GUX_ENUM_H(NodeType, NODE_TYPE);
|
GUX_ENUM_H(NodeType, NODE_TYPE);
|
||||||
|
|
|
@ -133,7 +133,8 @@ int parser_start_bexpr(struct parser* self)
|
||||||
struct node* tok = self->tokens.data[self->cursor];
|
struct node* tok = self->tokens.data[self->cursor];
|
||||||
|
|
||||||
return tok->type == NODE_OBRACE
|
return tok->type == NODE_OBRACE
|
||||||
|| tok->type == NODE_IF;
|
|| tok->type == NODE_IF
|
||||||
|
|| tok->type == NODE_WHILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct node* parser_try_new_root(struct parser* self, char const* source)
|
struct node* parser_try_new_root(struct parser* self, char const* source)
|
||||||
|
@ -181,6 +182,14 @@ struct node* parser_try_new_instr(struct parser* self)
|
||||||
{
|
{
|
||||||
struct node* node = parser_try_new_lexpr(self);
|
struct node* node = parser_try_new_lexpr(self);
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
self->error_line = parser_current_line(self);
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE, "invalid instruction");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (parser_ensure(self, NODE_SEMICOLON) != 0)
|
if (parser_ensure(self, NODE_SEMICOLON) != 0)
|
||||||
{
|
{
|
||||||
node_free(node);
|
node_free(node);
|
||||||
|
@ -208,6 +217,20 @@ struct node* parser_try_new_lexpr(struct parser* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
|
if (parser_try_consume(self, NODE_BREAK) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_BREAK, "", parser_current_line(self));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser_try_consume(self, NODE_CONTINUE) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_CONTINUE, "", parser_current_line(self));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
if (parser_type_is(self, NODE_ASSERT, 0))
|
if (parser_type_is(self, NODE_ASSERT, 0))
|
||||||
{
|
{
|
||||||
struct node* node = malloc(sizeof(struct node));
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
@ -261,6 +284,11 @@ struct node* parser_try_new_bexpr(struct parser* self)
|
||||||
return parser_try_new_if(self);
|
return parser_try_new_if(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_WHILE, 0))
|
||||||
|
{
|
||||||
|
return parser_try_new_while(self);
|
||||||
|
}
|
||||||
|
|
||||||
return parser_try_new_block(self);
|
return parser_try_new_block(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +318,19 @@ struct node* parser_try_new_if(struct parser* self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_while(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_WHILE, "", parser_current_line(self));
|
||||||
|
self->cursor++; // while
|
||||||
|
|
||||||
|
node_add_child(node, parser_try_new_expr(self));
|
||||||
|
node_add_child(node, parser_try_new_block(self));
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
struct node* parser_try_new_block(struct parser* self)
|
struct node* parser_try_new_block(struct parser* self)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct node* parser_try_new_expr(struct parser* self);
|
||||||
struct node* parser_try_new_lexpr(struct parser* self);
|
struct node* parser_try_new_lexpr(struct parser* self);
|
||||||
struct node* parser_try_new_bexpr(struct parser* self);
|
struct node* parser_try_new_bexpr(struct parser* self);
|
||||||
struct node* parser_try_new_if(struct parser* self);
|
struct node* parser_try_new_if(struct parser* self);
|
||||||
|
struct node* parser_try_new_while(struct parser* self);
|
||||||
struct node* parser_try_new_block(struct parser* self);
|
struct node* parser_try_new_block(struct parser* self);
|
||||||
struct node* parser_try_new_assign(struct parser* self);
|
struct node* parser_try_new_assign(struct parser* self);
|
||||||
struct node* parser_try_new_vardecl(struct parser* self);
|
struct node* parser_try_new_vardecl(struct parser* self);
|
||||||
|
|
|
@ -27,6 +27,29 @@ int type_checker_check(struct type_checker* self,
|
||||||
|
|
||||||
switch (node->type)
|
switch (node->type)
|
||||||
{
|
{
|
||||||
|
case NODE_WHILE: {
|
||||||
|
struct type have;
|
||||||
|
type_init(&have, TYPE_VOID);
|
||||||
|
type_resolver_resolve(&self->resolver,
|
||||||
|
node->children.data[0],
|
||||||
|
&have);
|
||||||
|
|
||||||
|
struct type want;
|
||||||
|
type_init(&want, TYPE_BOOL);
|
||||||
|
|
||||||
|
if (!type_equals(&have, &want))
|
||||||
|
{
|
||||||
|
type_checker_error_msg(self, node, &want, &have);
|
||||||
|
type_free(&have);
|
||||||
|
type_free(&want);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&have);
|
||||||
|
type_free(&want);
|
||||||
|
|
||||||
|
} return 0;
|
||||||
|
|
||||||
case NODE_IF: {
|
case NODE_IF: {
|
||||||
struct type expr;
|
struct type expr;
|
||||||
type_init(&expr, TYPE_VOID);
|
type_init(&expr, TYPE_VOID);
|
||||||
|
|
|
@ -127,6 +127,6 @@ Test(lexer, blocks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(lexer, flow_control) {
|
Test(lexer, flow_control) {
|
||||||
test_lexer("if else cond while for", 5,
|
test_lexer("if else cond while for break continue", 7,
|
||||||
"IF", "ELSE", "COND", "WHILE", "FOR");
|
"IF", "ELSE", "COND", "WHILE", "FOR", "BREAK", "CONTINUE");
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,3 +149,13 @@ Test(parser, if_block) {
|
||||||
"))))",
|
"))))",
|
||||||
"if a { 0; } else if b { 1; } else { 2; }");
|
"if a { 0; } else if b { 1; } else { 2; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(parser, while_block) {
|
||||||
|
test_parser("ROOT(WHILE(BOOL[true],BLOCK(INT[0])))",
|
||||||
|
"while true { 0; }");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, while_break_continue) {
|
||||||
|
test_parser("ROOT(WHILE(BOOL[true],BLOCK(BREAK,CONTINUE)))",
|
||||||
|
"while true { break; continue; }");
|
||||||
|
}
|
||||||
|
|
|
@ -171,3 +171,10 @@ Test(type_checker, assign) {
|
||||||
test_check_ko("x := 3; x = 4;");
|
test_check_ko("x := 3; x = 4;");
|
||||||
test_check_ko("z = 4;");
|
test_check_ko("z = 4;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(type_checker, while_loop) {
|
||||||
|
test_check_ok("while true {}");
|
||||||
|
test_check_ko("while 5 {}");
|
||||||
|
test_check_ko("while 5.0 {}");
|
||||||
|
test_check_ko("while 'bim' {}");
|
||||||
|
}
|
||||||
|
|
|
@ -59,3 +59,52 @@ if false {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert a == 8;
|
assert a == 8;
|
||||||
|
|
||||||
|
var b := 0;
|
||||||
|
|
||||||
|
while b < 100 {
|
||||||
|
b = b + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert b == 100;
|
||||||
|
|
||||||
|
var c := 0;
|
||||||
|
var d := 0;
|
||||||
|
var e := 0;
|
||||||
|
|
||||||
|
while c < 6 {
|
||||||
|
d = 0;
|
||||||
|
while d < 7 {
|
||||||
|
e = e + 1;
|
||||||
|
d = d + 1;
|
||||||
|
}
|
||||||
|
c = c + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert e == 42;
|
||||||
|
|
||||||
|
var f := 0;
|
||||||
|
|
||||||
|
while f < 100 {
|
||||||
|
if f == 37 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
f = f + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert f == 37;
|
||||||
|
|
||||||
|
var g := 0;
|
||||||
|
var h := 0;
|
||||||
|
|
||||||
|
while g < 100 {
|
||||||
|
g = g + 1;
|
||||||
|
|
||||||
|
if g % 2 == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = h + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert h == 50;
|
|
@ -88,7 +88,7 @@ void vm_store(struct vm* self, int addr)
|
||||||
|
|
||||||
if (loc->addr == addr)
|
if (loc->addr == addr)
|
||||||
{
|
{
|
||||||
loc->stack_addr = frame->sp - 1;
|
frame->stack[loc->stack_addr] = frame->stack[frame->sp - 1];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue