assert expression.

main
bog 2023-12-11 18:01:22 +01:00
parent 9c74cce2a4
commit d3f90a5ed1
24 changed files with 325 additions and 78 deletions

View File

@ -1,3 +1,6 @@
MOD ::= EXPR*
EXPR ::= BUILTIN
EXPR ::=
| ASSERT
| BUILTIN
ASSERT ::= assert EXPR
BUILTIN ::= num | bool | str

View File

@ -32,7 +32,7 @@ void compiler_run(compiler_t* compiler, node_t* node)
case NODE_NUM: {
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;
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: {
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;
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: {
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;
param_t param = (param_t) mod_push_new_value(compiler->mod, val);
mod_push_instr(compiler->mod, op, param);
} 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: {
char msg[RZ_STR_LIMIT];
snprintf(msg, RZ_STR_LIMIT, "Unknown node '%s'",
fprintf(stderr, "Cannot compile unknown node '%s'",
NodeTypeStr[node->type]);
err_error(compiler->err, msg, node->line);
abort();
} break;
}
}

View File

@ -1,5 +1,7 @@
#include "err.h"
RZ_ENUM_C(ErrType, ERR_TYPES);
void err_init(err_t* err)
{
assert(err);
@ -19,7 +21,7 @@ void err_free(err_t* err)
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->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]->what = strdup(what);
err->errors[err->size]->line = line;
err->errors[err->size]->type = type;
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);
int is_fatal = 0;
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]->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;
}

View File

@ -5,7 +5,13 @@
#include "commons.h"
#define ERR_TYPES(G) \
G(ERR_WARNING), G(ERR_FATAL)
RZ_ENUM_H(ErrType, ERR_TYPES);
typedef struct {
ErrType type;
char* what;
int line;
} err_msg_t;
@ -18,7 +24,9 @@ typedef struct {
void err_init(err_t* err);
void err_free(err_t* err);
void err_error(err_t* err, char* what, int line);
void err_abort(err_t* err);
void err_of_type(err_t* err, char* what, int line, ErrType type);
void err_fatal(err_t* err, char* what, int line);
int err_dump(err_t* err);
#endif

View File

@ -1,6 +1,12 @@
#include "lexer.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)
{
assert(lexer);
@ -22,35 +28,23 @@ node_t* lexer_try_new_next(lexer_t* lexer)
assert(lexer);
size_t len = strlen(lexer->source);
// skip spaces
{
while (lexer->cursor < len
&& isspace(lexer->source[lexer->cursor]))
{
if (lexer->source[lexer->cursor] == '\n')
{
lexer->line++;
}
lexer->cursor++;
}
}
lexer_skip_spaces(lexer);
// Keywords
// ========
{
node_t* kw = lexer_try_new_keyword(lexer, "true", NODE_BOOL, 1);
if (kw) { return kw; }
}
{
node_t* kw = lexer_try_new_keyword(lexer, "false", NODE_BOOL, 1);
if (kw) { return kw; }
}
RZ_KEYWORD("true", NODE_BOOL, 1);
RZ_KEYWORD("false", NODE_BOOL, 1);
RZ_KEYWORD("assert", NODE_ASSERT, 0);
// scan str
{
node_t* node = lexer_try_new_str(lexer);
if (node) { return node; }
if (node)
{
lexer_skip_spaces(lexer);
return node;
}
}
// scan num
@ -94,6 +88,8 @@ node_t* lexer_try_new_next(lexer_t* lexer)
str_free(&res_str);
lexer->cursor = cursor;
lexer_skip_spaces(lexer);
return tok;
}
@ -105,7 +101,7 @@ node_t* lexer_try_new_next(lexer_t* lexer)
size_t const SZ = RZ_STR_LIMIT;
char msg[SZ];
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;
@ -117,7 +113,7 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
size_t cursor = lexer->cursor;
int line = lexer->line;
NodeType type = 0;
NodeType type = -1;
for (int i=0; i<lookahead; i++)
{
@ -129,6 +125,10 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
node_free(node);
free(node);
}
else
{
break;
}
}
lexer->cursor = cursor;
@ -137,6 +137,24 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead)
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,
NodeType type, int has_value)
{

View File

@ -17,6 +17,7 @@ void lexer_free(lexer_t* lexer);
node_t* lexer_try_new_next(lexer_t* lexer);
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,
NodeType type, int has_value);

View File

@ -2,6 +2,7 @@
#include "parser.h"
#include "loader.h"
#include "compiler.h"
#include "prepass.h"
#include "mod.h"
#include "vm.h"
@ -67,20 +68,41 @@ void loader_ldfile(loader_t* loader, char const* path, int debug)
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();
}
// execute
{
vm_t vm;
vm_init(&vm);
vm_init(&vm, &err);
vm_exec_mod(&vm, &mod);
@ -110,15 +132,14 @@ void loader_ldfile(loader_t* loader, char const* path, int debug)
parser_free(&parser);
lexer_free(&lex);
str_free(&source);
tysy_free(&tysy);
if (err.size > 0)
{
err_abort(&err);
}
else
// check execution errors
if (err_dump(&err))
{
err_free(&err);
abort();
}
tysy_free(&tysy);
err_free(&err);
}

View File

@ -1,4 +1,5 @@
#include "mod.h"
#include "lib/commons.h"
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");
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",
(int) i,
OpcodeStr[mod->program.ops[i]] + strlen("OP_"),
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;
}

View File

@ -34,7 +34,7 @@ void node_free(node_t* node)
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(child);
@ -57,6 +57,14 @@ void node_add_child(node_t* node, node_t* child)
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)
{
assert(node);

View File

@ -5,9 +5,8 @@
#define NODE_TYPE(G) \
G(NODE_MOD), \
G(NODE_NUM), \
G(NODE_BOOL), \
G(NODE_STR)
G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \
G(NODE_ASSERT)
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_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);

View File

@ -4,7 +4,7 @@
#include "commons.h"
#define OPCODES(G) \
G(OP_HALT), \
G(OP_ASSERT), \
G(OP_PUSH), G(OP_POP)
RZ_ENUM_H(Opcode, OPCODES);

View File

@ -33,7 +33,7 @@ node_t* parser_try_new_mod(parser_t* parser)
if (expr)
{
node_add_child(mod, expr);
node_add_new_child(mod, expr);
}
else
{
@ -49,9 +49,34 @@ node_t* parser_try_new_mod(parser_t* parser)
node_t* parser_try_new_expr(parser_t* parser)
{
assert(parser);
NodeType type = lexer_peek(parser->lexer, 1);
if (type == -1)
{
return NULL;
}
if (type == NODE_ASSERT)
{
return parser_try_new_assert(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)
{
assert(parser);
@ -80,7 +105,7 @@ node_t* parser_try_new_consume(parser_t* parser, NodeType type)
size_t const SZ = RZ_STR_LIMIT;
char err_msg[SZ];
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);
free(next);
return NULL;

View File

@ -16,6 +16,7 @@ void parser_free(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_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_consume(parser_t* parser, NodeType type);

40
lib/prepass.c Normal file
View File

@ -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;
}
}

17
lib/prepass.h Normal file
View File

@ -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

View File

@ -69,34 +69,34 @@ type_t* tysy_try_find_type(tysy_t* tysy, char* name)
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);
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;
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);
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;
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);
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);
return val;

View File

@ -16,8 +16,8 @@ void tysy_free(tysy_t* tysy);
void tysy_register_new_type(tysy_t* tysy, char* name, type_t* type);
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_bool(tysy_t* tysy, int value);
value_t* tysy_new_str(tysy_t* tysy, char* value);
value_t* tysy_new_num(tysy_t* tysy, double value, int line);
value_t* tysy_new_bool(tysy_t* tysy, int value, int line);
value_t* tysy_new_str(tysy_t* tysy, char* value, int line);
#endif

View File

@ -1,11 +1,12 @@
#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(type);
value->type = type;
value->line = line;
}
void value_free(value_t* value)

View File

@ -12,9 +12,10 @@ typedef struct {
char* str;
} value;
int line;
} 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);
int value_eq(value_t* value, value_t* rhs);

View File

@ -1,13 +1,16 @@
#include "vm.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);
vm->pc = 0;
vm->bp = 0;
vm->sp = 0;
vm->err = err;
}
void vm_free(vm_t* vm)
@ -15,6 +18,16 @@ void vm_free(vm_t* 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)
{
assert(vm);
@ -37,12 +50,19 @@ void vm_exec_mod(vm_t* vm, mod_t* mod)
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]);
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);
@ -61,10 +81,40 @@ void vm_exec_instr(vm_t* vm, Opcode op, param_t param)
vm->pc++;
} 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: {
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
OpcodeStr[op]);
abort();
};
}
return 1;
}

View File

@ -4,20 +4,25 @@
#include "commons.h"
#include "opcodes.h"
#include "mod.h"
#include "err.h"
typedef struct {
param_t stack[RZ_STACK_LIMIT];
size_t pc;
size_t bp;
size_t sp;
err_t* err;
} vm_t;
void vm_init(vm_t* vm);
void vm_init(vm_t* vm, err_t* err);
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);
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

View File

@ -34,6 +34,7 @@ roza_lib = static_library(
# comp
'lib/opcodes.c',
'lib/mod.c',
'lib/prepass.c',
'lib/compiler.c',
# exec

View File

@ -105,3 +105,7 @@ Test(lexer, str) {
test_lexer("\"the \\\"cat\\\"\"", 1, "STR[the \"cat\"]");
test_lexer("\"hello\\n\\tworld\"", 1, "STR[hello\n\tworld]");
}
Test(lexer, assert) {
test_lexer("assert", 1, "ASSERT");
}

View File

@ -66,3 +66,8 @@ Test(parser, bool) {
Test(parser, str) {
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 ");
}