✨ simple variable declarations.
parent
5b8748b843
commit
bb83d39bb9
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
16
lib/err.c
16
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)
|
||||
|
@ -46,24 +50,36 @@ int err_dump(err_t* err)
|
|||
for (size_t i=0; i<err->size; i++)
|
||||
{
|
||||
if (err->errors[i]->type == ERR_WARNING)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
63
lib/lexer.c
63
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
78
lib/loader.c
78
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,11 +61,30 @@ 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)
|
||||
{
|
||||
|
@ -74,15 +95,20 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
|
|||
printf("%s\n", msg);
|
||||
}
|
||||
|
||||
if (node)
|
||||
{
|
||||
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, &err);
|
||||
prepass_init(&prepass, &sym, &tysolver, &err);
|
||||
prepass_run(&prepass, node);
|
||||
prepass_free(&prepass);
|
||||
}
|
||||
|
@ -90,11 +116,19 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
|
|||
// compile
|
||||
{
|
||||
compiler_t compiler;
|
||||
compiler_init(&compiler, &mod, &tysy, &err);
|
||||
compiler_run(&compiler, node);
|
||||
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);
|
||||
|
||||
|
@ -104,23 +138,18 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
|
|||
parser_free(&parser);
|
||||
lexer_free(&lex);
|
||||
str_free(&source);
|
||||
tysy_free(&tysy);
|
||||
err_free(&err);
|
||||
mod_free(&mod);
|
||||
sym_free(&sym);
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
if (debug)
|
||||
{
|
||||
char msg[RZ_STR_LIMIT];
|
||||
mod_str(&mod, msg, RZ_STR_LIMIT);
|
||||
printf("%s", msg);
|
||||
tysy_free(&tysy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// execute
|
||||
{
|
||||
vm_t vm;
|
||||
vm_init(&vm, &tysy, &err);
|
||||
vm_init(&vm, &sym, &tysy, &err);
|
||||
|
||||
int status = vm_exec_mod(&vm, &mod);
|
||||
|
||||
|
@ -128,6 +157,7 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
|
|||
{
|
||||
vm_free(&vm);
|
||||
mod_free(&mod);
|
||||
sym_free(&sym);
|
||||
parser_free(&parser);
|
||||
lexer_free(&lex);
|
||||
str_free(&source);
|
||||
|
@ -148,9 +178,21 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
|
|||
vm_free(&vm);
|
||||
}
|
||||
|
||||
mod_free(&mod);
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
74
lib/parser.c
74
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; i<node->children.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; i<sym->entries.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; i<sym->entries.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; i<sym->entries.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;
|
||||
}
|
|
@ -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
|
|
@ -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_"));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
250
lib/vm.c
250
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; i<frame->locals.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; i<vm->top_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; i<vm->sp; 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; i<vm->top_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: {
|
||||
|
|
31
lib/vm.h
31
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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# VARIABLE DECLARATIONS
|
||||
# =====================
|
||||
# simple
|
||||
let x = 35
|
||||
assert x == 35
|
||||
|
||||
let y = x + 2
|
||||
assert y == 37
|
|
@ -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<n; i++)
|
||||
{
|
||||
node_t* tok = lexer_try_new_next(&lexer);
|
||||
cr_assert(NULL != tok, "error -> %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]");
|
||||
}
|
||||
|
|
|
@ -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 ");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue