diff --git a/.gitignore b/.gitignore index 3a7a3a8..c14a965 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *\#* build doc/build +vgcore.* diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 5c7cba0..0cad324 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -4,6 +4,9 @@ EXPR ::= | ASSERT | DECL | ASSIGN +| BEGIN +BEGIN ::= begin BLOCK end +BLOCK ::= EXPR* ASSIGN ::= (ident|INDEX) assign EXPR DECL ::= (var|const) ident assign EXPR ASSERT ::= (assert_eq|assert_ne) tuple diff --git a/lib/compiler.c b/lib/compiler.c index b80d145..d81171f 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -32,6 +32,24 @@ void compiler_compile(compiler_t* self, switch (node->kind) { + case NODE_BLOCK: { + + sym_open_scope(sym); + for (size_t i=0; ichildren.size; i++) + { + compiler_compile( + self, + node->children.data[i], + prog + ); + } + + sym_close_scope(sym); + } break; + + case NODE_BEGIN: { + compiler_compile(self, node->children.data[0], prog); + } break; case NODE_ASSIGN: { node_t* target = node->children.data[0]; node_t* expr = node->children.data[1]; @@ -125,8 +143,17 @@ void compiler_compile(compiler_t* self, case NODE_IDENT: { char const* name = node->value; - sym_entry_t* entry = sym_try_get_value(sym, name); - assert(entry); + sym_entry_t const* entry = sym_try_get_value( + sym, + name + ); + + if (!entry) { + err_push(&self->err, node->line, + "undefined '%s'", name); + return; + } + prog_add_instr(prog, OP_LOCAL_LOAD, entry->local_addr); } break; diff --git a/lib/lexer.c b/lib/lexer.c index fc871f3..aa9e9d9 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -171,6 +171,8 @@ node_t* lexer_try_new_next(lexer_t* self) } } + CCM_KEYWORD("begin", NODE_BEGIN, 0); + CCM_KEYWORD("end", NODE_END, 0); CCM_KEYWORD("var", NODE_VAR, 0); CCM_KEYWORD("const", NODE_CONST, 0); CCM_KEYWORD("assert_eq", NODE_ASSERT_EQ, 0); diff --git a/lib/node.h b/lib/node.h index 47bd665..8ed2cfa 100644 --- a/lib/node.h +++ b/lib/node.h @@ -15,7 +15,8 @@ G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX), \ G(NODE_STR), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ G(NODE_GE), G(NODE_EQ), G(NODE_NE), G(NODE_ARRAY), \ G(NODE_VAR), G(NODE_IDENT), G(NODE_ASSIGN), \ -G(NODE_VARDECL), G(NODE_CONST), G(NODE_CONSTDECL) +G(NODE_VARDECL), G(NODE_CONST), G(NODE_CONSTDECL), \ +G(NODE_BEGIN), G(NODE_BLOCK), G(NODE_END) CCM_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/parser.c b/lib/parser.c index 833a833..96188bf 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -147,12 +147,62 @@ node_t* parser_try_new_expr(parser_t* self) return CCM_TRY(parser_try_new_assert); } + if (lexer_peek_kind(self->lexer, NODE_BEGIN, 0)) + { + return CCM_TRY(parser_try_new_begin); + } + node_t* assign = CCM_TRY(parser_try_new_assign); if (assign) { return assign; } return CCM_TRY(parser_try_new_or); } +node_t* parser_try_new_begin(parser_t* self) +{ + assert(self); + + if (!lexer_peek_kind(self->lexer, NODE_BEGIN, 0)) + { + return NULL; + } + + lexer_consume_next(self->lexer, NODE_BEGIN); + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_BEGIN, "", self->lexer->line); + + node_t* block = CCM_TRY(parser_try_new_block); + node_push_new_child(node, block); + + if (!block || !lexer_peek_kind(self->lexer, NODE_END, 0)) + { + node_free(node); free(node); + return NULL; + } + + lexer_consume_next(self->lexer, NODE_END); + + return node; +} + +node_t* parser_try_new_block(parser_t* self) +{ + assert(self); + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_BLOCK, "", self->lexer->line); + + while (1) + { + node_t* expr = CCM_TRY(parser_try_new_expr); + if (!expr) { break; } + + node_push_new_child(node, expr); + } + + return node; +} + node_t* parser_try_new_assign(parser_t* self) { assert(self); diff --git a/lib/parser.h b/lib/parser.h index ea13765..c5b0984 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -25,6 +25,8 @@ int parser_ensure(parser_t* self, node_t* node, NodeKind kind); node_t* parser_try_new_module(parser_t* self); node_t* parser_try_new_expr(parser_t* self); +node_t* parser_try_new_begin(parser_t* self); +node_t* parser_try_new_block(parser_t* self); node_t* parser_try_new_assign(parser_t* self); node_t* parser_try_new_decl(parser_t* self); node_t* parser_try_new_assert(parser_t* self); diff --git a/lib/sym.c b/lib/sym.c index 3241ee0..32323e8 100644 --- a/lib/sym.c +++ b/lib/sym.c @@ -15,6 +15,7 @@ void sym_free(sym_t* self) sym_free_env(self, self->env); free(self->env); } + } void sym_free_env(sym_t* self, env_t* env) @@ -58,8 +59,17 @@ void sym_close_scope(sym_t* self) assert(self); assert(self->env); + for (size_t i=0; ienv->entries.size; i++) + { + sym_entry_t* entry = self->env->entries.data[i]; + free(entry->name); + free(entry); + } + vec_free(&self->env->entries); - self->env = self->env->parent; + env_t* parent = self->env->parent; + free(self->env); + self->env = parent; } @@ -72,7 +82,7 @@ int sym_declare(sym_t* self, assert(self->env); assert(name); - if (sym_try_get_value(self, name) != NULL) + if (sym_try_get_env_value(self,self->env, name) != NULL) { return 0; } diff --git a/tests/block.ccm b/tests/block.ccm new file mode 100644 index 0000000..c9ec1e0 --- /dev/null +++ b/tests/block.ccm @@ -0,0 +1,36 @@ +var a = 12 + +begin + a = 32 +end + +assert_eq (32, a) + +# SHADOWING +# ========= + +var b = 34 + +begin + var b = "bim" + assert_eq ("bim", b) +end + +assert_eq(34, b) + +# NESTED BLOCKS +# ============= +var c = "hello" + +begin + var c = "world" + begin + var c = "pizza" + c = "cat" + assert_eq ("cat", c) + end + assert_eq ("world", c) +end + +assert_eq ("hello", c) +