diff --git a/doc/roza.bnf b/doc/roza.bnf index 95bd957..305d468 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -2,9 +2,13 @@ MOD ::= EXPR* EXPR ::= | ASSERT | VARDECL +| VARSET +| BLOCK | OR ASSERT ::= assert EXPR VARDECL ::= let ident assign EXPR +VARSET ::= ident assign EXPR +BLOCK ::= begin EXPR* end OR ::= AND (or AND)* AND ::= EQNE (and EQNE)* EQNE ::= diff --git a/lib/commons.h b/lib/commons.h index 5c07e8d..960377f 100644 --- a/lib/commons.h +++ b/lib/commons.h @@ -11,7 +11,7 @@ #include #include "str.h" -#define RZ_STR_LIMIT 256 +#define RZ_STR_LIMIT 1024 #define RZ_STACK_LIMIT 1024 #define RZ_MAX_TYPES 256 diff --git a/lib/compiler.c b/lib/compiler.c index 2319fc8..0203a05 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -18,6 +18,8 @@ void compiler_init(compiler_t* compiler, compiler->sym = sym; compiler->tysy = tysy; compiler->err = err; + + compiler->scope = 0; } void compiler_free(compiler_t* compiler) @@ -232,21 +234,69 @@ int compiler_run(compiler_t* compiler, node_t* node) case NODE_VARDECL: { char* name = ((node_t*) node->children.data[0])->value.data; - sym_entry_t* entry = sym_try_find_by_name(compiler->sym, name); + node_t* block = node_try_find_parent(node, NODE_BLOCK); + + sym_entry_t* entry = NULL; + + entry = sym_try_find_by_name(compiler->sym, name, + compiler->scope, + SYM_PRE, + block); + + if (!entry) + { + entry = sym_try_find_by_name(compiler->sym, name, + compiler->scope, + SYM_DECL, + block); + + } + assert(entry); + entry->state = SYM_DECL; + int id = entry->id; compiler_run(compiler, (node_t*) node->children.data[1]); mod_push_instr(compiler->mod, OP_STORE, id); + } break; - case NODE_IDENT: { - char* name = node->value.data; - sym_entry_t* entry = sym_try_find_by_name(compiler->sym, name); + case NODE_VARSET: { + char* name = ((node_t*) node->children.data[0])->value.data; + node_t* block = node_try_find_parent(node, NODE_BLOCK); + + sym_entry_t* entry = sym_try_find_by_name(compiler->sym, name, + compiler->scope, + SYM_DECL, + block); if (!entry) { char msg[RZ_STR_LIMIT]; - snprintf(msg, RZ_STR_LIMIT, "%s is undefined.", + snprintf(msg, RZ_STR_LIMIT, + "cannot assign value" + " to undefined variable '%s'.", name); + err_fatal(compiler->err, msg, node->line); + } + else + { + int id = entry->id; + compiler_run(compiler, (node_t*) node->children.data[1]); + mod_push_instr(compiler->mod, OP_STORE, id); + } + } break; + + case NODE_IDENT: { + char* name = node->value.data; + node_t* block = node_try_find_parent(node, NODE_BLOCK); + sym_entry_t* entry = sym_try_find_by_name(compiler->sym, name, + compiler->scope, + SYM_DECL, + block); + if (!entry) + { + char msg[RZ_STR_LIMIT]; + snprintf(msg, RZ_STR_LIMIT, "undefined variable '%s'.", name); err_fatal(compiler->err, msg, node->line); @@ -259,6 +309,16 @@ int compiler_run(compiler_t* compiler, node_t* node) } } break; + case NODE_BLOCK: { + compiler->scope++; + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, (node_t*) node->children.data[i]); + } + + compiler->scope--; + } break; + default: { fprintf(stderr, "Cannot compile unknown node '%s'", NodeTypeStr[node->type]); diff --git a/lib/compiler.h b/lib/compiler.h index c712af2..e995979 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -13,6 +13,8 @@ typedef struct { sym_t* sym; tysy_t* tysy; err_t* err; + + int scope; } compiler_t; void compiler_init(compiler_t* compiler, diff --git a/lib/lexer.c b/lib/lexer.c index 2f66738..a88716a 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -71,6 +71,8 @@ node_t* lexer_try_new_next(lexer_t* lexer) // Keywords // ======== + RZ_KEYWORD("begin", NODE_BEGIN, 0); + RZ_KEYWORD("end", NODE_END, 0); RZ_KEYWORD("let", NODE_LET, 0); RZ_KEYWORD("and", NODE_AND, 0); RZ_KEYWORD("or", NODE_OR, 0); diff --git a/lib/node.c b/lib/node.c index 66aed2b..d72837c 100644 --- a/lib/node.c +++ b/lib/node.c @@ -12,6 +12,7 @@ void node_init(node_t* node, NodeType type, char* value, int line) str_append(&node->value, value); node->line = line; + node->parent = NULL; node->children.size = 0; node->children.cap = 0; @@ -34,6 +35,20 @@ void node_free(node_t* node) node->children.cap = 0; } +node_t* node_try_find_parent(node_t* node, NodeType type) +{ + assert(node); + + node_t* itr = node; + + while (itr && itr->type != type) + { + itr = (node_t*) itr->parent; + } + + return itr; +} + void node_add_new_child(node_t* node, node_t* child) { assert(node); @@ -54,6 +69,8 @@ void node_add_new_child(node_t* node, node_t* child) size_t sz = node->children.size; node->children.data[sz] = (struct node_t*) child; + ((node_t*) node->children.data[sz])->parent = (struct node_t*) node; + node->children.size++; } diff --git a/lib/node.h b/lib/node.h index ac7f646..7bee8e2 100644 --- a/lib/node.h +++ b/lib/node.h @@ -3,16 +3,17 @@ #include "commons.h" -#define NODE_TYPE(G) \ - G(NODE_MOD), \ - G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \ - G(NODE_ASSERT), \ - G(NODE_EQ), G(NODE_NE), \ - G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ - G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \ - 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) +#define NODE_TYPE(G) \ + G(NODE_MOD), \ + G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \ + G(NODE_ASSERT), \ + G(NODE_EQ), G(NODE_NE), \ + G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ + G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \ + 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) RZ_ENUM_H(NodeType, NODE_TYPE); @@ -20,6 +21,7 @@ typedef struct { NodeType type; str_t value; int line; + struct node_t* parent; struct { struct node_t** data; @@ -31,6 +33,8 @@ typedef struct { void node_init(node_t* node, NodeType type, char* value, int line); void node_free(node_t* node); +node_t* node_try_find_parent(node_t* node, NodeType type); + void node_add_new_child(node_t* node, node_t* child); node_t* node_child(node_t* node, size_t index); diff --git a/lib/parser.c b/lib/parser.c index ccc2d87..697061b 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -50,23 +50,35 @@ node_t* parser_try_new_mod(parser_t* parser) node_t* parser_try_new_expr(parser_t* parser) { assert(parser); - NodeType type = lexer_peek(parser->lexer, 1); + NodeType type_1 = lexer_peek(parser->lexer, 1); + NodeType type_2 = lexer_peek(parser->lexer, 2); - if (type == -1) + if (type_1 == -1) { return NULL; } - if (type == NODE_ASSERT) + if (type_1 == NODE_IDENT + && type_2 == NODE_ASSIGN) + { + return parser_try_new_varset(parser); + } + + if (type_1 == NODE_ASSERT) { return parser_try_new_assert(parser); } - if (type == NODE_LET) + if (type_1 == NODE_LET) { return parser_try_new_vardecl(parser); } + if (type_1 == NODE_BEGIN) + { + return parser_try_new_block(parser); + } + return parser_try_new_or(parser); } @@ -101,6 +113,48 @@ node_t* parser_try_new_vardecl(parser_t* parser) return node; } +node_t* parser_try_new_varset(parser_t* parser) +{ + assert(parser); + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_VARSET, "", parser->lexer->line); + + node_t* ident = parser_try_new_consume(parser, NODE_IDENT); + assert(ident); + + parser_skip(parser, NODE_ASSIGN); + + node_t* expr = parser_try_new_expr(parser); + assert(expr); + + node_add_new_child(node, ident); + node_add_new_child(node, expr); + + return node; +} + +node_t* parser_try_new_block(parser_t* parser) +{ + assert(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) + { + node_add_new_child(block, parser_try_new_expr(parser)); + } + + parser_skip(parser, NODE_END); + + return block; +} + node_t* parser_try_new_or(parser_t* parser) { assert(parser); diff --git a/lib/parser.h b/lib/parser.h index 08862c5..d9af55d 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -18,6 +18,8 @@ node_t* parser_try_new_mod(parser_t* parser); node_t* parser_try_new_expr(parser_t* parser); 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_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/prepass.c b/lib/prepass.c index d9850ef..011dd63 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -1,4 +1,5 @@ #include "prepass.h" +#include "lib/sym.h" #include "node.h" void prepass_init(prepass_t* prepass, @@ -14,6 +15,8 @@ void prepass_init(prepass_t* prepass, prepass->sym = sym; prepass->tysolver = tysolver; prepass->err = err; + + prepass->scope = 0; } void prepass_free(prepass_t* prepass) @@ -28,13 +31,31 @@ void prepass_run(prepass_t* prepass, node_t* node) switch (node->type) { + case NODE_BLOCK: { + prepass->scope++; + + for (size_t i=0; ichildren.size; i++) + { + node_t* child = (node_t*) node->children.data[i]; + prepass_run(prepass, child); + } + + prepass->scope--; + } break; + case NODE_VARDECL: { char* name = ((node_t*) node->children.data[0])->value.data; + node_t* block = node_try_find_parent(node, NODE_BLOCK); + prepass_run(prepass, (node_t*) node->children.data[1]); type_t* type = tysolver_try_solve_node(prepass->tysolver, (node_t*) node->children.data[1]); - sym_declare(prepass->sym, name, type); + + sym_declare(prepass->sym, name, type, + prepass->scope, SYM_PRE, + block); + } break; default: { diff --git a/lib/prepass.h b/lib/prepass.h index d3d6b72..c4e588a 100644 --- a/lib/prepass.h +++ b/lib/prepass.h @@ -11,6 +11,9 @@ typedef struct { sym_t* sym; tysolver_t* tysolver; err_t* err; + + int scope; + } prepass_t; void prepass_init(prepass_t* prepass, diff --git a/lib/sym.c b/lib/sym.c index 7103092..0450eca 100644 --- a/lib/sym.c +++ b/lib/sym.c @@ -29,12 +29,23 @@ void sym_free(sym_t* sym) sym->entries.cap = 0; } -int sym_declare(sym_t* sym, char* name, type_t* type) +int sym_declare(sym_t* sym, char* name, type_t* type, + int scope, int state_flag, node_t* block) { assert(sym); assert(name); assert(type); + sym_entry_t* res = sym_try_find_by_name(sym, + name, + scope, + state_flag, + block); + if (res && res->block == block) + { + return res->id; + } + if (sym->entries.data == NULL) { sym->entries.cap = 2; @@ -57,22 +68,47 @@ int sym_declare(sym_t* sym, char* name, type_t* type) sym->entries.data[sym->entries.size]->name = strdup(name); sym->entries.data[sym->entries.size]->id = id; sym->entries.data[sym->entries.size]->type = type; + sym->entries.data[sym->entries.size]->scope = scope; + sym->entries.data[sym->entries.size]->state = state_flag; + sym->entries.data[sym->entries.size]->block = block; sym->entries.size++; return id; } -sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name) +sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name, + int scope, int state_flag, + node_t* block) { assert(sym); + sym_entry_t* res = NULL; + for (size_t i=0; ientries.size; i++) { - if (strcmp(sym->entries.data[i]->name, name) == 0) + sym_entry_t* entry = sym->entries.data[i]; + + node_t* itr = block; + + while (itr && itr != entry->block) { - return sym->entries.data[i]; + itr = (node_t*) itr->parent; } + + if (strcmp(entry->name, name) == 0 + && entry->scope == scope + && (entry->state & state_flag) != 0 + && (entry->block == itr)) + { + res = entry; + return res; + } + } + + if (scope > 0) + { + return sym_try_find_by_name(sym, name, scope - 1, state_flag, block); } return NULL; @@ -87,8 +123,8 @@ size_t sym_str(sym_t* sym, char* buffer, size_t size) for (size_t i=0; ientries.size; i++) { sym_entry_t* entry = sym->entries.data[i]; - sz += snprintf(buffer + sz, size - sz, "(%d %s ", - entry->id, entry->name); + sz += snprintf(buffer + sz, size - sz, "(%d| %d %s ", + entry->id, entry->scope, entry->name); sz += type_str(entry->type, buffer + sz, size - sz); diff --git a/lib/sym.h b/lib/sym.h index 96a6a44..a1ee350 100644 --- a/lib/sym.h +++ b/lib/sym.h @@ -4,11 +4,20 @@ #include "commons.h" #include "tysy.h" #include "err.h" +#include "node.h" + +typedef enum { + SYM_PRE = 1<<1, + SYM_DECL = 1<<2, +} sym_state_t; typedef struct { int id; char* name; type_t* type; + int scope; + sym_state_t state; + node_t* block; } sym_entry_t; typedef struct { @@ -26,8 +35,13 @@ typedef struct { void sym_init(sym_t* sym, tysy_t* tysy, err_t* err); void sym_free(sym_t* sym); -int sym_declare(sym_t* sym, char* name, type_t* type); -sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name); +int sym_declare(sym_t* sym, char* name, type_t* type, + int scope, int state_flag, + node_t* block); + +sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name, + int scope, int state_flag, + node_t* block); size_t sym_str(sym_t* sym, char* buffer, size_t size); diff --git a/lib/tysolver.c b/lib/tysolver.c index c9794b1..105e206 100644 --- a/lib/tysolver.c +++ b/lib/tysolver.c @@ -9,6 +9,7 @@ void tysolver_init(tysolver_t* tysolver, sym_t* sym, tysy_t* tysy) tysolver->sym = sym; tysolver->tysy = tysy; + tysolver->scope = 0; } void tysolver_free(tysolver_t* tysolver) @@ -28,6 +29,19 @@ type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node) switch (node->type) { + case NODE_BLOCK: { + tysolver->scope++; + + if (node->children.size > 0) + { + size_t last = node->children.size - 1; + + return tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[last]); + } + tysolver->scope--; + } break; + case NODE_VARDECL: { return tysolver_try_solve_node(tysolver, (node_t*) node->children.data[1]); @@ -35,7 +49,12 @@ type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node) case NODE_IDENT: { char* name = node->value.data; - sym_entry_t* entry = sym_try_find_by_name(tysolver->sym, name); + node_t* block = node_try_find_parent(node, NODE_BLOCK); + sym_entry_t* entry = sym_try_find_by_name(tysolver->sym, + name, + tysolver->scope, + SYM_PRE | SYM_DECL, + block); assert(entry); return entry->type; diff --git a/lib/tysolver.h b/lib/tysolver.h index 1613f20..0b098db 100644 --- a/lib/tysolver.h +++ b/lib/tysolver.h @@ -9,6 +9,8 @@ typedef struct { sym_t* sym; tysy_t* tysy; + + int scope; } tysolver_t; void tysolver_init(tysolver_t* tysolver, sym_t* sym, tysy_t* tysy); diff --git a/tests/acceptances/var.roza b/tests/acceptances/var.roza index f7b3d5d..ecdaa38 100644 --- a/tests/acceptances/var.roza +++ b/tests/acceptances/var.roza @@ -1,8 +1,47 @@ # VARIABLE DECLARATIONS # ===================== # simple -let x = 35 -assert x == 35 +let a = 35 +assert a == 35 -let y = x + 2 -assert y == 37 +let b = a + 2 +assert b == 37 + +# assignment +let c = 3 +assert c == 3 + +c = 5 +assert c == 5 + +c = "boom" +assert c == "boom" + +# blocks +let d = 1 + +begin + assert d == 1 + + let d = 2 + assert d == 2 + + begin + assert d == 2 + + let d = 3 + assert d == 3 + + d = -1 + assert d == -1 + end + + assert d == 2 +end + +assert d == 1 + +# multiple declaration +let e = 3 +let e = 7 +assert e == 7 \ No newline at end of file diff --git a/tests/units/lexer.c b/tests/units/lexer.c index e433a95..58437ab 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -156,3 +156,9 @@ Test(lexer, let_var) { "ASSIGN", "NUM[3]"); } + +Test(lexer, blocks) { + test_lexer("begin end", 2, + "BEGIN", + "END"); +} diff --git a/tests/units/parser.c b/tests/units/parser.c index 7f47bb5..be1c89e 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -109,3 +109,20 @@ Test(parser, var_decl) { test_parser_ok("MOD(VARDECL(IDENT[bim],MUL(NUM[3],NUM[6])))", " let bim = 3 * 6 "); } + +Test(parser, var_set) { + test_parser_ok("MOD(VARSET(IDENT[y],BOOL[true]))", + " y = true "); +} + +Test(parser, block) { + test_parser_ok("MOD(BLOCK)", + " begin end "); + + test_parser_ok("MOD(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])))", + " begin y = true z = false end "); +}