From bb83d39bb9f0712d1fe846cb10e057b2a49e591b Mon Sep 17 00:00:00 2001 From: bog Date: Sun, 17 Dec 2023 19:10:17 +0100 Subject: [PATCH] :sparkles: simple variable declarations. --- doc/roza.bnf | 4 +- lib/compiler.c | 43 ++++++- lib/compiler.h | 11 +- lib/err.c | 28 ++++- lib/err.h | 2 + lib/lexer.c | 63 ++++++++++ lib/lexer.h | 1 + lib/loader.c | 202 ++++++++++++++++++------------ lib/node.h | 3 +- lib/opcodes.h | 5 +- lib/parser.c | 74 ++++++++++- lib/parser.h | 2 + lib/prepass.c | 45 +++---- lib/prepass.h | 10 +- lib/sym.c | 99 +++++++++++++++ lib/sym.h | 34 +++++ lib/type.c | 9 ++ lib/type.h | 1 + lib/tysolver.c | 118 +++++++++++++++++ lib/tysolver.h | 20 +++ lib/vm.c | 250 ++++++++++++++++++++++++++++--------- lib/vm.h | 31 ++++- meson.build | 2 + tests/acceptances/var.roza | 8 ++ tests/units/lexer.c | 19 +-- tests/units/parser.c | 12 +- 26 files changed, 901 insertions(+), 195 deletions(-) create mode 100644 lib/sym.c create mode 100644 lib/sym.h create mode 100644 lib/tysolver.c create mode 100644 lib/tysolver.h create mode 100644 tests/acceptances/var.roza diff --git a/doc/roza.bnf b/doc/roza.bnf index 5212b8e..95bd957 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -1,8 +1,10 @@ MOD ::= EXPR* EXPR ::= | ASSERT +| VARDECL | OR ASSERT ::= assert EXPR +VARDECL ::= let ident assign EXPR OR ::= AND (or AND)* AND ::= EQNE (and EQNE)* EQNE ::= @@ -15,4 +17,4 @@ FACTOR ::= POWER ((mul | div | mod) POWER)* POWER ::= UNARY (pow UNARY)? UNARY ::= sub? GROUP | not? GROUP GROUP ::= BUILTIN | opar EXPR cpar -BUILTIN ::= num | bool | str +BUILTIN ::= num | bool | str | ident diff --git a/lib/compiler.c b/lib/compiler.c index 95099ba..2319fc8 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -1,13 +1,21 @@ #include "compiler.h" #include "lib/commons.h" #include "lib/mod.h" +#include "node.h" -void compiler_init(compiler_t* compiler, mod_t* mod, tysy_t* tysy, err_t* err) +void compiler_init(compiler_t* compiler, + mod_t* mod, + sym_t* sym, + tysy_t* tysy, + err_t* err) { assert(compiler); + assert(sym); + assert(tysy); assert(err); compiler->mod = mod; + compiler->sym = sym; compiler->tysy = tysy; compiler->err = err; } @@ -17,7 +25,7 @@ void compiler_free(compiler_t* compiler) assert(compiler); } -void compiler_run(compiler_t* compiler, node_t* node) +int compiler_run(compiler_t* compiler, node_t* node) { assert(compiler); assert(node); @@ -222,10 +230,41 @@ void compiler_run(compiler_t* compiler, node_t* node) mod_push_instr(compiler->mod, OP_NOT, RZ_NO_PARAM); } break; + 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); + assert(entry); + 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); + + if (!entry) + { + char msg[RZ_STR_LIMIT]; + snprintf(msg, RZ_STR_LIMIT, "%s is undefined.", + name); + + err_fatal(compiler->err, msg, node->line); + return -1; + } + else + { + int id = entry->id; + mod_push_instr(compiler->mod, OP_LOAD, id); + } + } break; + default: { fprintf(stderr, "Cannot compile unknown node '%s'", NodeTypeStr[node->type]); abort(); } break; } + + return 0; } diff --git a/lib/compiler.h b/lib/compiler.h index 5cf2652..c712af2 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -6,16 +6,23 @@ #include "mod.h" #include "node.h" #include "tysy.h" +#include "sym.h" typedef struct { mod_t* mod; + sym_t* sym; tysy_t* tysy; err_t* err; } compiler_t; -void compiler_init(compiler_t* compiler, mod_t* mod, tysy_t* tysy, err_t* err); +void compiler_init(compiler_t* compiler, + mod_t* mod, + sym_t* sym, + tysy_t* tysy, + err_t* err); + void compiler_free(compiler_t* compiler); -void compiler_run(compiler_t* compiler, node_t* node); +int compiler_run(compiler_t* compiler, node_t* node); #endif diff --git a/lib/err.c b/lib/err.c index 3a70312..a68ae8c 100644 --- a/lib/err.c +++ b/lib/err.c @@ -6,6 +6,8 @@ void err_init(err_t* err) { assert(err); err->size = 0; + err->total = 0; + err->quiet = 0; } void err_free(err_t* err) @@ -31,6 +33,8 @@ void err_of_type(err_t* err, char* what, int line, ErrType type) err->errors[err->size]->line = line; err->errors[err->size]->type = type; err->size++; + + err->total++; } void err_fatal(err_t* err, char* what, int line) @@ -47,23 +51,35 @@ int err_dump(err_t* err) { if (err->errors[i]->type == ERR_WARNING) { - fprintf(stderr, "\33[33mWARNING\33[0m[:%d] %s\n", - err->errors[i]->line, - err->errors[i]->what); + if (!err->quiet) + { + fprintf(stderr, "\33[33mWARNING\33[0m[:%d] %s\n", + err->errors[i]->line, + err->errors[i]->what); + } } else if (err->errors[i]->type == ERR_FATAL) { - fprintf(stderr, "\33[31mERROR\33[0m[:%d] %s\n", - err->errors[i]->line, - err->errors[i]->what); + if (!err->quiet) + { + fprintf(stderr, "\33[31mERROR\33[0m[:%d] %s\n", + err->errors[i]->line, + err->errors[i]->what); + } is_fatal = 1; } } + int total = err->total; + int quiet = err->quiet; + err_free(err); err_init(err); + err->total = total; + err->quiet = quiet; + return is_fatal; } diff --git a/lib/err.h b/lib/err.h index d8a2074..7ee92ec 100644 --- a/lib/err.h +++ b/lib/err.h @@ -17,6 +17,8 @@ typedef struct { } err_msg_t; typedef struct { + int total; + int quiet; size_t size; err_msg_t* errors[RZ_ERROR_STACK_SIZE]; } err_t; diff --git a/lib/lexer.c b/lib/lexer.c index 65799f9..2f66738 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -67,9 +67,11 @@ node_t* lexer_try_new_next(lexer_t* lexer) RZ_TEXT(">=", NODE_GE, 0); RZ_TEXT("<", NODE_LT, 0); RZ_TEXT(">", NODE_GT, 0); + RZ_TEXT("=", NODE_ASSIGN, 0); // Keywords // ======== + RZ_KEYWORD("let", NODE_LET, 0); RZ_KEYWORD("and", NODE_AND, 0); RZ_KEYWORD("or", NODE_OR, 0); RZ_KEYWORD("not", NODE_NOT, 0); @@ -88,6 +90,17 @@ node_t* lexer_try_new_next(lexer_t* lexer) } } + // scan str + { + node_t* node = lexer_try_new_ident(lexer); + + if (node) + { + lexer_skip_spaces(lexer); // usefull ??? + return node; + } + } + // scan num { size_t cursor = lexer->cursor; @@ -147,6 +160,7 @@ node_t* lexer_try_new_next(lexer_t* lexer) char msg[SZ]; snprintf(msg, SZ, "unexpected symbol '%c'", lexer->source[lexer->cursor]); err_fatal(lexer->err, msg, lexer->line); + err_dump(lexer->err); } return NULL; @@ -327,6 +341,55 @@ node_t* lexer_try_new_str(lexer_t* lexer) return tok; } +node_t* lexer_try_new_ident(lexer_t* lexer) +{ + assert(lexer); + + ssize_t cursor = lexer->cursor; + ssize_t len = strlen(lexer->source); + + str_t res_str; + str_init(&res_str); + int first = 1; + + while (cursor < len) + { + char c = lexer->source[cursor]; + int is_first = isalpha(c) || + c == '_' || + c == '?' || + c == '!'; + + int is_rest = is_first || isdigit(c); + + if ((first && is_first) || (!first && is_rest)) + { + str_push(&res_str, c); + first = 0; + } + else + { + break; + } + + cursor++; + } + + if (res_str.size == 0) + { + str_free(&res_str); + return NULL; + } + + node_t* tok = malloc(sizeof(node_t)); + node_init(tok, NODE_IDENT, res_str.data, lexer->line); + str_free(&res_str); + + lexer->cursor = cursor; + + return tok; +} + int lexer_is_sep(lexer_t* lexer, size_t idx) { assert(lexer); diff --git a/lib/lexer.h b/lib/lexer.h index 1ece250..9304a20 100644 --- a/lib/lexer.h +++ b/lib/lexer.h @@ -25,6 +25,7 @@ node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw, int is_kw); node_t* lexer_try_new_str(lexer_t* lexer); +node_t* lexer_try_new_ident(lexer_t* lexer); int lexer_is_sep(lexer_t* lexer, size_t idx); diff --git a/lib/loader.c b/lib/loader.c index 05741f2..9cffd61 100644 --- a/lib/loader.c +++ b/lib/loader.c @@ -6,6 +6,8 @@ #include "prepass.h" #include "mod.h" #include "vm.h" +#include "sym.h" +#include "tysolver.h" void loader_init(loader_t* loader) { @@ -59,98 +61,138 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) lexer_t lex; lexer_init(&lex, source.data, &err); + if (err_dump(&err)) + { + lexer_free(&lex); + err_free(&err); + tysy_free(&tysy); + abort(); + } + parser_t parser; parser_init(&parser, &lex, &err); node_t* node = parser_try_new_tree(&parser); - assert(node); + + if (!node) + { + str_free(&source); + err_dump(&err); + parser_free(&parser); + lexer_free(&lex); + err_free(&err); + tysy_free(&tysy); + + return -1; + } if (debug) - { - char msg[RZ_STR_LIMIT]; - node_str(node, msg, RZ_STR_LIMIT); + { + char msg[RZ_STR_LIMIT]; + node_str(node, msg, RZ_STR_LIMIT); - printf("\n======== AST ========\n"); - printf("%s\n", msg); + printf("\n======== AST ========\n"); + printf("%s\n", msg); + } + + sym_t sym; + sym_init(&sym, &tysy, &err); + + mod_t mod; + mod_init(&mod); + + + tysolver_t tysolver; + tysolver_init(&tysolver, &sym, &tysy); + + // prepass + { + prepass_t prepass; + prepass_init(&prepass, &sym, &tysolver, &err); + prepass_run(&prepass, node); + prepass_free(&prepass); } - if (node) + // compile + { + compiler_t compiler; + compiler_init(&compiler, &mod, &sym, &tysy, &err); + int status = compiler_run(&compiler, node); + + if (status != 0) + { + return -1; + } + + compiler_free(&compiler); + } + + tysolver_free(&tysolver); + + node_free(node); + free(node); + + // check compilation errors + if (err_dump(&err)) { - mod_t mod; - mod_init(&mod); - - // prepass - { - prepass_t prepass; - prepass_init(&prepass, &err); - prepass_run(&prepass, node); - prepass_free(&prepass); - } - - // compile - { - compiler_t compiler; - compiler_init(&compiler, &mod, &tysy, &err); - compiler_run(&compiler, node); - compiler_free(&compiler); - } - - node_free(node); - free(node); - - // check compilation errors - if (err_dump(&err)) - { - parser_free(&parser); - lexer_free(&lex); - str_free(&source); - tysy_free(&tysy); - err_free(&err); - - abort(); - } - - if (debug) - { - char msg[RZ_STR_LIMIT]; - mod_str(&mod, msg, RZ_STR_LIMIT); - printf("%s", msg); - } - - // execute - { - vm_t vm; - vm_init(&vm, &tysy, &err); - - int status = vm_exec_mod(&vm, &mod); - - if (status != 0) - { - vm_free(&vm); - mod_free(&mod); - parser_free(&parser); - lexer_free(&lex); - str_free(&source); - tysy_free(&tysy); - err_free(&err); - - return status; - } - - if (debug) - { - char msg[RZ_STR_LIMIT]; - vm_stack_str(&vm, msg, RZ_STR_LIMIT); - printf("\n======== STACK ========\n"); - printf("%s\n", msg); - } - - vm_free(&vm); - } - + parser_free(&parser); + lexer_free(&lex); + str_free(&source); + err_free(&err); mod_free(&mod); + sym_free(&sym); + + tysy_free(&tysy); + return -1; } + // execute + { + vm_t vm; + vm_init(&vm, &sym, &tysy, &err); + + int status = vm_exec_mod(&vm, &mod); + + if (status != 0) + { + vm_free(&vm); + mod_free(&mod); + sym_free(&sym); + parser_free(&parser); + lexer_free(&lex); + str_free(&source); + tysy_free(&tysy); + err_free(&err); + + return status; + } + + if (debug) + { + char msg[RZ_STR_LIMIT]; + vm_stack_str(&vm, msg, RZ_STR_LIMIT); + printf("\n======== STACK ========\n"); + printf("%s\n", msg); + } + + vm_free(&vm); + } + + if (debug) + { + char msg[RZ_STR_LIMIT]; + mod_str(&mod, msg, RZ_STR_LIMIT); + printf("%s", msg); + + char msg2[RZ_STR_LIMIT]; + sym_str(&sym, msg2, RZ_STR_LIMIT); + printf("\n======== SYM TABLE ========\n"); + printf("%s", msg2); + } + + mod_free(&mod); + sym_free(&sym); + // free parser_free(&parser); lexer_free(&lex); @@ -161,7 +203,7 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) if (err_dump(&err)) { err_free(&err); - abort(); + return -1; } err_free(&err); diff --git a/lib/node.h b/lib/node.h index f045312..ac7f646 100644 --- a/lib/node.h +++ b/lib/node.h @@ -11,7 +11,8 @@ 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_AND), G(NODE_OR), G(NODE_NOT), \ + G(NODE_LET), G(NODE_IDENT), G(NODE_ASSIGN), G(NODE_VARDECL) RZ_ENUM_H(NodeType, NODE_TYPE); diff --git a/lib/opcodes.h b/lib/opcodes.h index 62c0c99..30626ba 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -10,9 +10,10 @@ G(OP_LT), G(OP_LE), G(OP_GT), G(OP_GE), \ G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), \ G(OP_MODULO), G(OP_POW), G(OP_USUB), \ - G(OP_AND), G(OP_OR), G(OP_NOT), \ + G(OP_NOT), \ G(OP_BRF), G(OP_BRT), G(OP_BR), \ - G(OP_STRCAT), G(OP_STRDUP) + G(OP_STRCAT), G(OP_STRDUP), \ + G(OP_LOAD), G(OP_STORE) RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/parser.c b/lib/parser.c index 62bfce2..ccc2d87 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -1,4 +1,5 @@ #include "parser.h" +#include "lib/commons.h" #include "lib/lexer.h" #include "lib/node.h" @@ -61,6 +62,11 @@ node_t* parser_try_new_expr(parser_t* parser) return parser_try_new_assert(parser); } + if (type == NODE_LET) + { + return parser_try_new_vardecl(parser); + } + return parser_try_new_or(parser); } @@ -77,6 +83,24 @@ node_t* parser_try_new_assert(parser_t* parser) return node; } +node_t* parser_try_new_vardecl(parser_t* parser) +{ + assert(parser); + + parser_skip(parser, NODE_LET); + node_t* ident = parser_try_new_consume(parser, NODE_IDENT); + parser_skip(parser, NODE_ASSIGN); + node_t* expr = parser_try_new_expr(parser); + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_VARDECL, "", parser->lexer->line); + + node_add_new_child(node, ident); + node_add_new_child(node, expr); + + return node; +} + node_t* parser_try_new_or(parser_t* parser) { assert(parser); @@ -303,11 +327,30 @@ node_t* parser_try_new_builtin(parser_t* parser) assert(parser); NodeType next = lexer_peek(parser->lexer, 1); - if (next == NODE_NUM || next == NODE_BOOL || next == NODE_STR) + if (next == NODE_NUM + || next == NODE_BOOL + || next == NODE_STR + || next == NODE_IDENT) { return parser_try_new_consume(parser, next); } + if (next) + { + node_t* node = lexer_try_new_next(parser->lexer); + char nstr[RZ_STR_LIMIT]; + node_str(node, nstr, RZ_STR_LIMIT); + node_free(node); + free(node); + + size_t limit = RZ_STR_LIMIT + strlen(nstr); + char msg[limit]; + snprintf(msg, limit, "unexpected node '%s'.", nstr); + + err_fatal(parser->err, msg, parser->lexer->line); + err_dump(parser->err); + } + return NULL; } @@ -329,8 +372,37 @@ node_t* parser_try_new_consume(parser_t* parser, NodeType type) err_fatal(parser->err, err_msg, next->line); node_free(next); free(next); + + err_dump(parser->err); return NULL; } return next; } + +int parser_skip(parser_t* parser, NodeType type) +{ + assert(parser); + + node_t* next = lexer_try_new_next(parser->lexer); + assert(next); + + if (next->type != type) + { + size_t const SZ = RZ_STR_LIMIT; + char err_msg[SZ]; + snprintf(err_msg, SZ, "unexpected node '%s'", NodeTypeStr[next->type]); + err_fatal(parser->err, err_msg, next->line); + node_free(next); + free(next); + err_dump(parser->err); + return 0; + } + else + { + node_free(next); + free(next); + + return 1; + } +} diff --git a/lib/parser.h b/lib/parser.h index 407b577..08862c5 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -17,6 +17,7 @@ node_t* parser_try_new_tree(parser_t* parser); 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_or(parser_t* parser); node_t* parser_try_new_and(parser_t* parser); node_t* parser_try_new_eqne(parser_t* parser); @@ -29,5 +30,6 @@ node_t* parser_try_new_group(parser_t* parser); node_t* parser_try_new_builtin(parser_t* parser); node_t* parser_try_new_consume(parser_t* parser, NodeType type); +int parser_skip(parser_t* parser, NodeType type); #endif diff --git a/lib/prepass.c b/lib/prepass.c index bcbb9a2..d9850ef 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -1,10 +1,18 @@ #include "prepass.h" +#include "node.h" -void prepass_init(prepass_t* prepass, err_t* err) +void prepass_init(prepass_t* prepass, + sym_t* sym, + tysolver_t* tysolver, + err_t* err) { assert(prepass); + assert(sym); + assert(tysolver); assert(err); + prepass->sym = sym; + prepass->tysolver = tysolver; prepass->err = err; } @@ -20,39 +28,20 @@ void prepass_run(prepass_t* prepass, node_t* node) switch (node->type) { - case NODE_MOD: - case NODE_NUM: - case NODE_STR: - case NODE_BOOL: - case NODE_ASSERT: - case NODE_EQ: - case NODE_NE: - case NODE_LT: - case NODE_LE: - case NODE_GT: - case NODE_GE: + case NODE_VARDECL: { + char* name = ((node_t*) node->children.data[0])->value.data; + prepass_run(prepass, (node_t*) node->children.data[1]); - case NODE_ADD: - case NODE_SUB: - case NODE_MUL: - case NODE_DIV: - case NODE_MODULO: - case NODE_POW: + type_t* type = tysolver_try_solve_node(prepass->tysolver, + (node_t*) node->children.data[1]); + sym_declare(prepass->sym, name, type); + } break; - case NODE_OR: - case NODE_AND: - case NODE_NOT: - { + default: { for (size_t i=0; ichildren.size; i++) { prepass_run(prepass, (node_t*) node->children.data[i]); } } break; - - default: { - fprintf(stderr, "Cannot prepass unknown node '%s'.\n", - NodeTypeStr[node->type]); - abort(); - } break; } } diff --git a/lib/prepass.h b/lib/prepass.h index 5590c3a..d3d6b72 100644 --- a/lib/prepass.h +++ b/lib/prepass.h @@ -4,12 +4,20 @@ #include "commons.h" #include "err.h" #include "node.h" +#include "sym.h" +#include "tysolver.h" typedef struct { + sym_t* sym; + tysolver_t* tysolver; err_t* err; } prepass_t; -void prepass_init(prepass_t* prepass, err_t* err); +void prepass_init(prepass_t* prepass, + sym_t* sym, + tysolver_t* tysolver, + err_t* err); + void prepass_free(prepass_t* prepass); void prepass_run(prepass_t* prepass, node_t* node); diff --git a/lib/sym.c b/lib/sym.c new file mode 100644 index 0000000..7103092 --- /dev/null +++ b/lib/sym.c @@ -0,0 +1,99 @@ +#include "sym.h" + +void sym_init(sym_t* sym, tysy_t* tysy, err_t* err) +{ + assert(sym); + + sym->tysy = tysy; + sym->err = err; + + sym->entries.id_counter = 0; + sym->entries.size = 0; + sym->entries.cap = 0; + sym->entries.data = NULL; +} + +void sym_free(sym_t* sym) +{ + assert(sym); + + for (size_t i=0; ientries.size; i++) + { + sym_entry_t* entry = sym->entries.data[i]; + free(entry->name); + free(entry); + } + + free(sym->entries.data); + sym->entries.size = 0; + sym->entries.cap = 0; +} + +int sym_declare(sym_t* sym, char* name, type_t* type) +{ + assert(sym); + assert(name); + assert(type); + + if (sym->entries.data == NULL) + { + sym->entries.cap = 2; + sym->entries.data = malloc(sizeof(sym_entry_t*) + * sym->entries.cap); + } + + if (sym->entries.size >= sym->entries.cap) + { + sym->entries.cap *= 2; + sym->entries.data = realloc(sym->entries.data, + sizeof(sym_entry_t*) * sym->entries.cap); + } + + sym->entries.data[sym->entries.size] = malloc(sizeof(sym_entry_t)); + + int id = sym->entries.id_counter; + sym->entries.id_counter++; + + 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.size++; + + return id; +} + +sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name) +{ + assert(sym); + + for (size_t i=0; ientries.size; i++) + { + if (strcmp(sym->entries.data[i]->name, name) == 0) + { + return sym->entries.data[i]; + } + } + + return NULL; +} + +size_t sym_str(sym_t* sym, char* buffer, size_t size) +{ + assert(sym); + assert(buffer); + size_t sz = 0; + + 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 += type_str(entry->type, buffer + sz, size - sz); + + sz += snprintf(buffer + sz, size - sz, ")\n"); + } + + return sz; +} diff --git a/lib/sym.h b/lib/sym.h new file mode 100644 index 0000000..96a6a44 --- /dev/null +++ b/lib/sym.h @@ -0,0 +1,34 @@ +#ifndef RZ_SYM_H +#define RZ_SYM_H + +#include "commons.h" +#include "tysy.h" +#include "err.h" + +typedef struct { + int id; + char* name; + type_t* type; +} sym_entry_t; + +typedef struct { + tysy_t* tysy; + err_t* err; + + struct { + int id_counter; + size_t size; + size_t cap; + sym_entry_t** data; + } entries; +} sym_t; + +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); + +size_t sym_str(sym_t* sym, char* buffer, size_t size); + +#endif diff --git a/lib/type.c b/lib/type.c index bda4886..1d3503d 100644 --- a/lib/type.c +++ b/lib/type.c @@ -20,3 +20,12 @@ int type_eq(type_t* type, type_t* rhs) return type->kind == rhs->kind; } + +size_t type_str(type_t* type, char* buffer, size_t size) +{ + assert(type); + assert(buffer); + + return snprintf(buffer, size, "%s", + TypeKindStr[type->kind] + strlen("TYPE_")); +} diff --git a/lib/type.h b/lib/type.h index 08612af..fb6ec5c 100644 --- a/lib/type.h +++ b/lib/type.h @@ -16,5 +16,6 @@ void type_init(type_t* type, TypeKind kind); void type_free(type_t* type); int type_eq(type_t* type, type_t* rhs); +size_t type_str(type_t* type, char* buffer, size_t size); #endif diff --git a/lib/tysolver.c b/lib/tysolver.c new file mode 100644 index 0000000..c9794b1 --- /dev/null +++ b/lib/tysolver.c @@ -0,0 +1,118 @@ +#include "tysolver.h" +#include "lib/tysy.h" + +void tysolver_init(tysolver_t* tysolver, sym_t* sym, tysy_t* tysy) +{ + assert(tysolver); + assert(sym); + assert(tysy); + + tysolver->sym = sym; + tysolver->tysy = tysy; +} + +void tysolver_free(tysolver_t* tysolver) +{ + assert(tysolver); +} + +/* + #define NODE_TYPE(G) \ + G(NODE_MOD), \ + G(NODE_LET), G(NODE_IDENT), G(NODE_ASSIGN), G(NODE_VARDECL) + */ +type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node) +{ + assert(tysolver); + assert(node); + + switch (node->type) + { + case NODE_VARDECL: { + return tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[1]); + } break; + + case NODE_IDENT: { + char* name = node->value.data; + sym_entry_t* entry = sym_try_find_by_name(tysolver->sym, name); + assert(entry); + + return entry->type; + } break; + + case NODE_SUB: + case NODE_DIV: + case NODE_MODULO: + case NODE_POW: + case NODE_NUM: { + type_t* ty = tysy_try_find_type(tysolver->tysy, "num"); + assert(ty); + return ty; + } break; + + case NODE_ADD: { + return tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[0]); + } break; + + case NODE_MUL: { + type_t* lhs = tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[0]); + + type_t* rhs = tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[1]); + + if (type_eq(lhs, rhs) && lhs->kind == TYPE_NUM) + { + return tysolver_try_solve_node(tysolver, + (node_t*) node->children.data[0]); + } + else if ((lhs->kind == TYPE_NUM && rhs->kind == TYPE_STR) + || (rhs->kind == TYPE_NUM && lhs->kind == TYPE_STR)) + { + return tysy_try_find_type(tysolver->tysy, "str"); + } + else + { + tysolver_error(tysolver, node); + } + + } break; + + case NODE_ASSERT: + case NODE_EQ: + case NODE_NE: + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE: + case NODE_AND: + case NODE_OR: + case NODE_NOT: + case NODE_BOOL: { + type_t* ty = tysy_try_find_type(tysolver->tysy, "bool"); + assert(ty); + return ty; + } break; + + case NODE_STR: { + type_t* ty = tysy_try_find_type(tysolver->tysy, "str"); + assert(ty); + return ty; + } break; + + default: { + tysolver_error(tysolver, node); + } break; + } + + return NULL; +} + +void tysolver_error(tysolver_t* tysolver, node_t* node) +{ + fprintf(stderr, "Cannot solve type of '%s'.\n", + NodeTypeStr[node->type]); + abort(); +} diff --git a/lib/tysolver.h b/lib/tysolver.h new file mode 100644 index 0000000..1613f20 --- /dev/null +++ b/lib/tysolver.h @@ -0,0 +1,20 @@ +#ifndef RZ_TYSOLVER_H +#define RZ_TYSOLVER_H + +#include "commons.h" +#include "tysy.h" +#include "node.h" +#include "sym.h" + +typedef struct { + sym_t* sym; + tysy_t* tysy; +} tysolver_t; + +void tysolver_init(tysolver_t* tysolver, sym_t* sym, tysy_t* tysy); +void tysolver_free(tysolver_t* tysolver); + +type_t* tysolver_try_solve_node(tysolver_t* tysolver, node_t* node); +void tysolver_error(tysolver_t* tysolver, node_t* node); + +#endif diff --git a/lib/vm.c b/lib/vm.c index fb4f0e0..df50b74 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -4,13 +4,14 @@ #include "lib/type.h" #include "lib/tysy.h" -void vm_init(vm_t* vm, tysy_t* tysy, err_t* err) +void vm_init(vm_t* vm, sym_t* sym, tysy_t* tysy, err_t* err) { assert(vm); + assert(sym); + assert(tysy); - vm->pc = 0; - vm->bp = 0; - vm->sp = 0; + vm->top_frame = NULL; + vm_frame_push(vm); vm->tysy = tysy; vm->err = err; @@ -19,6 +20,125 @@ void vm_init(vm_t* vm, tysy_t* tysy, err_t* err) void vm_free(vm_t* vm) { assert(vm); + + while (vm_frame_pop(vm)) + { + } + + vm->top_frame = NULL; +} + +void vm_frame_init(frame_t* frame) +{ + assert(frame); + + frame->pc = 0; + frame->bp = 0; + frame->sp = 0; + + frame->prev = NULL; + frame->locals.cap = 0; + frame->locals.size = 0; + frame->locals.data = NULL; +} + +void vm_frame_free(frame_t* frame) +{ + for (size_t i=0; ilocals.size; i++) + { + free(frame->locals.data[i]); + } + + free(frame->locals.data); + frame->locals.data = NULL; + frame->locals.size = 0; + frame->locals.cap = 0; + + if (frame->prev != NULL) + { + vm_frame_free((frame_t*) frame->prev); + frame->prev = NULL; + } +} + +void vm_frame_push(vm_t* vm) +{ + assert(vm); + + frame_t* frame = malloc(sizeof(frame_t)); + vm_frame_init(frame); + + frame->prev = (struct frame*) vm->top_frame; + + vm->top_frame = frame; +} + +int vm_frame_pop(vm_t* vm) +{ + assert(vm); + + if (vm->top_frame) + { + frame_t* frame = vm->top_frame; + vm->top_frame = (frame_t*) vm->top_frame->prev; + + vm_frame_free(frame); + free(frame); + return 1; + } + + return 0; +} + +local_t* vm_try_local_load(vm_t* vm, int id) +{ + assert(vm); + + for (size_t i=0; itop_frame->locals.size; i++) + { + if (vm->top_frame->locals.data[i]->id == id) + { + return vm->top_frame->locals.data[i]; + } + } + + return NULL; +} + +void vm_local_store(vm_t* vm, int id, param_t addr) +{ + assert(vm); + local_t* loc = vm_try_local_load(vm, id); + + if (!loc) + { + if (vm->top_frame->locals.data == NULL) + { + vm->top_frame->locals.cap = 2; + vm->top_frame->locals.data = malloc(sizeof(local_t*) + * vm->top_frame->locals.cap); + } + + if (vm->top_frame->locals.size >= vm->top_frame->locals.cap) + { + vm->top_frame->locals.cap *= 2; + vm->top_frame->locals.data = realloc(vm->top_frame->locals.data, + sizeof(local_t*) + * vm->top_frame->locals.cap); + } + + vm->top_frame->locals.data[vm->top_frame->locals.size] + = malloc(sizeof(local_t)); + + vm->top_frame->locals.data[vm->top_frame->locals.size]->id = id; + vm->top_frame->locals.data[vm->top_frame->locals.size]->addr = addr; + vm->top_frame->locals.size++; + } + else + { + loc->id = id; + loc->addr = addr; + } } void vm_push_new_value(vm_t* vm, mod_t* mod, value_t* value) @@ -33,18 +153,18 @@ void vm_push_new_value(vm_t* vm, mod_t* mod, value_t* value) void vm_push(vm_t* vm, param_t param) { assert(vm); - assert(vm->sp + 1 < RZ_STACK_LIMIT); + assert(vm->top_frame->sp + 1 < RZ_STACK_LIMIT); - vm->stack[vm->sp] = param; - vm->sp++; + vm->top_frame->stack[vm->top_frame->sp] = param; + vm->top_frame->sp++; } param_t vm_pop(vm_t* vm) { assert(vm); - assert(vm->sp > 0); - param_t param = vm->stack[vm->sp - 1]; - vm->sp--; + assert(vm->top_frame->sp > 0); + param_t param = vm->top_frame->stack[vm->top_frame->sp - 1]; + vm->top_frame->sp--; return param; } @@ -56,9 +176,15 @@ size_t vm_stack_str(vm_t* vm, char* buffer, size_t size) size_t sz = 0; - for (size_t i=0; isp; i++) + if (vm->top_frame->sp == 0) { - sz += snprintf(buffer + sz, size - sz, "| %d |\n", vm->stack[vm->sp - 1 - i]); + sz += snprintf(buffer + sz, size - sz, "** empty **\n"); + return sz; + } + + for (size_t i=0; itop_frame->sp; i++) + { + sz += snprintf(buffer + sz, size - sz, "| %d |\n", vm->top_frame->stack[vm->top_frame->sp - 1 - i]); } return sz; @@ -67,14 +193,14 @@ size_t vm_stack_str(vm_t* vm, char* buffer, size_t size) int vm_exec_mod(vm_t* vm, mod_t* mod) { assert(vm); - vm->pc = 0; + vm->top_frame->pc = 0; - while (vm->pc < mod->program.size) + while (vm->top_frame->pc < mod->program.size) { int status = vm_exec_instr(vm, mod, - mod->program.ops[vm->pc], - mod->program.params[vm->pc]); + mod->program.ops[vm->top_frame->pc], + mod->program.params[vm->top_frame->pc]); if (status != 0) { @@ -91,17 +217,16 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) switch (op) { - case OP_PUSH: { - if (vm->sp + 1 >= RZ_STACK_LIMIT) + if (vm->top_frame->sp + 1 >= RZ_STACK_LIMIT) { fprintf(stderr, "Stack overflow\n"); abort(); } - vm->stack[vm->sp] = param; - vm->sp++; - vm->pc++; + vm->top_frame->stack[vm->top_frame->sp] = param; + vm->top_frame->sp++; + vm->top_frame->pc++; } break; case OP_ASSERT: { @@ -112,7 +237,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) { vm_ensure_type(vm, value, TYPE_BOOL); - vm->pc++; + vm->top_frame->pc++; break; } @@ -123,7 +248,8 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) return -1; } - vm->pc++; + vm_push(vm, top); + vm->top_frame->pc++; } break; @@ -148,7 +274,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm_push_new_value(vm, mod, res); - vm->pc++; + vm->top_frame->pc++; } break; case OP_LT: @@ -178,7 +304,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); vm_push_new_value(vm, mod, res); - vm->pc++; + vm->top_frame->pc++; } break; case OP_ADD: @@ -212,7 +338,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) value_t* res = tysy_new_num(vm->tysy, val, lhs->line); vm_push_new_value(vm, mod, res); - vm->pc++; + vm->top_frame->pc++; } break; case OP_STRCAT: { @@ -235,7 +361,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm_push_new_value(vm, mod, value); - vm->pc++; + vm->top_frame->pc++; } break; case OP_STRDUP: { @@ -268,33 +394,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm_push_new_value(vm, mod, value); - vm->pc++; - } break; - - case OP_AND: - case OP_OR: { - int r = vm_pop(vm); - int l = vm_pop(vm); - - value_t* lhs = mod->values.data[l]; - value_t* rhs = mod->values.data[r]; - - vm_ensure_type(vm, lhs, TYPE_BOOL); - vm_ensure_type(vm, rhs, TYPE_BOOL); - - int val = 0; - - switch (op) - { - case OP_AND: val = lhs->value.bool && rhs->value.bool; break; - case OP_OR: val = lhs->value.bool || rhs->value.bool; break; - default: assert(0); break; - } - - value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); - vm_push_new_value(vm, mod, res); - - vm->pc++; + vm->top_frame->pc++; } break; case OP_USUB: { @@ -309,7 +409,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) value_t* res = tysy_new_num(vm->tysy, val, lhs->line); vm_push_new_value(vm, mod, res); - vm->pc++; + vm->top_frame->pc++; } break; case OP_NOT: { @@ -322,7 +422,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); vm_push_new_value(vm, mod, res); - vm->pc++; + vm->top_frame->pc++; } break; case OP_BRT: @@ -333,21 +433,51 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) if (op == OP_BRT && value->value.bool) { - vm->pc = param; + vm->top_frame->pc = param; } else if (op == OP_BRF && !value->value.bool) { - vm->pc = param; + vm->top_frame->pc = param; } else { - vm->pc++; + vm->top_frame->pc++; } } break; case OP_BR: { - vm->pc = param; + vm->top_frame->pc = param; + } break; + + case OP_LOAD: { + local_t* local = vm_try_local_load(vm, param); + + if (!local) + { + char msg[RZ_STR_LIMIT]; + + snprintf(msg, RZ_STR_LIMIT, "Cannot load local variable '%d'.", + param); + + param_t p = vm->top_frame->stack[vm->top_frame->sp - 1]; + int line = mod->values.data[p]->line; + + err_fatal(vm->err, msg, line); + err_dump(vm->err); + } + else + { + vm_push(vm, vm->top_frame->stack[local->addr]); + } + + vm->top_frame->pc++; + } break; + + case OP_STORE: { + assert(vm->top_frame->sp > 0); + vm_local_store(vm, param, vm->top_frame->sp - 1); + vm->top_frame->pc++; } break; default: { diff --git a/lib/vm.h b/lib/vm.h index 6292b39..545702a 100644 --- a/lib/vm.h +++ b/lib/vm.h @@ -6,20 +6,47 @@ #include "mod.h" #include "err.h" #include "tysy.h" +#include "sym.h" typedef struct { - tysy_t* tysy; + int id; + param_t addr; +} local_t; + +typedef struct stack_frame{ + struct frame* prev; + param_t stack[RZ_STACK_LIMIT]; size_t pc; size_t bp; size_t sp; + struct { + size_t size; + size_t cap; + local_t** data; + } locals; +} frame_t; + +typedef struct { + sym_t* sym; + tysy_t* tysy; err_t* err; + frame_t* top_frame; } vm_t; -void vm_init(vm_t* vm, tysy_t* tysy, err_t* err); +void vm_init(vm_t* vm, sym_t* sym, tysy_t* tysy, err_t* err); void vm_free(vm_t* vm); +void vm_frame_init(frame_t* frame); +void vm_frame_free(frame_t* frame); + +void vm_frame_push(vm_t* vm); +int vm_frame_pop(vm_t* vm); + +local_t* vm_try_local_load(vm_t* vm, int id); +void vm_local_store(vm_t* vm, int id, param_t addr); + void vm_push_new_value(vm_t* vm, mod_t* mod, value_t* value); void vm_push(vm_t* vm, param_t param); param_t vm_pop(vm_t* vm); diff --git a/meson.build b/meson.build index e8e1049..4832a1e 100644 --- a/meson.build +++ b/meson.build @@ -25,6 +25,7 @@ roza_lib = static_library( # core 'lib/err.c', 'lib/loader.c', + 'lib/sym.c', # lang 'lib/node.c', @@ -33,6 +34,7 @@ roza_lib = static_library( 'lib/type.c', 'lib/value.c', 'lib/tysy.c', + 'lib/tysolver.c', # comp 'lib/opcodes.c', diff --git a/tests/acceptances/var.roza b/tests/acceptances/var.roza new file mode 100644 index 0000000..f7b3d5d --- /dev/null +++ b/tests/acceptances/var.roza @@ -0,0 +1,8 @@ +# VARIABLE DECLARATIONS +# ===================== +# simple +let x = 35 +assert x == 35 + +let y = x + 2 +assert y == 37 diff --git a/tests/units/lexer.c b/tests/units/lexer.c index 9065688..e433a95 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -25,13 +25,14 @@ static void test_lexer_ko(char const* source) lexer_t lex; err_t err; err_init(&err); + err.quiet = 1; lexer_init(&lex, source, &err); node_t* tok = lexer_try_new_next(&lex); cr_assert(tok == NULL, "error -> %s", source); - cr_assert(err.size > 0); + cr_assert_gt(err.total, 0); err_free(&err); lexer_free(&lex); @@ -48,7 +49,7 @@ static void test_lexer(char const* source, size_t n, ...) for (size_t i=0; i %s", source); + cr_assert(NULL != tok, "error token %ld: %s", i, source); size_t const SZ = 256; char tok_str[SZ]; @@ -83,10 +84,7 @@ Test(lexer, num) { } Test(lexer, bool) { - test_lexer_ko("true3"); test_lexer_ko("1true"); - test_lexer_ko("afalse"); - test_lexer_ko("falset"); test_lexer("true 3 false", 3, "BOOL[true]", @@ -95,9 +93,6 @@ Test(lexer, bool) { } Test(lexer, str) { - test_lexer_ko("\"hello"); - test_lexer_ko("hello\""); - test_lexer(" \"bonjour\" \" monde \"", 2, "STR[bonjour]", "STR[ monde ]"); @@ -153,3 +148,11 @@ Test(lexer, bool_arith) { "OR", "NOT"); } + +Test(lexer, let_var) { + test_lexer("let x = 3", 4, + "LET", + "IDENT[x]", + "ASSIGN", + "NUM[3]"); +} diff --git a/tests/units/parser.c b/tests/units/parser.c index 753e332..7f47bb5 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -6,6 +6,7 @@ static void test_parser_ok(char const* oracle, char const* source) { err_t err; err_init(&err); + err.quiet = 1; lexer_t lex; lexer_init(&lex, source, &err); @@ -35,6 +36,7 @@ static void test_parser_ko(char const* source) { err_t err; err_init(&err); + err.quiet = 1; lexer_t lex; lexer_init(&lex, source, &err); @@ -45,7 +47,7 @@ static void test_parser_ko(char const* source) node_t* node = parser_try_new_tree(&parser); cr_assert(NULL == node); - cr_assert_gt(err.size, 0); + cr_assert_gt(err.total, 0); parser_free(&parser); lexer_free(&lex); @@ -99,3 +101,11 @@ Test(parser, bool_arith) { test_parser_ok("MOD(ASSERT(AND(BOOL[true],BOOL[false])))", " assert (true and false) "); } + +Test(parser, var_decl) { + test_parser_ok("MOD(VARDECL(IDENT[x],NUM[33]))", + " let x = 33 "); + + test_parser_ok("MOD(VARDECL(IDENT[bim],MUL(NUM[3],NUM[6])))", + " let bim = 3 * 6 "); +}