'if then else' expressions.

main
bog 2023-12-18 19:34:31 +01:00
parent 400f31025a
commit a27eda5eda
13 changed files with 281 additions and 21 deletions

View File

@ -3,12 +3,15 @@ EXPR ::=
| ASSERT | ASSERT
| VARDECL | VARDECL
| VARSET | VARSET
| BLOCK | SCOPE
| IF
| OR | OR
ASSERT ::= assert EXPR ASSERT ::= assert EXPR
VARDECL ::= let ident assign EXPR VARDECL ::= let ident assign EXPR
VARSET ::= 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)* OR ::= AND (or AND)*
AND ::= EQNE (and EQNE)* AND ::= EQNE (and EQNE)*
EQNE ::= EQNE ::=

View File

@ -309,6 +309,10 @@ int compiler_run(compiler_t* compiler, node_t* node)
} }
} break; } break;
case NODE_SCOPE: {
compiler_run(compiler, (node_t*) node->children.data[0]);
} break;
case NODE_BLOCK: { case NODE_BLOCK: {
compiler->scope++; compiler->scope++;
for (size_t i=0; i<node->children.size; i++) for (size_t i=0; i<node->children.size; i++)
@ -319,6 +323,23 @@ int compiler_run(compiler_t* compiler, node_t* node)
compiler->scope--; compiler->scope--;
} break; } 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; i<size; i++)
{
compiler->mod->program.params[end_points[i]]
= compiler->mod->program.size;
}
} break;
default: { default: {
fprintf(stderr, "Cannot compile unknown node '%s'", fprintf(stderr, "Cannot compile unknown node '%s'",
NodeTypeStr[node->type]); NodeTypeStr[node->type]);
@ -328,3 +349,39 @@ int compiler_run(compiler_t* compiler, node_t* node)
return 0; 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);
}
}
}

View File

@ -26,5 +26,7 @@ void compiler_init(compiler_t* compiler,
void compiler_free(compiler_t* compiler); void compiler_free(compiler_t* compiler);
int compiler_run(compiler_t* compiler, node_t* node); 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 #endif

View File

@ -71,6 +71,9 @@ node_t* lexer_try_new_next(lexer_t* lexer)
// Keywords // 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("begin", NODE_BEGIN, 0);
RZ_KEYWORD("end", NODE_END, 0); RZ_KEYWORD("end", NODE_END, 0);
RZ_KEYWORD("let", NODE_LET, 0); RZ_KEYWORD("let", NODE_LET, 0);

View File

@ -13,7 +13,8 @@
G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR), \ G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR), \
G(NODE_AND), G(NODE_OR), G(NODE_NOT), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), \
G(NODE_LET), G(NODE_IDENT), G(NODE_ASSIGN), G(NODE_VARDECL), \ 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); RZ_ENUM_H(NodeType, NODE_TYPE);

View File

@ -64,6 +64,11 @@ node_t* parser_try_new_expr(parser_t* parser)
return parser_try_new_varset(parser); return parser_try_new_varset(parser);
} }
if (type_1 == NODE_IF)
{
return parser_try_new_if(parser);
}
if (type_1 == NODE_ASSERT) if (type_1 == NODE_ASSERT)
{ {
return parser_try_new_assert(parser); return parser_try_new_assert(parser);
@ -76,7 +81,7 @@ node_t* parser_try_new_expr(parser_t* parser)
if (type_1 == NODE_BEGIN) if (type_1 == NODE_BEGIN)
{ {
return parser_try_new_block(parser); return parser_try_new_scope(parser);
} }
return parser_try_new_or(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_t* block = malloc(sizeof(node_t));
node_init(block, NODE_BLOCK, "", parser->lexer->line); node_init(block, NODE_BLOCK, "", parser->lexer->line);
parser_skip(parser, NODE_BEGIN); while (1)
NodeType next;
while ( (next = lexer_peek(parser->lexer, 1)) != NODE_END)
{ {
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)); 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); 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) node_t* parser_try_new_or(parser_t* parser)

View File

@ -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_vardecl(parser_t* parser);
node_t* parser_try_new_varset(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_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_or(parser_t* parser);
node_t* parser_try_new_and(parser_t* parser); node_t* parser_try_new_and(parser_t* parser);
node_t* parser_try_new_eqne(parser_t* parser); node_t* parser_try_new_eqne(parser_t* parser);

View File

@ -34,7 +34,6 @@ int sym_declare(sym_t* sym, char* name, type_t* type,
{ {
assert(sym); assert(sym);
assert(name); assert(name);
assert(type);
sym_entry_t* res = sym_try_find_by_name(sym, sym_entry_t* res = sym_try_find_by_name(sym,
name, name,

View File

@ -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) size_t type_str(type_t* type, char* buffer, size_t size)
{ {
assert(type);
assert(buffer); assert(buffer);
return snprintf(buffer, size, "%s", if (type)
TypeKindStr[type->kind] + strlen("TYPE_")); {
return snprintf(buffer, size, "%s",
TypeKindStr[type->kind] + strlen("TYPE_"));
}
else
{
return snprintf(buffer, size, "*unknown*");
}
} }

View File

@ -121,6 +121,10 @@ type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node)
return ty; return ty;
} break; } break;
case NODE_IF: {
return NULL;
} break;
default: { default: {
tysolver_error(tysolver, node); tysolver_error(tysolver, node);
} break; } break;

113
tests/acceptances/if.roza Normal file
View File

@ -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"

View File

@ -158,7 +158,8 @@ Test(lexer, let_var) {
} }
Test(lexer, blocks) { Test(lexer, blocks) {
test_lexer("begin end", 2, test_lexer("if then else", 3,
"BEGIN", "IF",
"END"); "THEN",
"ELSE");
} }

View File

@ -116,13 +116,26 @@ Test(parser, var_set) {
} }
Test(parser, block) { Test(parser, block) {
test_parser_ok("MOD(BLOCK)", test_parser_ok("MOD(SCOPE(BLOCK))",
" begin end "); " 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 "); " begin 1 + 2 end ");
test_parser_ok("MOD(BLOCK(VARSET(IDENT[y],BOOL[true])" test_parser_ok("MOD(SCOPE(BLOCK(VARSET(IDENT[y],BOOL[true])"
",VARSET(IDENT[z],BOOL[false])))", ",VARSET(IDENT[z],BOOL[false]))))",
" begin y = true z = false end "); " 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");
}