From 3b7b0b42959ccc05be006634f6dacc8e4569b2bd Mon Sep 17 00:00:00 2001 From: bog Date: Tue, 13 Feb 2024 05:14:56 +0100 Subject: [PATCH] :sparkles: if statement. --- bc/src/compiler.c | 70 +++++++++++++++++++++++++++++++++++++++++ bc/src/compiler.h | 5 +++ doc/gux.bnf | 9 +++++- lang/src/lexer.c | 6 ++++ lang/src/node.h | 4 ++- lang/src/parser.c | 35 ++++++++++++++++++++- lang/src/parser.h | 1 + lang/src/type_checker.c | 36 +++++++++++++++++++++ lang/tests/lexer.c | 5 +++ lang/tests/parser.c | 17 ++++++++++ tests/flow_control.gux | 61 +++++++++++++++++++++++++++++++++++ 11 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 tests/flow_control.gux diff --git a/bc/src/compiler.c b/bc/src/compiler.c index 1da7fcf..9d961d2 100644 --- a/bc/src/compiler.c +++ b/bc/src/compiler.c @@ -30,6 +30,26 @@ int compiler_compile(struct compiler* self, switch (node->type) { + case NODE_IF: { + struct vec to_end; + vec_init(&to_end, 1); + + compiler_compile_if(self, node, program, &to_end); + + size_t end = program->instructions.size; + + for (size_t i=0; iinstructions.data[*addr])->param + = end; + } + + vec_free_elements(&to_end); + vec_free(&to_end); + } break; + case NODE_BLOCK: { int stack_base = self->stack_size; @@ -343,6 +363,56 @@ int compiler_compile(struct compiler* self, return 0; } +int compiler_compile_if(struct compiler* self, + struct node* node, + struct program* program, + struct vec* to_end) +{ + assert(node->children.size >= 2); + + struct instruction* instr = NULL; + + // if expr + if (compiler_compile(self, node->children.data[0], program) != 0) + { + return 1; + } + + size_t goto_else = compiler_gen_instr(self, program, OP_BRF, 0); + + // block + if (compiler_compile(self, node->children.data[1], program) != 0) + { + return 1; + } + + size_t* goto_end = malloc(sizeof(size_t)); + *goto_end = compiler_gen_instr(self, program, OP_BR, 0); + vec_push(to_end, goto_end); + + instr = program->instructions.data[goto_else]; + instr->param = program->instructions.size; + + // else block + if (node->children.size > 2 + && ((struct node*) node->children.data[2])->type == NODE_BLOCK + && compiler_compile(self, node->children.data[2], program) != 0) + { + return 1; + } + + // else + if (node->children.size > 2 + && ((struct node*) node->children.data[2])->type == NODE_IF + && compiler_compile_if(self, node->children.data[2], program, + to_end) != 0) + { + return 1; + } + + return 0; +} + size_t compiler_gen_instr(struct compiler* self, struct program* program, enum Opcodes op, diff --git a/bc/src/compiler.h b/bc/src/compiler.h index 4301be1..25c4931 100644 --- a/bc/src/compiler.h +++ b/bc/src/compiler.h @@ -20,6 +20,11 @@ int compiler_compile(struct compiler* self, struct node* node, struct program* program); +int compiler_compile_if(struct compiler* self, + struct node* node, + struct program* program, + struct vec* to_end); + size_t compiler_gen_instr(struct compiler* self, struct program* program, enum Opcodes op, diff --git a/doc/gux.bnf b/doc/gux.bnf index 9bcc89a..5172681 100644 --- a/doc/gux.bnf +++ b/doc/gux.bnf @@ -13,7 +13,14 @@ LEXPR ::= | CONSTDECL | ASSIGN -BEXPR ::= BLOCK +BEXPR ::= +| BLOCK +| IF + +IF ::= +| if EXPR BLOCK +| if EXPR BLOCK else BLOCK +| if EXPR BLOCK else IF BLOCK ::= obrace INSTR* cbrace VARDECL ::= var ident colon type? assign EXPR diff --git a/lang/src/lexer.c b/lang/src/lexer.c index 5e81778..72b7755 100644 --- a/lang/src/lexer.c +++ b/lang/src/lexer.c @@ -12,6 +12,12 @@ void lexer_init(struct lexer* self, char const* source) vec_init(&self->toks, 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); + lexer_add_tok(self, "while", NODE_WHILE, "", 1); + lexer_add_tok(self, "for", NODE_FOR, "", 1); + lexer_add_tok(self, "var", NODE_VAR, "", 1); lexer_add_tok(self, "int", NODE_TYPE, "int", 1); diff --git a/lang/src/node.h b/lang/src/node.h index 86ba439..cea504a 100644 --- a/lang/src/node.h +++ b/lang/src/node.h @@ -14,7 +14,9 @@ G(NODE_MOD), G(NODE_POW), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ 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_OBRACE), G(NODE_CBRACE), G(NODE_BLOCK), \ + G(NODE_IF), G(NODE_ELSE), G(NODE_COND), G(NODE_WHILE), G(NODE_FOR) + GUX_ENUM_H(NodeType, NODE_TYPE); diff --git a/lang/src/parser.c b/lang/src/parser.c index 6bf024b..ec00912 100644 --- a/lang/src/parser.c +++ b/lang/src/parser.c @@ -132,7 +132,8 @@ int parser_start_bexpr(struct parser* self) assert(self); struct node* tok = self->tokens.data[self->cursor]; - return tok->type == NODE_OBRACE; + return tok->type == NODE_OBRACE + || tok->type == NODE_IF; } struct node* parser_try_new_root(struct parser* self, char const* source) @@ -254,9 +255,41 @@ struct node* parser_try_new_lexpr(struct parser* self) struct node* parser_try_new_bexpr(struct parser* self) { assert(self); + + if (parser_type_is(self, NODE_IF, 0)) + { + return parser_try_new_if(self); + } + return parser_try_new_block(self); } +struct node* parser_try_new_if(struct parser* self) +{ + assert(self); + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_IF, "", parser_current_line(self)); + + self->cursor++; // if + node_add_child(node, parser_try_new_expr(self)); + node_add_child(node, parser_try_new_block(self)); + + if (parser_try_consume(self, NODE_ELSE) == 0) + { + if (parser_type_is(self, NODE_IF, 0)) + { + node_add_child(node, parser_try_new_if(self)); + } + + if (parser_type_is(self, NODE_OBRACE, 0)) + { + 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 bb392a8..7ce77ef 100644 --- a/lang/src/parser.h +++ b/lang/src/parser.h @@ -31,6 +31,7 @@ struct node* parser_try_new_instr(struct parser* self); 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_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 74b5f31..960c592 100644 --- a/lang/src/type_checker.c +++ b/lang/src/type_checker.c @@ -27,6 +27,42 @@ int type_checker_check(struct type_checker* self, switch (node->type) { + case NODE_IF: { + struct type expr; + type_init(&expr, TYPE_VOID); + + if (type_resolver_resolve(&self->resolver, + node->children.data[0], + &expr) != 0) + { + type_free(&expr); + return 1; + } + + if (!type_is(&expr, "BOOL")) + { + struct type bool_ty; + type_init(&bool_ty, TYPE_BOOL); + + type_checker_error_msg(self, node, &bool_ty, &expr); + + type_free(&bool_ty); + type_free(&expr); + return 1; + } + + type_free(&expr); + + if (node->children.size > 2) + { + if (type_checker_check(self, node->children.data[2]) != 0) + { + return 1; + } + } + + } return 0; + case NODE_ASSIGN: { struct node* ident = node->children.data[0]; struct node* expr = node->children.data[1]; diff --git a/lang/tests/lexer.c b/lang/tests/lexer.c index 3a33f2a..6e393ec 100644 --- a/lang/tests/lexer.c +++ b/lang/tests/lexer.c @@ -125,3 +125,8 @@ Test(lexer, var_types) { Test(lexer, blocks) { test_lexer("{}", 2, "OBRACE", "CBRACE"); } + +Test(lexer, flow_control) { + test_lexer("if else cond while for", 5, + "IF", "ELSE", "COND", "WHILE", "FOR"); +} diff --git a/lang/tests/parser.c b/lang/tests/parser.c index bcc82e0..621c3cc 100644 --- a/lang/tests/parser.c +++ b/lang/tests/parser.c @@ -132,3 +132,20 @@ Test(parser, block) { test_parser("ROOT(BLOCK(ASSIGN(IDENT[x],INT[2]),ADD(INT[1],INT[1])))", "{ x = 2; 1 + 1;}"); } + +Test(parser, if_block) { + test_parser("ROOT(IF(IDENT[a],BLOCK(INT[0])))", + "if a { 0; }"); + + test_parser("ROOT(IF(IDENT[a],BLOCK(INT[0]),BLOCK(INT[1])))", + "if a { 0; } else { 1; }"); + + test_parser("ROOT(IF(IDENT[a],BLOCK(" + "INT[0]" + "),IF(IDENT[b],BLOCK(" + "INT[1]" + "),BLOCK(" + "INT[2]" + "))))", + "if a { 0; } else if b { 1; } else { 2; }"); +} diff --git a/tests/flow_control.gux b/tests/flow_control.gux new file mode 100644 index 0000000..0e438b2 --- /dev/null +++ b/tests/flow_control.gux @@ -0,0 +1,61 @@ +var a := 0; + +if true { a = 1; } +assert a == 1; + +if false { a = 2; } +assert a == 1; + +if true { a = 3; } else { a = 4; } +assert a == 3; + +if false { a = 3; } else { a = 4; } +assert a == 4; + +if true { + a = 5; +} else if false { + a = 6; +} else if false { + a = 7; +} else { + a = 8; +} + +assert a == 5; + +if false { + a = 5; +} else if true { + a = 6; +} else if false { + a = 7; +} else { + a = 8; +} + +assert a == 6; + +if false { + a = 5; +} else if false { + a = 6; +} else if true { + a = 7; +} else { + a = 8; +} + +assert a == 7; + +if false { + a = 5; +} else if false { + a = 6; +} else if false { + a = 7; +} else { + a = 8; +} + +assert a == 8;