diff --git a/doc/roza.bnf b/doc/roza.bnf index 305d468..9ee02d7 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -3,12 +3,15 @@ EXPR ::= | ASSERT | VARDECL | VARSET -| BLOCK +| SCOPE +| IF | OR ASSERT ::= assert EXPR VARDECL ::= let ident assign EXPR VARSET ::= ident assign EXPR -BLOCK ::= begin EXPR* end +BLOCK ::= EXPR* +SCOPE ::= begin BLOCK end +IF ::= if EXPR then BLOCK (else BLOCK | IF)? end OR ::= AND (or AND)* AND ::= EQNE (and EQNE)* EQNE ::= diff --git a/lib/compiler.c b/lib/compiler.c index 0203a05..675b4d4 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -309,6 +309,10 @@ int compiler_run(compiler_t* compiler, node_t* node) } } break; + case NODE_SCOPE: { + compiler_run(compiler, (node_t*) node->children.data[0]); + } break; + case NODE_BLOCK: { compiler->scope++; for (size_t i=0; ichildren.size; i++) @@ -319,6 +323,23 @@ int compiler_run(compiler_t* compiler, node_t* node) compiler->scope--; } break; + case NODE_IF: { + size_t const IF_LIMIT = 1024; + + int end_points[IF_LIMIT]; + size_t size = 0; + + compiler_run_if(compiler, node, end_points, &size); + assert(size < IF_LIMIT); + + for (size_t i=0; imod->program.params[end_points[i]] + = compiler->mod->program.size; + } + + } break; + default: { fprintf(stderr, "Cannot compile unknown node '%s'", NodeTypeStr[node->type]); @@ -328,3 +349,39 @@ int compiler_run(compiler_t* compiler, node_t* node) return 0; } + +void compiler_run_if(compiler_t* compiler, node_t* node, + int* end_points, size_t* sz) +{ + assert(compiler); + assert(node); + + // if + compiler_run(compiler, (node_t*) node->children.data[0]); + // if false goto next + int to_next = mod_push_instr(compiler->mod, OP_BRF, RZ_NO_PARAM); + + // then + compiler_run(compiler, (node_t*) node->children.data[1]); + // goto end + int to_end = mod_push_instr(compiler->mod, OP_BR, RZ_NO_PARAM); + end_points[*sz] = to_end; + (*sz)++; + + compiler->mod->program.params[to_next] + = compiler->mod->program.size; + + if (node->children.size >= 3) + { + node_t* next = (node_t*) node->children.data[2]; + + if (next->type == NODE_BLOCK) + { + compiler_run(compiler, next); + } + else if (next->type == NODE_IF) + { + compiler_run_if(compiler, next, end_points, sz); + } + } +} diff --git a/lib/compiler.h b/lib/compiler.h index e995979..d7ea233 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -26,5 +26,7 @@ void compiler_init(compiler_t* compiler, void compiler_free(compiler_t* compiler); int compiler_run(compiler_t* compiler, node_t* node); +void compiler_run_if(compiler_t* compiler, node_t* node, + int* end_points, size_t* sz); #endif diff --git a/lib/lexer.c b/lib/lexer.c index a88716a..f41e232 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -71,6 +71,9 @@ node_t* lexer_try_new_next(lexer_t* lexer) // Keywords // ======== + RZ_KEYWORD("if", NODE_IF, 0); + RZ_KEYWORD("then", NODE_THEN, 0); + RZ_KEYWORD("else", NODE_ELSE, 0); RZ_KEYWORD("begin", NODE_BEGIN, 0); RZ_KEYWORD("end", NODE_END, 0); RZ_KEYWORD("let", NODE_LET, 0); diff --git a/lib/node.h b/lib/node.h index 7bee8e2..e661b73 100644 --- a/lib/node.h +++ b/lib/node.h @@ -13,7 +13,8 @@ G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), \ G(NODE_LET), G(NODE_IDENT), G(NODE_ASSIGN), G(NODE_VARDECL), \ - G(NODE_VARSET), G(NODE_BEGIN), G(NODE_END), G(NODE_BLOCK) + G(NODE_VARSET), G(NODE_BEGIN), G(NODE_END), G(NODE_BLOCK), \ + G(NODE_SCOPE), G(NODE_IF), G(NODE_THEN), G(NODE_ELSE) RZ_ENUM_H(NodeType, NODE_TYPE); diff --git a/lib/parser.c b/lib/parser.c index 697061b..1705c44 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -64,6 +64,11 @@ node_t* parser_try_new_expr(parser_t* parser) return parser_try_new_varset(parser); } + if (type_1 == NODE_IF) + { + return parser_try_new_if(parser); + } + if (type_1 == NODE_ASSERT) { return parser_try_new_assert(parser); @@ -76,7 +81,7 @@ node_t* parser_try_new_expr(parser_t* parser) if (type_1 == NODE_BEGIN) { - return parser_try_new_block(parser); + return parser_try_new_scope(parser); } return parser_try_new_or(parser); @@ -141,18 +146,69 @@ node_t* parser_try_new_block(parser_t* parser) node_t* block = malloc(sizeof(node_t)); node_init(block, NODE_BLOCK, "", parser->lexer->line); - parser_skip(parser, NODE_BEGIN); - - NodeType next; - - while ( (next = lexer_peek(parser->lexer, 1)) != NODE_END) + while (1) { + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_END + || next == NODE_ELSE) + { + break; + } + node_add_new_child(block, parser_try_new_expr(parser)); } + return block; +} + +node_t* parser_try_new_scope(parser_t* parser) +{ + assert(parser); + parser_skip(parser, NODE_BEGIN); + node_t* block = parser_try_new_block(parser); parser_skip(parser, NODE_END); - return block; + node_t* scope = malloc(sizeof(node_t)); + node_init(scope, NODE_SCOPE, "", parser->lexer->line); + node_add_new_child(scope, block); + + return scope; +} + +node_t* parser_try_new_if(parser_t* parser) +{ + assert(parser); + + node_t* node = parser_try_new_consume(parser, NODE_IF); + node_add_new_child(node, parser_try_new_expr(parser)); + + parser_skip(parser, NODE_THEN); + node_add_new_child(node, parser_try_new_block(parser)); + + int next = lexer_peek(parser->lexer, 1); + + if (next == NODE_ELSE) + { + parser_skip(parser, NODE_ELSE); + int n = lexer_peek(parser->lexer, 1); + + if (n == NODE_IF) + { + node_add_new_child(node, parser_try_new_if(parser)); + } + else + { + node_add_new_child(node, parser_try_new_block(parser)); + parser_skip(parser, NODE_END); + } + } + else + { + parser_skip(parser, NODE_END); + } + + return node; } node_t* parser_try_new_or(parser_t* parser) diff --git a/lib/parser.h b/lib/parser.h index d9af55d..f2c139a 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -20,6 +20,8 @@ node_t* parser_try_new_assert(parser_t* parser); node_t* parser_try_new_vardecl(parser_t* parser); node_t* parser_try_new_varset(parser_t* parser); node_t* parser_try_new_block(parser_t* parser); +node_t* parser_try_new_scope(parser_t* parser); +node_t* parser_try_new_if(parser_t* parser); node_t* parser_try_new_or(parser_t* parser); node_t* parser_try_new_and(parser_t* parser); node_t* parser_try_new_eqne(parser_t* parser); diff --git a/lib/sym.c b/lib/sym.c index 0450eca..4c358a8 100644 --- a/lib/sym.c +++ b/lib/sym.c @@ -34,7 +34,6 @@ int sym_declare(sym_t* sym, char* name, type_t* type, { assert(sym); assert(name); - assert(type); sym_entry_t* res = sym_try_find_by_name(sym, name, diff --git a/lib/type.c b/lib/type.c index 1d3503d..bde1a9b 100644 --- a/lib/type.c +++ b/lib/type.c @@ -23,9 +23,15 @@ int type_eq(type_t* type, type_t* rhs) size_t type_str(type_t* type, char* buffer, size_t size) { - assert(type); assert(buffer); - return snprintf(buffer, size, "%s", - TypeKindStr[type->kind] + strlen("TYPE_")); + if (type) + { + return snprintf(buffer, size, "%s", + TypeKindStr[type->kind] + strlen("TYPE_")); + } + else + { + return snprintf(buffer, size, "*unknown*"); + } } diff --git a/lib/tysolver.c b/lib/tysolver.c index 105e206..92c2c30 100644 --- a/lib/tysolver.c +++ b/lib/tysolver.c @@ -121,6 +121,10 @@ type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node) return ty; } break; + case NODE_IF: { + return NULL; + } break; + default: { tysolver_error(tysolver, node); } break; diff --git a/tests/acceptances/if.roza b/tests/acceptances/if.roza new file mode 100644 index 0000000..91fa261 --- /dev/null +++ b/tests/acceptances/if.roza @@ -0,0 +1,113 @@ +# SIMPLE IF +# ========= +let count = 0 + +if true then + count = 1 +end + +if false then + count = 0 +end + +assert count == 1 + +# ELSE +# ==== +count = 0 + +if true then + count = count + 1 +else +end + +if false then +else + count = count + 1 +end + +assert count == 2 + +# ELSE IF +# ======= +count = 0 + +if true then + count = 1 +else if true then + count = 0 +else if true then + count = 0 +else + count = 0 +end + +assert count == 1 +count = 0 + +if false then + count = 0 +else if true then + count = 1 +else if true then + count = 0 +else + count = 0 +end + +assert count == 1 +count = 0 + +if false then + count = 0 +else if false then + count = 0 +else if true then + count = 1 +else + count = 0 +end + +assert count == 1 +count = 0 + +if false then + count = 0 +else if false then + count = 0 +else if false then + count = 0 +else + count = 1 +end + +assert count == 1 + +# IF AS EXPR +# ========== +let a = if true then 5 else 6 end +let b = if false then 5 else 6 end + +assert a == 5 +assert b == 6 + +# IF BLOCKS +# ========= +let c = 34 + +if true then + let c = 9 + assert c == 9 +end + +assert c == 34 + +let d = "bim" + +if false then +else + let d = "bam" + assert d == "bam" +end + +assert d == "bim" \ No newline at end of file diff --git a/tests/units/lexer.c b/tests/units/lexer.c index 58437ab..70fe2a2 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -158,7 +158,8 @@ Test(lexer, let_var) { } Test(lexer, blocks) { - test_lexer("begin end", 2, - "BEGIN", - "END"); + test_lexer("if then else", 3, + "IF", + "THEN", + "ELSE"); } diff --git a/tests/units/parser.c b/tests/units/parser.c index be1c89e..e09847f 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -116,13 +116,26 @@ Test(parser, var_set) { } Test(parser, block) { - test_parser_ok("MOD(BLOCK)", + test_parser_ok("MOD(SCOPE(BLOCK))", " begin end "); - test_parser_ok("MOD(BLOCK(ADD(NUM[1],NUM[2])))", + test_parser_ok("MOD(SCOPE(BLOCK(ADD(NUM[1],NUM[2]))))", " begin 1 + 2 end "); - test_parser_ok("MOD(BLOCK(VARSET(IDENT[y],BOOL[true])" - ",VARSET(IDENT[z],BOOL[false])))", + test_parser_ok("MOD(SCOPE(BLOCK(VARSET(IDENT[y],BOOL[true])" + ",VARSET(IDENT[z],BOOL[false]))))", " begin y = true z = false end "); } + +Test(parser, if_then_else) { + test_parser_ok("MOD(IF(IDENT[x],BLOCK(IDENT[y])))", + "if x then y end"); + + test_parser_ok("MOD(IF(IDENT[x],BLOCK(IDENT[y]),BLOCK(IDENT[z])))", + "if x then y else z end"); + + test_parser_ok("MOD(IF(IDENT[x]," + "BLOCK(IDENT[y])," + "IF(IDENT[z],BLOCK(IDENT[u]),BLOCK(IDENT[v]))))", + "if x then y else if z then u else v end"); +}