diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 5e966d2..21ed6fc 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -6,6 +6,8 @@ EXPR ::= | CONST_DECL | ASSIGN | BLOCK +| IF +IF ::= if EXPR BLOCK (else (BLOCK | IF))? BLOCK ::= begin EXPR* end ASSIGN ::= ident assign EXPR VAR_DECL ::= var ident assign EXPR diff --git a/features/conds.sk b/features/conds.sk new file mode 100644 index 0000000..4d73364 --- /dev/null +++ b/features/conds.sk @@ -0,0 +1,62 @@ +var a = 4 + +if true + a = 5 +end + +assert a eq 5 + +if false + a = 12 +end + +assert a eq 5 + +if true + a = 1 +else if true + a = 2 +else if true + a = 3 +else + a = 4 +end + +assert a eq 1 + +if false + a = 1 +else if true + a = 2 +else if true + a = 3 +else + a = 4 +end + +assert a eq 2 + +if false + a = 1 +else if false + a = 2 +else if true + a = 3 +else + a = 4 +end + +assert a eq 3 + +if false + a = 1 +else if false + a = 2 +else if false + a = 3 +else + a = 4 +end + +assert a eq 4 + diff --git a/lib/include/compiler.h b/lib/include/compiler.h index 95da880..e1071ab 100644 --- a/lib/include/compiler.h +++ b/lib/include/compiler.h @@ -37,5 +37,11 @@ void compiler_compile_or(struct compiler* self, struct node* node, struct prog* prog, struct sym* sym); + +void compiler_compile_if(struct compiler* self, + struct node* node, + struct prog* prog, + struct sym* sym, + struct vec* to_end); #endif diff --git a/lib/include/node.h b/lib/include/node.h index 6efe52f..fa3f88d 100644 --- a/lib/include/node.h +++ b/lib/include/node.h @@ -14,7 +14,7 @@ G(NODE_NOT), G(NODE_FLOAT), G(NODE_STRING), \ G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \ G(NODE_CONST_DECL), G(NODE_IDENT), G(NODE_ASSIGN), \ -G(NODE_BLOCK) +G(NODE_BLOCK), G(NODE_IF) SK_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/include/parser.h b/lib/include/parser.h index d12f5b8..d5e50f8 100644 --- a/lib/include/parser.h +++ b/lib/include/parser.h @@ -20,6 +20,8 @@ struct node* parser_try_parse(struct parser* self); struct node* parser_try_root(struct parser* self); struct node* parser_try_expr(struct parser* self); struct node* parser_try_block(struct parser* self); +struct node* parser_try_inner_block(struct parser* self); +struct node* parser_try_if(struct parser* self); struct node* parser_try_assign(struct parser* self); struct node* parser_try_var_decl(struct parser* self); struct node* parser_try_const_decl(struct parser* self); diff --git a/lib/include/token.h b/lib/include/token.h index 59d7f63..1f2f855 100644 --- a/lib/include/token.h +++ b/lib/include/token.h @@ -15,7 +15,7 @@ G(TOKEN_NOT), G(TOKEN_FLOAT), G(TOKEN_STRING), \ G(TOKEN_LT), G(TOKEN_LE), G(TOKEN_GT), G(TOKEN_GE), \ G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \ G(TOKEN_CONST), G(TOKEN_ASSIGN), G(TOKEN_IDENT), \ -G(TOKEN_BEGIN), G(TOKEN_END) +G(TOKEN_BEGIN), G(TOKEN_END), G(TOKEN_IF), G(TOKEN_ELSE) SK_ENUM_H(TokenKind, TOKEN_KIND); diff --git a/lib/src/compiler.c b/lib/src/compiler.c index 0231de4..3b3355c 100644 --- a/lib/src/compiler.c +++ b/lib/src/compiler.c @@ -40,6 +40,22 @@ void compiler_compile(struct compiler* self, sym_close_scope(sym); } break; + case NODE_IF: { + struct vec to_end; + vec_init(&to_end); + + compiler_compile_if(self, node, prog, sym, &to_end); + size_t end_point = prog->params.size; + + for (size_t i=0; iparams.data)[addr] = end_point; + } + + vec_free(&to_end); + } break; + case NODE_NOT: { compiler_compile_children(self, node, prog, sym); prog_add_instr(prog, OP_NOT, SK_NO_PARAM); @@ -338,3 +354,37 @@ void compiler_compile_or(struct compiler* self, vec_free(&to_true); } + +void compiler_compile_if(struct compiler* self, + struct node* node, + struct prog* prog, + struct sym* sym, + struct vec* to_end) +{ + struct node* cond = node->children.data[0]; + struct node* block = node->children.data[1]; + + compiler_compile(self, cond, prog, sym); + size_t brf = prog_add_instr(prog, OP_BRF, 40); // to next + + compiler_compile(self, block, prog, sym); + + size_t br = prog_add_instr(prog, OP_BR, 0); // to end + vec_push(to_end, (void*) br); + + size_t next_point = prog->opcodes.size; + ((size_t*)prog->params.data)[brf] = next_point; + + if (node->children.size == 3) + { + struct node* next = node->children.data[2]; + if (next->kind == NODE_IF) + { + compiler_compile_if(self, next, prog, sym, to_end); + } + else + { + compiler_compile(self, next, prog, sym); + } + } +} diff --git a/lib/src/lexer.c b/lib/src/lexer.c index 470553d..378f8e9 100644 --- a/lib/src/lexer.c +++ b/lib/src/lexer.c @@ -193,6 +193,8 @@ struct token* lexer_try_new_next(struct lexer* self) SK_SCAN_TEXT(">", TOKEN_GT); SK_SCAN_TEXT("<", TOKEN_LT); SK_SCAN_TEXT("=", TOKEN_ASSIGN); + SK_SCAN_KEYWORD("if", TOKEN_IF, NULL); + SK_SCAN_KEYWORD("else", TOKEN_ELSE, NULL); SK_SCAN_KEYWORD("begin", TOKEN_BEGIN, NULL); SK_SCAN_KEYWORD("end", TOKEN_END, NULL); SK_SCAN_KEYWORD("var", TOKEN_VAR, NULL); diff --git a/lib/src/parser.c b/lib/src/parser.c index 447286f..0b99986 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -115,6 +115,11 @@ struct node* parser_try_expr(struct parser* self) return SK_TRY(parser_try_block); } + if (lexer_next_is(&self->lexer, TOKEN_IF)) + { + return SK_TRY(parser_try_if); + } + return SK_TRY(parser_try_or); } @@ -129,7 +134,31 @@ struct node* parser_try_block(struct parser* self) struct token* tok = lexer_try_new_next(&self->lexer); + struct node* node = SK_TRY(parser_try_inner_block); + + if (!node) + { + token_free(tok); free(tok); + return NULL; + } + + if (!lexer_next_is(&self->lexer, TOKEN_END)) + { + node_free(node); free(node); + return NULL; + } + + lexer_consume_next(&self->lexer); + return node; +} + +struct node* parser_try_inner_block(struct parser* self) +{ + assert(self); + struct node* node = malloc(sizeof(struct node)); + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_BEGIN, "", self->lexer.context.line); node_init(node, NODE_BLOCK, tok); while (true) @@ -144,13 +173,100 @@ struct node* parser_try_block(struct parser* self) node_push_new_child(node, child); } - if (!lexer_next_is(&self->lexer, TOKEN_END)) + return node; +} + +struct node* parser_try_if(struct parser* self) +{ + assert(self); + + if (!lexer_next_is(&self->lexer, TOKEN_IF)) { - node_free(node); free(node); return NULL; } - lexer_consume_next(&self->lexer); + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* cond = SK_TRY(parser_try_expr); + + if (!cond) + { + token_free(tok); free(tok); + return NULL; + } + + struct node* block = SK_TRY(parser_try_inner_block); + + if (0&&lexer_next_is(&self->lexer, TOKEN_END)) + { + lexer_consume_next(&self->lexer); + } + + + if (!block) + { + token_free(tok); free(tok); + node_free(cond); free(cond); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_IF, tok); + node_push_new_child(node, cond); + node_push_new_child(node, block); + + if (lexer_next_is(&self->lexer, TOKEN_ELSE)) + { + lexer_consume_next(&self->lexer); + + if (lexer_next_is(&self->lexer, TOKEN_IF)) + { + struct node* next = SK_TRY(parser_try_if); + + if (!next) + { + token_free(tok); free(tok); + node_free(node); free(node); + return NULL; + } + + node_push_new_child(node, next); + } + else + { + struct node* else_block + = SK_TRY(parser_try_inner_block); + + if (!else_block) + { + token_free(tok); free(tok); + node_free(node); free(node); + return NULL; + } + + node_push_new_child(node, else_block); + + if (!lexer_next_is(&self->lexer, TOKEN_END)) + { + token_free(tok); free(tok); + node_free(node); free(node); + return NULL; + } + + lexer_consume_next(&self->lexer); + } + } + else + { + if (!lexer_next_is(&self->lexer, TOKEN_END)) + { + token_free(tok); free(tok); + node_free(node); free(node); + return NULL; + } + + lexer_consume_next(&self->lexer); + } + return node; } diff --git a/tests/lexer.h b/tests/lexer.h index 8ec51f2..64b5beb 100644 --- a/tests/lexer.h +++ b/tests/lexer.h @@ -127,6 +127,13 @@ static void test_lexer_block() ); } +static void test_lexer_cond() +{ + test_lexer("if else", 2, + "IF", + "ELSE" + ); +} void register_lexer() { CU_pSuite suite = CU_add_suite("Lexer", 0, 0); @@ -138,6 +145,7 @@ void register_lexer() CU_add_test(suite, "Comparisons", test_lexer_cmp); CU_add_test(suite, "Var Declarations", test_lexer_decl); CU_add_test(suite, "Blocks", test_lexer_block); + CU_add_test(suite, "Conditionnals", test_lexer_cond); } #endif diff --git a/tests/parser.h b/tests/parser.h index f2ccbcb..666d202 100644 --- a/tests/parser.h +++ b/tests/parser.h @@ -114,6 +114,19 @@ static void test_parser_block() "begin 1 2 end"); } +static void test_parser_if() +{ + test_parser("ROOT(IF(BOOL[true],BLOCK(INT[0])))", + "if true 0 end"); + + test_parser("ROOT(IF(BOOL[true],BLOCK(INT[0]),BLOCK(INT[1])))", + "if true 0 else 1 end"); + + test_parser("ROOT(IF(BOOL[true],BLOCK(INT[0])," + "IF(BOOL[false],BLOCK(INT[1]),BLOCK(INT[2]))))", + "if true 0 else if false 1 else 2 end"); +} + void register_parser() { CU_pSuite suite = CU_add_suite("Parser", 0, 0); @@ -125,6 +138,7 @@ void register_parser() CU_add_test(suite, "Declarations", test_parser_decl); CU_add_test(suite, "Assignments", test_parser_assign); CU_add_test(suite, "Blocks", test_parser_block); + CU_add_test(suite, "IfExpression", test_parser_if); } #endif