✨ if expressions.
parent
8e9b6f956d
commit
74e5f5225d
|
@ -6,6 +6,8 @@ EXPR ::=
|
||||||
| CONST_DECL
|
| CONST_DECL
|
||||||
| ASSIGN
|
| ASSIGN
|
||||||
| BLOCK
|
| BLOCK
|
||||||
|
| IF
|
||||||
|
IF ::= if EXPR BLOCK (else (BLOCK | IF))?
|
||||||
BLOCK ::= begin EXPR* end
|
BLOCK ::= begin EXPR* end
|
||||||
ASSIGN ::= ident assign EXPR
|
ASSIGN ::= ident assign EXPR
|
||||||
VAR_DECL ::= var ident assign EXPR
|
VAR_DECL ::= var ident assign EXPR
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -37,5 +37,11 @@ void compiler_compile_or(struct compiler* self,
|
||||||
struct node* node,
|
struct node* node,
|
||||||
struct prog* prog,
|
struct prog* prog,
|
||||||
struct sym* sym);
|
struct sym* sym);
|
||||||
|
|
||||||
|
void compiler_compile_if(struct compiler* self,
|
||||||
|
struct node* node,
|
||||||
|
struct prog* prog,
|
||||||
|
struct sym* sym,
|
||||||
|
struct vec* to_end);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \
|
||||||
G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \
|
G(NODE_EQUAL), G(NODE_NOT_EQUAL), G(NODE_VAR_DECL), \
|
||||||
G(NODE_CONST_DECL), G(NODE_IDENT), G(NODE_ASSIGN), \
|
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);
|
SK_ENUM_H(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ struct node* parser_try_parse(struct parser* self);
|
||||||
struct node* parser_try_root(struct parser* self);
|
struct node* parser_try_root(struct parser* self);
|
||||||
struct node* parser_try_expr(struct parser* self);
|
struct node* parser_try_expr(struct parser* self);
|
||||||
struct node* parser_try_block(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_assign(struct parser* self);
|
||||||
struct node* parser_try_var_decl(struct parser* self);
|
struct node* parser_try_var_decl(struct parser* self);
|
||||||
struct node* parser_try_const_decl(struct parser* self);
|
struct node* parser_try_const_decl(struct parser* self);
|
||||||
|
|
|
@ -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_LT), G(TOKEN_LE), G(TOKEN_GT), G(TOKEN_GE), \
|
||||||
G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \
|
G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL), G(TOKEN_VAR), \
|
||||||
G(TOKEN_CONST), G(TOKEN_ASSIGN), G(TOKEN_IDENT), \
|
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);
|
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,22 @@ void compiler_compile(struct compiler* self,
|
||||||
sym_close_scope(sym);
|
sym_close_scope(sym);
|
||||||
} break;
|
} 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: {
|
case NODE_NOT: {
|
||||||
compiler_compile_children(self, node, prog, sym);
|
compiler_compile_children(self, node, prog, sym);
|
||||||
prog_add_instr(prog, OP_NOT, SK_NO_PARAM);
|
prog_add_instr(prog, OP_NOT, SK_NO_PARAM);
|
||||||
|
@ -338,3 +354,37 @@ void compiler_compile_or(struct compiler* self,
|
||||||
|
|
||||||
vec_free(&to_true);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -193,6 +193,8 @@ struct token* lexer_try_new_next(struct lexer* self)
|
||||||
SK_SCAN_TEXT(">", TOKEN_GT);
|
SK_SCAN_TEXT(">", TOKEN_GT);
|
||||||
SK_SCAN_TEXT("<", TOKEN_LT);
|
SK_SCAN_TEXT("<", TOKEN_LT);
|
||||||
SK_SCAN_TEXT("=", TOKEN_ASSIGN);
|
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("begin", TOKEN_BEGIN, NULL);
|
||||||
SK_SCAN_KEYWORD("end", TOKEN_END, NULL);
|
SK_SCAN_KEYWORD("end", TOKEN_END, NULL);
|
||||||
SK_SCAN_KEYWORD("var", TOKEN_VAR, NULL);
|
SK_SCAN_KEYWORD("var", TOKEN_VAR, NULL);
|
||||||
|
|
116
lib/src/parser.c
116
lib/src/parser.c
|
@ -115,6 +115,11 @@ struct node* parser_try_expr(struct parser* self)
|
||||||
return SK_TRY(parser_try_block);
|
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);
|
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 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 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);
|
node_init(node, NODE_BLOCK, tok);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -144,13 +173,100 @@ struct node* parser_try_block(struct parser* self)
|
||||||
node_push_new_child(node, child);
|
node_push_new_child(node, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_if(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!lexer_next_is(&self->lexer, TOKEN_IF))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
if (!lexer_next_is(&self->lexer, TOKEN_END))
|
||||||
{
|
{
|
||||||
|
token_free(tok); free(tok);
|
||||||
node_free(node); free(node);
|
node_free(node); free(node);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
lexer_consume_next(&self->lexer);
|
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;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,13 @@ static void test_lexer_block()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_lexer_cond()
|
||||||
|
{
|
||||||
|
test_lexer("if else", 2,
|
||||||
|
"IF",
|
||||||
|
"ELSE"
|
||||||
|
);
|
||||||
|
}
|
||||||
void register_lexer()
|
void register_lexer()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
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, "Comparisons", test_lexer_cmp);
|
||||||
CU_add_test(suite, "Var Declarations", test_lexer_decl);
|
CU_add_test(suite, "Var Declarations", test_lexer_decl);
|
||||||
CU_add_test(suite, "Blocks", test_lexer_block);
|
CU_add_test(suite, "Blocks", test_lexer_block);
|
||||||
|
CU_add_test(suite, "Conditionnals", test_lexer_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -114,6 +114,19 @@ static void test_parser_block()
|
||||||
"begin 1 2 end");
|
"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()
|
void register_parser()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
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, "Declarations", test_parser_decl);
|
||||||
CU_add_test(suite, "Assignments", test_parser_assign);
|
CU_add_test(suite, "Assignments", test_parser_assign);
|
||||||
CU_add_test(suite, "Blocks", test_parser_block);
|
CU_add_test(suite, "Blocks", test_parser_block);
|
||||||
|
CU_add_test(suite, "IfExpression", test_parser_if);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue