From 719abe66c4f690434f00874d3ed1d3c86626ca06 Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 22 Mar 2024 16:33:41 +0100 Subject: [PATCH] :sparkles: if-then-else expression. --- doc/grammar.bnf | 3 ++ lib/compiler.c | 69 +++++++++++++++++++++++++++++++++++---- lib/compiler.h | 5 +++ lib/lexer.c | 2 ++ lib/node.h | 3 +- lib/parser.c | 85 +++++++++++++++++++++++++++++++++++++++++++++---- lib/parser.h | 2 ++ tests/cond.ccm | 77 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 232 insertions(+), 14 deletions(-) create mode 100644 tests/cond.ccm diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 0cad324..0ef9097 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -5,6 +5,9 @@ EXPR ::= | DECL | ASSIGN | BEGIN +| IF end +IF ::= +| if EXPR BLOCK (else (IF | BLOCK))? BEGIN ::= begin BLOCK end BLOCK ::= EXPR* ASSIGN ::= (ident|INDEX) assign EXPR diff --git a/lib/compiler.c b/lib/compiler.c index d81171f..ea5d846 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -32,14 +32,32 @@ void compiler_compile(compiler_t* self, switch (node->kind) { + case NODE_IF: { + vec_t to_end; + vec_init(&to_end); + compiler_compile_if(self, node, prog, &to_end); + + size_t end_point = prog->instrs.size; + + for (size_t i=0; iinstrs.data[*addr]) + ->param = end_point; + } + + vec_free_elements(&to_end, NULL); + vec_free(&to_end); + } break; + case NODE_BLOCK: { sym_open_scope(sym); for (size_t i=0; ichildren.size; i++) { compiler_compile( - self, - node->children.data[i], + self, + node->children.data[i], prog ); } @@ -92,12 +110,12 @@ void compiler_compile(compiler_t* self, compiler_compile(self, expr, prog); int status = sym_try_assign( - sym, + sym, target->value, self->loc_counter ); sym_entry_t* entry = sym_try_get_value( - sym, + sym, target->value ); @@ -118,8 +136,6 @@ void compiler_compile(compiler_t* self, } prog_add_instr(prog, OP_LOCAL_STORE, self->loc_counter); - - self->loc_counter++; } } break; @@ -144,7 +160,7 @@ void compiler_compile(compiler_t* self, case NODE_IDENT: { char const* name = node->value; sym_entry_t const* entry = sym_try_get_value( - sym, + sym, name ); @@ -425,3 +441,42 @@ void compiler_compile_and(compiler_t* self, vec_free(&to_false); } + +void compiler_compile_if(compiler_t* self, + node_t* node, + prog_t* prog, + vec_t* to_end) +{ + assert(self); assert(node); assert(prog); + + if (node->kind != NODE_IF) + { + compiler_compile(self, node, prog); + return; + } + + node_t* cond = node->children.data[0]; + node_t* block = node->children.data[1]; + node_t* next = node->children.size == 3 ? + node->children.data[2] : + NULL; + + compiler_compile_if(self, cond, prog, to_end); + int cond_point = prog_add_instr(prog, OP_BRF, 0); + + compiler_compile_if(self, block, prog, to_end); + int end_point = prog_add_instr(prog, OP_BR, 0); // to end + int* addr = malloc(sizeof(int)); + *addr = end_point; + vec_push(to_end, (void*) addr); + + int next_pos = prog->instrs.size; + + ((instr_t*) prog->instrs.data[cond_point])->param + = next_pos; + + if (next) + { + compiler_compile_if(self, next, prog, to_end); + } +} diff --git a/lib/compiler.h b/lib/compiler.h index e3176c5..12d777d 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -28,4 +28,9 @@ void compiler_compile_or(compiler_t* self, void compiler_compile_and(compiler_t* self, node_t* node, prog_t* prog); + +void compiler_compile_if(compiler_t* self, + node_t* node, + prog_t* prog, + vec_t* to_end); #endif diff --git a/lib/lexer.c b/lib/lexer.c index aa9e9d9..d42a109 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -171,6 +171,8 @@ node_t* lexer_try_new_next(lexer_t* self) } } + CCM_KEYWORD("if", NODE_IF, 0); + CCM_KEYWORD("else", NODE_ELSE, 0); CCM_KEYWORD("begin", NODE_BEGIN, 0); CCM_KEYWORD("end", NODE_END, 0); CCM_KEYWORD("var", NODE_VAR, 0); diff --git a/lib/node.h b/lib/node.h index 8ed2cfa..4e15c8b 100644 --- a/lib/node.h +++ b/lib/node.h @@ -16,7 +16,8 @@ G(NODE_STR), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ G(NODE_GE), G(NODE_EQ), G(NODE_NE), G(NODE_ARRAY), \ G(NODE_VAR), G(NODE_IDENT), G(NODE_ASSIGN), \ G(NODE_VARDECL), G(NODE_CONST), G(NODE_CONSTDECL), \ -G(NODE_BEGIN), G(NODE_BLOCK), G(NODE_END) +G(NODE_BEGIN), G(NODE_BLOCK), G(NODE_END), G(NODE_IF), \ +G(NODE_ELSE) CCM_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/parser.c b/lib/parser.c index 96188bf..063b2fa 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -82,6 +82,17 @@ node_t* parser_try_new_rule(parser_t* self, rule_t rule) return (void*) NULL; } +int parser_consume(parser_t* self, NodeKind kind) +{ + if (!lexer_peek_kind(self->lexer, kind, 0)) + { + return 0; + } + + lexer_consume_next(self->lexer, kind); + return 1; +} + int parser_ensure(parser_t* self, node_t* node, NodeKind kind) { assert(self); @@ -152,12 +163,74 @@ node_t* parser_try_new_expr(parser_t* self) return CCM_TRY(parser_try_new_begin); } + if (lexer_peek_kind(self->lexer, NODE_IF, 0)) + { + node_t* node = CCM_TRY(parser_try_new_if); + if (!node) { return NULL; } + lexer_consume_next(self->lexer, NODE_END); + return node; + } + node_t* assign = CCM_TRY(parser_try_new_assign); if (assign) { return assign; } return CCM_TRY(parser_try_new_or); } +node_t* parser_try_new_if(parser_t* self) +{ + assert(self); + + if (!parser_consume(self, NODE_IF)) + { + return NULL; + } + node_t* cond = CCM_TRY(parser_try_new_expr); + if (!cond) { return NULL; } + + node_t* block = CCM_TRY(parser_try_new_block); + if (!block) + { + node_free(cond); free(cond); + return NULL; + } + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_IF, "", cond->line); + node_push_new_child(node, cond); + node_push_new_child(node, block); + + if (parser_consume(self, NODE_ELSE)) + { + if (lexer_peek_kind(self->lexer, NODE_IF, 0)) + { + node_t* next = CCM_TRY(parser_try_new_if); + if (!next) + { + node_free(cond); free(cond); + node_free(block); free(block); + node_free(node); free(node); + return NULL; + } + node_push_new_child(node, next); + } + else + { + node_t* next = CCM_TRY(parser_try_new_block); + if (!next) + { + node_free(cond); free(cond); + node_free(block); free(block); + node_free(node); free(node); + return NULL; + } + node_push_new_child(node, next); + } + } + + return node; +} + node_t* parser_try_new_begin(parser_t* self) { assert(self); @@ -171,7 +244,7 @@ node_t* parser_try_new_begin(parser_t* self) node_t* node = malloc(sizeof(node_t)); node_init(node, NODE_BEGIN, "", self->lexer->line); - + node_t* block = CCM_TRY(parser_try_new_block); node_push_new_child(node, block); @@ -192,14 +265,14 @@ node_t* parser_try_new_block(parser_t* self) node_t* node = malloc(sizeof(node_t)); node_init(node, NODE_BLOCK, "", self->lexer->line); - while (1) + while (1) { node_t* expr = CCM_TRY(parser_try_new_expr); if (!expr) { break; } node_push_new_child(node, expr); } - + return node; } @@ -275,7 +348,7 @@ node_t* parser_try_new_decl(parser_t* self) } node_t* node = malloc(sizeof(node_t)); - node_init(node, is_const ? NODE_CONSTDECL : NODE_VARDECL, + node_init(node, is_const ? NODE_CONSTDECL : NODE_VARDECL, "", self->lexer->line); node_push_new_child(node, ident); @@ -681,7 +754,7 @@ node_t* parser_try_new_array(parser_t* self) node_t* node = malloc(sizeof(node_t)); node_init(node, NODE_ARRAY, "", self->lexer->line); - + if (!lexer_peek_kind(self->lexer, NODE_OSQUARE, 0)) { node_free(node); free(node); @@ -728,7 +801,7 @@ node_t* parser_try_new_index(parser_t* self) assert(self); node_t* target = NULL; - + if (lexer_peek_kind(self->lexer, NODE_STR, 0) || lexer_peek_kind(self->lexer, NODE_IDENT, 0)) { diff --git a/lib/parser.h b/lib/parser.h index c5b0984..f39733f 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -21,10 +21,12 @@ void parser_free(parser_t* self); node_t* parser_try_new_parse(parser_t* self); node_t* parser_try_new_rule(parser_t* self, rule_t rule); node_t* parser_try_new_rule_ll1(parser_t* self, rule_ll1_t rule, node_t* node); +int parser_consume(parser_t* self, NodeKind kind); int parser_ensure(parser_t* self, node_t* node, NodeKind kind); node_t* parser_try_new_module(parser_t* self); node_t* parser_try_new_expr(parser_t* self); +node_t* parser_try_new_if(parser_t* self); node_t* parser_try_new_begin(parser_t* self); node_t* parser_try_new_block(parser_t* self); node_t* parser_try_new_assign(parser_t* self); diff --git a/tests/cond.ccm b/tests/cond.ccm new file mode 100644 index 0000000..4739c36 --- /dev/null +++ b/tests/cond.ccm @@ -0,0 +1,77 @@ +# IF +# == +var a = 0 + +if true + a = 1 +end + +assert_eq (1, a) + +if false + a = 2 +end + +assert_eq (1, a) + +# IF THEN ELSE +# ============ + +var b = 0 + +b = 0 + +if true + b = 1 +else if true + b = 2 +else if true + b = 3 +else + b = 4 +end + +assert_eq (1, b) + + +b = 0 + +if false + b = 1 +else if true + b = 2 +else if true + b = 3 +else + b = 4 +end + +assert_eq (2, b) + +b = 0 + +if false + b = 1 +else if false + b = 2 +else if true + b = 3 +else + b = 4 +end + +assert_eq (3, b) + +b = 0 + +if false + b = 1 +else if false + b = 2 +else if false + b = 3 +else + b = 4 +end + +assert_eq (4, b)