✨ assert expression.
parent
9c74cce2a4
commit
d3f90a5ed1
|
@ -1,3 +1,6 @@
|
||||||
MOD ::= EXPR*
|
MOD ::= EXPR*
|
||||||
EXPR ::= BUILTIN
|
EXPR ::=
|
||||||
|
| ASSERT
|
||||||
|
| BUILTIN
|
||||||
|
ASSERT ::= assert EXPR
|
||||||
BUILTIN ::= num | bool | str
|
BUILTIN ::= num | bool | str
|
||||||
|
|
|
@ -32,7 +32,7 @@ void compiler_run(compiler_t* compiler, node_t* node)
|
||||||
|
|
||||||
case NODE_NUM: {
|
case NODE_NUM: {
|
||||||
double value = atof(node->value.data);
|
double value = atof(node->value.data);
|
||||||
value_t* val = tysy_new_num(compiler->tysy, value);
|
value_t* val = tysy_new_num(compiler->tysy, value, node->line);
|
||||||
Opcode op = OP_PUSH;
|
Opcode op = OP_PUSH;
|
||||||
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ void compiler_run(compiler_t* compiler, node_t* node)
|
||||||
|
|
||||||
case NODE_BOOL: {
|
case NODE_BOOL: {
|
||||||
int value = strcmp(node->value.data, "true") == 0;
|
int value = strcmp(node->value.data, "true") == 0;
|
||||||
value_t* val = tysy_new_bool(compiler->tysy, value);
|
value_t* val = tysy_new_bool(compiler->tysy, value, node->line);
|
||||||
Opcode op = OP_PUSH;
|
Opcode op = OP_PUSH;
|
||||||
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
||||||
|
|
||||||
|
@ -51,18 +51,24 @@ void compiler_run(compiler_t* compiler, node_t* node)
|
||||||
|
|
||||||
case NODE_STR: {
|
case NODE_STR: {
|
||||||
char* value = node->value.data;
|
char* value = node->value.data;
|
||||||
value_t* val = tysy_new_str(compiler->tysy, value);
|
value_t* val = tysy_new_str(compiler->tysy, value, node->line);
|
||||||
Opcode op = OP_PUSH;
|
Opcode op = OP_PUSH;
|
||||||
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
|
||||||
|
|
||||||
mod_push_instr(compiler->mod, op, param);
|
mod_push_instr(compiler->mod, op, param);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_ASSERT: {
|
||||||
|
assert(node->children.size == 1);
|
||||||
|
compiler_run(compiler, node_child(node, 0));
|
||||||
|
mod_push_instr(compiler->mod, OP_ASSERT, RZ_NO_PARAM);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
char msg[RZ_STR_LIMIT];
|
fprintf(stderr, "Cannot compile unknown node '%s'",
|
||||||
snprintf(msg, RZ_STR_LIMIT, "Unknown node '%s'",
|
|
||||||
NodeTypeStr[node->type]);
|
NodeTypeStr[node->type]);
|
||||||
err_error(compiler->err, msg, node->line);
|
abort();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
lib/err.c
32
lib/err.c
|
@ -1,5 +1,7 @@
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
|
|
||||||
|
RZ_ENUM_C(ErrType, ERR_TYPES);
|
||||||
|
|
||||||
void err_init(err_t* err)
|
void err_init(err_t* err)
|
||||||
{
|
{
|
||||||
assert(err);
|
assert(err);
|
||||||
|
@ -19,7 +21,7 @@ void err_free(err_t* err)
|
||||||
err->size = 0;
|
err->size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void err_error(err_t* err, char* what, int line)
|
void err_of_type(err_t* err, char* what, int line, ErrType type)
|
||||||
{
|
{
|
||||||
assert(err);
|
assert(err);
|
||||||
assert(err->size + 1 < RZ_ERROR_STACK_SIZE);
|
assert(err->size + 1 < RZ_ERROR_STACK_SIZE);
|
||||||
|
@ -27,21 +29,41 @@ void err_error(err_t* err, char* what, int line)
|
||||||
err->errors[err->size] = malloc(sizeof(err_msg_t));
|
err->errors[err->size] = malloc(sizeof(err_msg_t));
|
||||||
err->errors[err->size]->what = strdup(what);
|
err->errors[err->size]->what = strdup(what);
|
||||||
err->errors[err->size]->line = line;
|
err->errors[err->size]->line = line;
|
||||||
|
err->errors[err->size]->type = type;
|
||||||
err->size++;
|
err->size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void err_abort(err_t* err)
|
void err_fatal(err_t* err, char* what, int line)
|
||||||
|
{
|
||||||
|
err_of_type(err, what, line, ERR_FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int err_dump(err_t* err)
|
||||||
{
|
{
|
||||||
assert(err);
|
assert(err);
|
||||||
|
int is_fatal = 0;
|
||||||
|
|
||||||
for (size_t i=0; i<err->size; i++)
|
for (size_t i=0; i<err->size; i++)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERR(%d) %s\n",
|
if (err->errors[i]->type == ERR_WARNING)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\33[33mWARNING\33[0m[:%d] %s\n",
|
||||||
err->errors[i]->line,
|
err->errors[i]->line,
|
||||||
err->errors[i]->what);
|
err->errors[i]->what);
|
||||||
}
|
}
|
||||||
|
|
||||||
err_free(err);
|
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);
|
||||||
|
|
||||||
abort();
|
is_fatal = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err_free(err);
|
||||||
|
err_init(err);
|
||||||
|
|
||||||
|
return is_fatal;
|
||||||
}
|
}
|
||||||
|
|
12
lib/err.h
12
lib/err.h
|
@ -5,7 +5,13 @@
|
||||||
|
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
|
#define ERR_TYPES(G) \
|
||||||
|
G(ERR_WARNING), G(ERR_FATAL)
|
||||||
|
|
||||||
|
RZ_ENUM_H(ErrType, ERR_TYPES);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
ErrType type;
|
||||||
char* what;
|
char* what;
|
||||||
int line;
|
int line;
|
||||||
} err_msg_t;
|
} err_msg_t;
|
||||||
|
@ -18,7 +24,9 @@ typedef struct {
|
||||||
void err_init(err_t* err);
|
void err_init(err_t* err);
|
||||||
void err_free(err_t* err);
|
void err_free(err_t* err);
|
||||||
|
|
||||||
void err_error(err_t* err, char* what, int line);
|
void err_of_type(err_t* err, char* what, int line, ErrType type);
|
||||||
void err_abort(err_t* err);
|
void err_fatal(err_t* err, char* what, int line);
|
||||||
|
|
||||||
|
int err_dump(err_t* err);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
66
lib/lexer.c
66
lib/lexer.c
|
@ -1,6 +1,12 @@
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "lib/commons.h"
|
#include "lib/commons.h"
|
||||||
|
|
||||||
|
#define RZ_KEYWORD(KW, NODE, VAL) \
|
||||||
|
{ \
|
||||||
|
node_t* kw = lexer_try_new_keyword(lexer, KW, NODE, VAL); \
|
||||||
|
if (kw) { lexer_skip_spaces(lexer); return kw; } \
|
||||||
|
}
|
||||||
|
|
||||||
void lexer_init(lexer_t* lexer, char const* source, err_t* err)
|
void lexer_init(lexer_t* lexer, char const* source, err_t* err)
|
||||||
{
|
{
|
||||||
assert(lexer);
|
assert(lexer);
|
||||||
|
@ -22,35 +28,23 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
assert(lexer);
|
assert(lexer);
|
||||||
size_t len = strlen(lexer->source);
|
size_t len = strlen(lexer->source);
|
||||||
|
|
||||||
// skip spaces
|
lexer_skip_spaces(lexer);
|
||||||
{
|
|
||||||
while (lexer->cursor < len
|
|
||||||
&& isspace(lexer->source[lexer->cursor]))
|
|
||||||
{
|
|
||||||
if (lexer->source[lexer->cursor] == '\n')
|
|
||||||
{
|
|
||||||
lexer->line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer->cursor++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
// ========
|
// ========
|
||||||
{
|
RZ_KEYWORD("true", NODE_BOOL, 1);
|
||||||
node_t* kw = lexer_try_new_keyword(lexer, "true", NODE_BOOL, 1);
|
RZ_KEYWORD("false", NODE_BOOL, 1);
|
||||||
if (kw) { return kw; }
|
RZ_KEYWORD("assert", NODE_ASSERT, 0);
|
||||||
}
|
|
||||||
{
|
|
||||||
node_t* kw = lexer_try_new_keyword(lexer, "false", NODE_BOOL, 1);
|
|
||||||
if (kw) { return kw; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan str
|
// scan str
|
||||||
{
|
{
|
||||||
node_t* node = lexer_try_new_str(lexer);
|
node_t* node = lexer_try_new_str(lexer);
|
||||||
if (node) { return node; }
|
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
lexer_skip_spaces(lexer);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scan num
|
// scan num
|
||||||
|
@ -94,6 +88,8 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
str_free(&res_str);
|
str_free(&res_str);
|
||||||
|
|
||||||
lexer->cursor = cursor;
|
lexer->cursor = cursor;
|
||||||
|
|
||||||
|
lexer_skip_spaces(lexer);
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +101,7 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
size_t const SZ = RZ_STR_LIMIT;
|
size_t const SZ = RZ_STR_LIMIT;
|
||||||
char msg[SZ];
|
char msg[SZ];
|
||||||
snprintf(msg, SZ, "unexpected symbol '%c'", lexer->source[lexer->cursor]);
|
snprintf(msg, SZ, "unexpected symbol '%c'", lexer->source[lexer->cursor]);
|
||||||
err_error(lexer->err, msg, lexer->line);
|
err_fatal(lexer->err, msg, lexer->line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -117,7 +113,7 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
|
||||||
size_t cursor = lexer->cursor;
|
size_t cursor = lexer->cursor;
|
||||||
int line = lexer->line;
|
int line = lexer->line;
|
||||||
|
|
||||||
NodeType type = 0;
|
NodeType type = -1;
|
||||||
|
|
||||||
for (int i=0; i<lookahead; i++)
|
for (int i=0; i<lookahead; i++)
|
||||||
{
|
{
|
||||||
|
@ -129,6 +125,10 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
|
||||||
node_free(node);
|
node_free(node);
|
||||||
free(node);
|
free(node);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lexer->cursor = cursor;
|
lexer->cursor = cursor;
|
||||||
|
@ -137,6 +137,24 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lexer_skip_spaces(lexer_t* lexer)
|
||||||
|
{
|
||||||
|
assert(lexer);
|
||||||
|
size_t len = strlen(lexer->source);
|
||||||
|
|
||||||
|
while (lexer->cursor < len
|
||||||
|
&& isspace(lexer->source[lexer->cursor]))
|
||||||
|
{
|
||||||
|
if (lexer->source[lexer->cursor] == '\n')
|
||||||
|
{
|
||||||
|
lexer->line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer->cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
||||||
NodeType type, int has_value)
|
NodeType type, int has_value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,7 @@ void lexer_free(lexer_t* lexer);
|
||||||
|
|
||||||
node_t* lexer_try_new_next(lexer_t* lexer);
|
node_t* lexer_try_new_next(lexer_t* lexer);
|
||||||
NodeType lexer_peek(lexer_t* lexer, int lookahead);
|
NodeType lexer_peek(lexer_t* lexer, int lookahead);
|
||||||
|
void lexer_skip_spaces(lexer_t* lexer);
|
||||||
|
|
||||||
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
||||||
NodeType type, int has_value);
|
NodeType type, int has_value);
|
||||||
|
|
35
lib/loader.c
35
lib/loader.c
|
@ -2,6 +2,7 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
|
#include "prepass.h"
|
||||||
#include "mod.h"
|
#include "mod.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
|
@ -67,20 +68,41 @@ void loader_ldfile(loader_t* loader, char const* path, int debug)
|
||||||
mod_t mod;
|
mod_t mod;
|
||||||
mod_init(&mod);
|
mod_init(&mod);
|
||||||
|
|
||||||
|
// prepass
|
||||||
|
{
|
||||||
|
prepass_t prepass;
|
||||||
|
prepass_init(&prepass, &err);
|
||||||
|
prepass_run(&prepass, node);
|
||||||
|
prepass_free(&prepass);
|
||||||
|
}
|
||||||
|
|
||||||
// compile
|
// compile
|
||||||
{
|
{
|
||||||
compiler_t compiler;
|
compiler_t compiler;
|
||||||
compiler_init(&compiler, &mod, &tysy, &err);
|
compiler_init(&compiler, &mod, &tysy, &err);
|
||||||
compiler_run(&compiler, node);
|
compiler_run(&compiler, node);
|
||||||
compiler_free(&compiler);
|
compiler_free(&compiler);
|
||||||
|
}
|
||||||
|
|
||||||
node_free(node);
|
node_free(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();
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
{
|
{
|
||||||
vm_t vm;
|
vm_t vm;
|
||||||
vm_init(&vm);
|
vm_init(&vm, &err);
|
||||||
|
|
||||||
vm_exec_mod(&vm, &mod);
|
vm_exec_mod(&vm, &mod);
|
||||||
|
|
||||||
|
@ -110,15 +132,14 @@ void loader_ldfile(loader_t* loader, char const* path, int debug)
|
||||||
parser_free(&parser);
|
parser_free(&parser);
|
||||||
lexer_free(&lex);
|
lexer_free(&lex);
|
||||||
str_free(&source);
|
str_free(&source);
|
||||||
|
tysy_free(&tysy);
|
||||||
|
|
||||||
if (err.size > 0)
|
// check execution errors
|
||||||
{
|
if (err_dump(&err))
|
||||||
err_abort(&err);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
err_free(&err);
|
err_free(&err);
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
tysy_free(&tysy);
|
err_free(&err);
|
||||||
}
|
}
|
||||||
|
|
10
lib/mod.c
10
lib/mod.c
|
@ -1,4 +1,5 @@
|
||||||
#include "mod.h"
|
#include "mod.h"
|
||||||
|
#include "lib/commons.h"
|
||||||
|
|
||||||
void mod_init(mod_t* mod)
|
void mod_init(mod_t* mod)
|
||||||
{
|
{
|
||||||
|
@ -73,12 +74,21 @@ size_t mod_str(mod_t* mod, char* buffer, size_t size)
|
||||||
|
|
||||||
sz += snprintf(buffer + sz, size - sz, "\n======== PROGRAM ========\n");
|
sz += snprintf(buffer + sz, size - sz, "\n======== PROGRAM ========\n");
|
||||||
for (size_t i=0; i<mod->program.size; i++)
|
for (size_t i=0; i<mod->program.size; i++)
|
||||||
|
{
|
||||||
|
if (mod->program.params[i] != RZ_NO_PARAM)
|
||||||
{
|
{
|
||||||
sz += snprintf(buffer + sz, size - sz, "%d| %s %d\n",
|
sz += snprintf(buffer + sz, size - sz, "%d| %s %d\n",
|
||||||
(int) i,
|
(int) i,
|
||||||
OpcodeStr[mod->program.ops[i]] + strlen("OP_"),
|
OpcodeStr[mod->program.ops[i]] + strlen("OP_"),
|
||||||
mod->program.params[i]);
|
mod->program.params[i]);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%d| %s\n",
|
||||||
|
(int) i,
|
||||||
|
OpcodeStr[mod->program.ops[i]] + strlen("OP_"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
10
lib/node.c
10
lib/node.c
|
@ -34,7 +34,7 @@ void node_free(node_t* node)
|
||||||
node->children.cap = 0;
|
node->children.cap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void node_add_child(node_t* node, node_t* child)
|
void node_add_new_child(node_t* node, node_t* child)
|
||||||
{
|
{
|
||||||
assert(node);
|
assert(node);
|
||||||
assert(child);
|
assert(child);
|
||||||
|
@ -57,6 +57,14 @@ void node_add_child(node_t* node, node_t* child)
|
||||||
node->children.size++;
|
node->children.size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node_t* node_child(node_t* node, size_t index)
|
||||||
|
{
|
||||||
|
assert(node);
|
||||||
|
assert(index < node->children.size);
|
||||||
|
|
||||||
|
return (node_t*) node->children.data[index];
|
||||||
|
}
|
||||||
|
|
||||||
size_t node_str(node_t* node, char* buffer, size_t size)
|
size_t node_str(node_t* node, char* buffer, size_t size)
|
||||||
{
|
{
|
||||||
assert(node);
|
assert(node);
|
||||||
|
|
|
@ -5,9 +5,8 @@
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
#define NODE_TYPE(G) \
|
||||||
G(NODE_MOD), \
|
G(NODE_MOD), \
|
||||||
G(NODE_NUM), \
|
G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \
|
||||||
G(NODE_BOOL), \
|
G(NODE_ASSERT)
|
||||||
G(NODE_STR)
|
|
||||||
|
|
||||||
RZ_ENUM_H(NodeType, NODE_TYPE);
|
RZ_ENUM_H(NodeType, NODE_TYPE);
|
||||||
|
|
||||||
|
@ -26,7 +25,8 @@ typedef struct {
|
||||||
void node_init(node_t* node, NodeType type, char* value, int line);
|
void node_init(node_t* node, NodeType type, char* value, int line);
|
||||||
void node_free(node_t* node);
|
void node_free(node_t* node);
|
||||||
|
|
||||||
void node_add_child(node_t* node, node_t* child);
|
void node_add_new_child(node_t* node, node_t* child);
|
||||||
|
node_t* node_child(node_t* node, size_t index);
|
||||||
|
|
||||||
size_t node_str(node_t* node, char* buffer, size_t size);
|
size_t node_str(node_t* node, char* buffer, size_t size);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
#define OPCODES(G) \
|
#define OPCODES(G) \
|
||||||
G(OP_HALT), \
|
G(OP_ASSERT), \
|
||||||
G(OP_PUSH), G(OP_POP)
|
G(OP_PUSH), G(OP_POP)
|
||||||
|
|
||||||
RZ_ENUM_H(Opcode, OPCODES);
|
RZ_ENUM_H(Opcode, OPCODES);
|
||||||
|
|
29
lib/parser.c
29
lib/parser.c
|
@ -33,7 +33,7 @@ node_t* parser_try_new_mod(parser_t* parser)
|
||||||
|
|
||||||
if (expr)
|
if (expr)
|
||||||
{
|
{
|
||||||
node_add_child(mod, expr);
|
node_add_new_child(mod, expr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -49,9 +49,34 @@ node_t* parser_try_new_mod(parser_t* parser)
|
||||||
node_t* parser_try_new_expr(parser_t* parser)
|
node_t* parser_try_new_expr(parser_t* parser)
|
||||||
{
|
{
|
||||||
assert(parser);
|
assert(parser);
|
||||||
|
NodeType type = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (type == -1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == NODE_ASSERT)
|
||||||
|
{
|
||||||
|
return parser_try_new_assert(parser);
|
||||||
|
}
|
||||||
|
|
||||||
return parser_try_new_builtin(parser);
|
return parser_try_new_builtin(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_assert(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
node_t* node = parser_try_new_consume(parser, NODE_ASSERT);
|
||||||
|
assert(node);
|
||||||
|
|
||||||
|
node_t* expr = parser_try_new_expr(parser);
|
||||||
|
assert(expr);
|
||||||
|
node_add_new_child(node, expr);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_builtin(parser_t* parser)
|
node_t* parser_try_new_builtin(parser_t* parser)
|
||||||
{
|
{
|
||||||
assert(parser);
|
assert(parser);
|
||||||
|
@ -80,7 +105,7 @@ node_t* parser_try_new_consume(parser_t* parser, NodeType type)
|
||||||
size_t const SZ = RZ_STR_LIMIT;
|
size_t const SZ = RZ_STR_LIMIT;
|
||||||
char err_msg[SZ];
|
char err_msg[SZ];
|
||||||
snprintf(err_msg, SZ, "unexpected node '%s'", NodeTypeStr[next->type]);
|
snprintf(err_msg, SZ, "unexpected node '%s'", NodeTypeStr[next->type]);
|
||||||
err_error(parser->err, err_msg, next->line);
|
err_fatal(parser->err, err_msg, next->line);
|
||||||
node_free(next);
|
node_free(next);
|
||||||
free(next);
|
free(next);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -16,6 +16,7 @@ void parser_free(parser_t* parser);
|
||||||
node_t* parser_try_new_tree(parser_t* parser);
|
node_t* parser_try_new_tree(parser_t* parser);
|
||||||
node_t* parser_try_new_mod(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_expr(parser_t* parser);
|
||||||
|
node_t* parser_try_new_assert(parser_t* parser);
|
||||||
node_t* parser_try_new_builtin(parser_t* parser);
|
node_t* parser_try_new_builtin(parser_t* parser);
|
||||||
|
|
||||||
node_t* parser_try_new_consume(parser_t* parser, NodeType type);
|
node_t* parser_try_new_consume(parser_t* parser, NodeType type);
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "prepass.h"
|
||||||
|
|
||||||
|
void prepass_init(prepass_t* prepass, err_t* err)
|
||||||
|
{
|
||||||
|
assert(prepass);
|
||||||
|
assert(err);
|
||||||
|
|
||||||
|
prepass->err = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepass_free(prepass_t* prepass)
|
||||||
|
{
|
||||||
|
assert(prepass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepass_run(prepass_t* prepass, node_t* node)
|
||||||
|
{
|
||||||
|
assert(prepass);
|
||||||
|
assert(node);
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_NUM:
|
||||||
|
case NODE_STR:
|
||||||
|
case NODE_BOOL:
|
||||||
|
case NODE_ASSERT:{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef RZ_PREPASS_H
|
||||||
|
#define RZ_PREPASS_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
#include "err.h"
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
err_t* err;
|
||||||
|
} prepass_t;
|
||||||
|
|
||||||
|
void prepass_init(prepass_t* prepass, err_t* err);
|
||||||
|
void prepass_free(prepass_t* prepass);
|
||||||
|
|
||||||
|
void prepass_run(prepass_t* prepass, node_t* node);
|
||||||
|
|
||||||
|
#endif
|
12
lib/tysy.c
12
lib/tysy.c
|
@ -69,34 +69,34 @@ type_t* tysy_try_find_type(tysy_t* tysy, char* name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t* tysy_new_num(tysy_t* tysy, double value)
|
value_t* tysy_new_num(tysy_t* tysy, double value, int line)
|
||||||
{
|
{
|
||||||
assert(tysy);
|
assert(tysy);
|
||||||
|
|
||||||
value_t* val = malloc(sizeof(value_t));
|
value_t* val = malloc(sizeof(value_t));
|
||||||
value_init(val, tysy_try_find_type(tysy, "num"));
|
value_init(val, tysy_try_find_type(tysy, "num"), line);
|
||||||
val->value.num = value;
|
val->value.num = value;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t* tysy_new_bool(tysy_t* tysy, int value)
|
value_t* tysy_new_bool(tysy_t* tysy, int value, int line)
|
||||||
{
|
{
|
||||||
assert(tysy);
|
assert(tysy);
|
||||||
|
|
||||||
value_t* val = malloc(sizeof(value_t));
|
value_t* val = malloc(sizeof(value_t));
|
||||||
value_init(val, tysy_try_find_type(tysy, "bool"));
|
value_init(val, tysy_try_find_type(tysy, "bool"), line);
|
||||||
val->value.bool = value;
|
val->value.bool = value;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t* tysy_new_str(tysy_t* tysy, char* value)
|
value_t* tysy_new_str(tysy_t* tysy, char* value, int line)
|
||||||
{
|
{
|
||||||
assert(tysy);
|
assert(tysy);
|
||||||
|
|
||||||
value_t* val = malloc(sizeof(value_t));
|
value_t* val = malloc(sizeof(value_t));
|
||||||
value_init(val, tysy_try_find_type(tysy, "str"));
|
value_init(val, tysy_try_find_type(tysy, "str"), line);
|
||||||
val->value.str = strdup(value);
|
val->value.str = strdup(value);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
|
|
|
@ -16,8 +16,8 @@ void tysy_free(tysy_t* tysy);
|
||||||
void tysy_register_new_type(tysy_t* tysy, char* name, type_t* type);
|
void tysy_register_new_type(tysy_t* tysy, char* name, type_t* type);
|
||||||
type_t* tysy_try_find_type(tysy_t* tysy, char* name);
|
type_t* tysy_try_find_type(tysy_t* tysy, char* name);
|
||||||
|
|
||||||
value_t* tysy_new_num(tysy_t* tysy, double value);
|
value_t* tysy_new_num(tysy_t* tysy, double value, int line);
|
||||||
value_t* tysy_new_bool(tysy_t* tysy, int value);
|
value_t* tysy_new_bool(tysy_t* tysy, int value, int line);
|
||||||
value_t* tysy_new_str(tysy_t* tysy, char* value);
|
value_t* tysy_new_str(tysy_t* tysy, char* value, int line);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
||||||
void value_init(value_t* value, type_t* type)
|
void value_init(value_t* value, type_t* type, int line)
|
||||||
{
|
{
|
||||||
assert(value);
|
assert(value);
|
||||||
assert(type);
|
assert(type);
|
||||||
|
|
||||||
value->type = type;
|
value->type = type;
|
||||||
|
value->line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void value_free(value_t* value)
|
void value_free(value_t* value)
|
||||||
|
|
|
@ -12,9 +12,10 @@ typedef struct {
|
||||||
char* str;
|
char* str;
|
||||||
} value;
|
} value;
|
||||||
|
|
||||||
|
int line;
|
||||||
} value_t;
|
} value_t;
|
||||||
|
|
||||||
void value_init(value_t* value, type_t* type);
|
void value_init(value_t* value, type_t* type, int line);
|
||||||
void value_free(value_t* value);
|
void value_free(value_t* value);
|
||||||
|
|
||||||
int value_eq(value_t* value, value_t* rhs);
|
int value_eq(value_t* value, value_t* rhs);
|
||||||
|
|
56
lib/vm.c
56
lib/vm.c
|
@ -1,13 +1,16 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "lib/commons.h"
|
#include "lib/commons.h"
|
||||||
|
#include "lib/type.h"
|
||||||
|
|
||||||
void vm_init(vm_t* vm)
|
void vm_init(vm_t* vm, err_t* err)
|
||||||
{
|
{
|
||||||
assert(vm);
|
assert(vm);
|
||||||
|
|
||||||
vm->pc = 0;
|
vm->pc = 0;
|
||||||
vm->bp = 0;
|
vm->bp = 0;
|
||||||
vm->sp = 0;
|
vm->sp = 0;
|
||||||
|
|
||||||
|
vm->err = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm_free(vm_t* vm)
|
void vm_free(vm_t* vm)
|
||||||
|
@ -15,6 +18,16 @@ void vm_free(vm_t* vm)
|
||||||
assert(vm);
|
assert(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
param_t vm_pop(vm_t* vm)
|
||||||
|
{
|
||||||
|
assert(vm);
|
||||||
|
assert(vm->sp > 0);
|
||||||
|
param_t param = vm->stack[vm->sp - 1];
|
||||||
|
vm->sp--;
|
||||||
|
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size)
|
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size)
|
||||||
{
|
{
|
||||||
assert(vm);
|
assert(vm);
|
||||||
|
@ -37,12 +50,19 @@ void vm_exec_mod(vm_t* vm, mod_t* mod)
|
||||||
|
|
||||||
while (vm->pc < mod->program.size)
|
while (vm->pc < mod->program.size)
|
||||||
{
|
{
|
||||||
vm_exec_instr(vm, mod->program.ops[vm->pc],
|
int status = vm_exec_instr(vm,
|
||||||
|
mod,
|
||||||
|
mod->program.ops[vm->pc],
|
||||||
mod->program.params[vm->pc]);
|
mod->program.params[vm->pc]);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm_exec_instr(vm_t* vm, Opcode op, param_t param)
|
int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
|
||||||
{
|
{
|
||||||
assert(vm);
|
assert(vm);
|
||||||
|
|
||||||
|
@ -61,10 +81,40 @@ void vm_exec_instr(vm_t* vm, Opcode op, param_t param)
|
||||||
vm->pc++;
|
vm->pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OP_ASSERT: {
|
||||||
|
param_t top = vm_pop(vm);
|
||||||
|
value_t* value = mod->values.data[top];
|
||||||
|
|
||||||
|
if (value->type->kind != TYPE_BOOL)
|
||||||
|
{
|
||||||
|
char msg[RZ_STR_LIMIT];
|
||||||
|
snprintf(msg, RZ_STR_LIMIT,
|
||||||
|
"type mismatch: BOOL expected, got %s.",
|
||||||
|
TypeKindStr[value->type->kind] + strlen("TYPE_"));
|
||||||
|
|
||||||
|
err_fatal(vm->err, msg, value->line);
|
||||||
|
|
||||||
|
vm->pc++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value->value.bool)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\33[31mASSERT\33[0m[:%d] assertion failed\n",
|
||||||
|
value->line);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->pc++;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
|
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
|
||||||
OpcodeStr[op]);
|
OpcodeStr[op]);
|
||||||
abort();
|
abort();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
9
lib/vm.h
9
lib/vm.h
|
@ -4,20 +4,25 @@
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include "mod.h"
|
#include "mod.h"
|
||||||
|
#include "err.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
param_t stack[RZ_STACK_LIMIT];
|
param_t stack[RZ_STACK_LIMIT];
|
||||||
size_t pc;
|
size_t pc;
|
||||||
size_t bp;
|
size_t bp;
|
||||||
size_t sp;
|
size_t sp;
|
||||||
|
|
||||||
|
err_t* err;
|
||||||
} vm_t;
|
} vm_t;
|
||||||
|
|
||||||
void vm_init(vm_t* vm);
|
void vm_init(vm_t* vm, err_t* err);
|
||||||
void vm_free(vm_t* vm);
|
void vm_free(vm_t* vm);
|
||||||
|
|
||||||
|
param_t vm_pop(vm_t* vm);
|
||||||
|
|
||||||
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size);
|
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size);
|
||||||
|
|
||||||
void vm_exec_mod(vm_t* vm, mod_t* mod);
|
void vm_exec_mod(vm_t* vm, mod_t* mod);
|
||||||
void vm_exec_instr(vm_t* vm, Opcode op, param_t param);
|
int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,6 +34,7 @@ roza_lib = static_library(
|
||||||
# comp
|
# comp
|
||||||
'lib/opcodes.c',
|
'lib/opcodes.c',
|
||||||
'lib/mod.c',
|
'lib/mod.c',
|
||||||
|
'lib/prepass.c',
|
||||||
'lib/compiler.c',
|
'lib/compiler.c',
|
||||||
|
|
||||||
# exec
|
# exec
|
||||||
|
|
|
@ -105,3 +105,7 @@ Test(lexer, str) {
|
||||||
test_lexer("\"the \\\"cat\\\"\"", 1, "STR[the \"cat\"]");
|
test_lexer("\"the \\\"cat\\\"\"", 1, "STR[the \"cat\"]");
|
||||||
test_lexer("\"hello\\n\\tworld\"", 1, "STR[hello\n\tworld]");
|
test_lexer("\"hello\\n\\tworld\"", 1, "STR[hello\n\tworld]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(lexer, assert) {
|
||||||
|
test_lexer("assert", 1, "ASSERT");
|
||||||
|
}
|
||||||
|
|
|
@ -66,3 +66,8 @@ Test(parser, bool) {
|
||||||
Test(parser, str) {
|
Test(parser, str) {
|
||||||
test_parser_ok("MOD(STR[bim bam bum])", " \"bim bam bum\"");
|
test_parser_ok("MOD(STR[bim bam bum])", " \"bim bam bum\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(parser, assert) {
|
||||||
|
test_parser_ok("MOD(ASSERT(BOOL[false]))", " assert false ");
|
||||||
|
test_parser_ok("MOD(ASSERT(BOOL[true]))", " assert true ");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue