From 6642b6613e871a64a737c3c61e403be560278bb3 Mon Sep 17 00:00:00 2001 From: bog Date: Tue, 13 Feb 2024 13:50:11 +0100 Subject: [PATCH] :sparkles: while loop. --- bc/src/compiler.c | 62 +++++++++++++++++++++++++++++++++++++++ bc/src/compiler.h | 6 ++++ doc/gux.bnf | 5 ++++ lang/src/lexer.c | 2 ++ lang/src/node.h | 3 +- lang/src/parser.c | 43 ++++++++++++++++++++++++++- lang/src/parser.h | 1 + lang/src/type_checker.c | 23 +++++++++++++++ lang/tests/lexer.c | 4 +-- lang/tests/parser.c | 10 +++++++ lang/tests/type_checker.c | 7 +++++ tests/flow_control.gux | 49 +++++++++++++++++++++++++++++++ vm/src/vm.c | 2 +- 13 files changed, 212 insertions(+), 5 deletions(-) diff --git a/bc/src/compiler.c b/bc/src/compiler.c index 9d961d2..bcbb16a 100644 --- a/bc/src/compiler.c +++ b/bc/src/compiler.c @@ -13,10 +13,13 @@ void compiler_init(struct compiler* self, struct syms* syms) self->syms = syms; memset(self->error_msg, 0, GUX_STR_SIZE); self->stack_size = 0; + vec_init(&self->loops, 1); } void compiler_free(struct compiler* self) { + vec_free_elements(&self->loops); + vec_free(&self->loops); assert(self); } @@ -30,6 +33,65 @@ int compiler_compile(struct compiler* self, 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; ito_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: { struct vec to_end; vec_init(&to_end, 1); diff --git a/bc/src/compiler.h b/bc/src/compiler.h index 25c4931..a3cb162 100644 --- a/bc/src/compiler.h +++ b/bc/src/compiler.h @@ -6,11 +6,17 @@ #include "program.h" #include "syms.h" +struct loop_info { + size_t to_cond; + struct vec to_end; +}; + struct compiler { char error_msg[GUX_STR_SIZE]; struct syms* syms; int error_line; int stack_size; + struct vec loops; }; void compiler_init(struct compiler* self, struct syms* syms); diff --git a/doc/gux.bnf b/doc/gux.bnf index 5172681..149fe42 100644 --- a/doc/gux.bnf +++ b/doc/gux.bnf @@ -12,16 +12,21 @@ LEXPR ::= | VARDECL | CONSTDECL | ASSIGN +| break +| continue BEXPR ::= | BLOCK | IF +| WHILE IF ::= | if EXPR BLOCK | if EXPR BLOCK else BLOCK | if EXPR BLOCK else IF +WHILE ::= while EXPR BLOCK + BLOCK ::= obrace INSTR* cbrace VARDECL ::= var ident colon type? assign EXPR CONSTDECL ::= ident colon type? assign EXPR diff --git a/lang/src/lexer.c b/lang/src/lexer.c index 72b7755..dc96426 100644 --- a/lang/src/lexer.c +++ b/lang/src/lexer.c @@ -12,6 +12,8 @@ void lexer_init(struct lexer* self, char const* source) 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, "else", NODE_ELSE, "", 1); lexer_add_tok(self, "cond", NODE_COND, "", 1); diff --git a/lang/src/node.h b/lang/src/node.h index cea504a..aceba1d 100644 --- a/lang/src/node.h +++ b/lang/src/node.h @@ -15,7 +15,8 @@ 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_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); diff --git a/lang/src/parser.c b/lang/src/parser.c index ec00912..5c1bbb5 100644 --- a/lang/src/parser.c +++ b/lang/src/parser.c @@ -133,7 +133,8 @@ int parser_start_bexpr(struct parser* self) struct node* tok = self->tokens.data[self->cursor]; 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) @@ -181,6 +182,14 @@ struct node* parser_try_new_instr(struct parser* 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) { node_free(node); @@ -208,6 +217,20 @@ struct node* parser_try_new_lexpr(struct parser* 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)) { 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); } + if (parser_type_is(self, NODE_WHILE, 0)) + { + return parser_try_new_while(self); + } + return parser_try_new_block(self); } @@ -290,6 +318,19 @@ struct node* parser_try_new_if(struct parser* self) 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) { diff --git a/lang/src/parser.h b/lang/src/parser.h index 7ce77ef..a7e9f48 100644 --- a/lang/src/parser.h +++ b/lang/src/parser.h @@ -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_bexpr(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_assign(struct parser* self); struct node* parser_try_new_vardecl(struct parser* self); diff --git a/lang/src/type_checker.c b/lang/src/type_checker.c index 960c592..92fa3f1 100644 --- a/lang/src/type_checker.c +++ b/lang/src/type_checker.c @@ -27,6 +27,29 @@ int type_checker_check(struct type_checker* self, 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: { struct type expr; type_init(&expr, TYPE_VOID); diff --git a/lang/tests/lexer.c b/lang/tests/lexer.c index 6e393ec..c866073 100644 --- a/lang/tests/lexer.c +++ b/lang/tests/lexer.c @@ -127,6 +127,6 @@ Test(lexer, blocks) { } Test(lexer, flow_control) { - test_lexer("if else cond while for", 5, - "IF", "ELSE", "COND", "WHILE", "FOR"); + test_lexer("if else cond while for break continue", 7, + "IF", "ELSE", "COND", "WHILE", "FOR", "BREAK", "CONTINUE"); } diff --git a/lang/tests/parser.c b/lang/tests/parser.c index 621c3cc..87b3e50 100644 --- a/lang/tests/parser.c +++ b/lang/tests/parser.c @@ -149,3 +149,13 @@ Test(parser, if_block) { "))))", "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; }"); +} diff --git a/lang/tests/type_checker.c b/lang/tests/type_checker.c index 77c733a..ee1ba20 100644 --- a/lang/tests/type_checker.c +++ b/lang/tests/type_checker.c @@ -171,3 +171,10 @@ Test(type_checker, assign) { test_check_ko("x := 3; x = 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' {}"); +} diff --git a/tests/flow_control.gux b/tests/flow_control.gux index 0e438b2..90c935c 100644 --- a/tests/flow_control.gux +++ b/tests/flow_control.gux @@ -59,3 +59,52 @@ if false { } 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; \ No newline at end of file diff --git a/vm/src/vm.c b/vm/src/vm.c index 12b099a..f0b59f7 100644 --- a/vm/src/vm.c +++ b/vm/src/vm.c @@ -88,7 +88,7 @@ void vm_store(struct vm* self, int addr) if (loc->addr == addr) { - loc->stack_addr = frame->sp - 1; + frame->stack[loc->stack_addr] = frame->stack[frame->sp - 1]; return; } }