if expressions.

main
bog 2024-04-02 19:16:22 +02:00
parent 8e9b6f956d
commit 74e5f5225d
11 changed files with 267 additions and 5 deletions

View File

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

62
features/conds.sk Normal file
View File

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

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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; i<to_end.size; i++)
{
size_t addr = (size_t) to_end.data[i];
((size_t*) prog->params.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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

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

View File

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