diff --git a/Makefile b/Makefile index 6dfb306..1f3c1dd 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,16 @@ build: tests: build build/roza-tests -install: build +install: tests meson install -C build check: @cppcheck --language=c --enable=all lib src -q \ - --suppress=missingIncludeSystem + --suppress=missingIncludeSystem \ + --suppress=missingInclude \ + --suppress=unmatchedSuppression @cppcheck --language=c --enable=all tests/units -q \ --suppress=missingIncludeSystem \ - --suppress=unusedFunction + --suppress=unusedFunction \ + --suppress=unmatchedSuppression diff --git a/doc/roza.bnf b/doc/roza.bnf index e69de29..6e3027d 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -0,0 +1,2 @@ +MOD ::= EXPR* +EXPR ::= num diff --git a/lib/commons.h b/lib/commons.h new file mode 100644 index 0000000..61f0193 --- /dev/null +++ b/lib/commons.h @@ -0,0 +1,30 @@ +#ifndef RZ_COMMONS_H +#define RZ_COMMONS_H + +#include +#include +#include +#include +#include + +#include +#include "str.h" + +#define RZ_STR_LIMIT 256 +#define RZ_STACK_LIMIT 1024 +#define RZ_MAX_TYPES 256 + +#define RZ_NO_PARAM (-1) +typedef int param_t; + +#define RZ_ENUM_ID(X) X +#define RZ_ENUM_STR(X) #X + +#define RZ_ENUM_H(PREFIX, NAME) \ + typedef enum PREFIX { NAME(RZ_ENUM_ID) } PREFIX; \ + extern char const* PREFIX ## Str [] + +#define RZ_ENUM_C(PREFIX, NAME) \ + char const* PREFIX ## Str [] = { NAME(RZ_ENUM_STR) } + +#endif diff --git a/lib/compiler.c b/lib/compiler.c new file mode 100644 index 0000000..7b4335e --- /dev/null +++ b/lib/compiler.c @@ -0,0 +1,50 @@ +#include "compiler.h" +#include "lib/commons.h" + +void compiler_init(compiler_t* compiler, mod_t* mod, tysy_t* tysy, err_t* err) +{ + assert(compiler); + assert(err); + + compiler->mod = mod; + compiler->tysy = tysy; + compiler->err = err; +} + +void compiler_free(compiler_t* compiler) +{ + assert(compiler); +} + +void compiler_run(compiler_t* compiler, node_t* node) +{ + assert(compiler); + assert(node); + + switch (node->type) + { + case NODE_MOD: { + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, (node_t*) node->children.data[i]); + } + } break; + + case NODE_NUM: { + double value = atof(node->value.data); + value_t* val = tysy_new_num(compiler->tysy, value); + Opcode op = OP_PUSH; + param_t param = (param_t) mod_push_new_value(compiler->mod, val); + + mod_push_instr(compiler->mod, op, param); + + } break; + + default: { + char msg[RZ_STR_LIMIT]; + snprintf(msg, RZ_STR_LIMIT, "Unknown node '%s'", + NodeTypeStr[node->type]); + err_error(compiler->err, msg, node->line); + } break; + } +} diff --git a/lib/compiler.h b/lib/compiler.h new file mode 100644 index 0000000..5cf2652 --- /dev/null +++ b/lib/compiler.h @@ -0,0 +1,21 @@ +#ifndef RZ_COMPILER_H +#define RZ_COMPILER_H + +#include "commons.h" +#include "err.h" +#include "mod.h" +#include "node.h" +#include "tysy.h" + +typedef struct { + mod_t* mod; + 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_free(compiler_t* compiler); + +void compiler_run(compiler_t* compiler, node_t* node); + +#endif diff --git a/lib/err.c b/lib/err.c new file mode 100644 index 0000000..9d3eb6e --- /dev/null +++ b/lib/err.c @@ -0,0 +1,47 @@ +#include "err.h" + +void err_init(err_t* err) +{ + assert(err); + err->size = 0; +} + +void err_free(err_t* err) +{ + assert(err); + + for (size_t i=0; isize; i++) + { + free(err->errors[i]->what); + free(err->errors[i]); + } + + err->size = 0; +} + +void err_error(err_t* err, char* what, int line) +{ + assert(err); + assert(err->size + 1 < RZ_ERROR_STACK_SIZE); + + err->errors[err->size] = malloc(sizeof(err_msg_t)); + err->errors[err->size]->what = strdup(what); + err->errors[err->size]->line = line; + err->size++; +} + +void err_abort(err_t* err) +{ + assert(err); + + for (size_t i=0; isize; i++) + { + fprintf(stderr, "ERR(%d) %s\n", + err->errors[i]->line, + err->errors[i]->what); + } + + err_free(err); + + abort(); +} diff --git a/lib/err.h b/lib/err.h new file mode 100644 index 0000000..09d2c4f --- /dev/null +++ b/lib/err.h @@ -0,0 +1,24 @@ +#ifndef RZ_ERR_H +#define RZ_ERR_H + +#define RZ_ERROR_STACK_SIZE 256 + +#include "commons.h" + +typedef struct { + char* what; + int line; +} err_msg_t; + +typedef struct { + size_t size; + err_msg_t* errors[RZ_ERROR_STACK_SIZE]; +} err_t; + +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); + +#endif diff --git a/lib/lexer.c b/lib/lexer.c new file mode 100644 index 0000000..2ed9c6f --- /dev/null +++ b/lib/lexer.c @@ -0,0 +1,95 @@ +#include "lexer.h" +#include "lib/commons.h" + +void lexer_init(lexer_t* lexer, char const* source, err_t* err) +{ + assert(lexer); + lexer->source = strdup(source); + lexer->cursor = 0; + lexer->err = err; + lexer->line = 1; +} + +void lexer_free(lexer_t* lexer) +{ + assert(lexer); + free(lexer->source); + lexer->source = NULL; +} + +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++; + } + } + + // scan num + { + size_t cursor = lexer->cursor; + + str_t res_str; + str_init(&res_str); + + if (cursor < len && lexer->source[cursor] == '-') + { + str_push(&res_str, lexer->source[cursor]); + cursor++; + } + + while (cursor < len + && isdigit(lexer->source[cursor])) + { + str_push(&res_str, lexer->source[cursor]); + cursor += 1; + } + + if (cursor < len && lexer->source[cursor] == '.') + { + str_push(&res_str, lexer->source[cursor]); + cursor++; + + while (cursor < len + && isdigit(lexer->source[cursor])) + { + str_push(&res_str, lexer->source[cursor]); + cursor += 1; + } + } + + if (res_str.size > 0 + && (cursor >= len || isspace(lexer->source[cursor]))) + { + node_t* tok = malloc(sizeof(node_t)); + node_init(tok, NODE_NUM, res_str.data, lexer->line); + str_free(&res_str); + + lexer->cursor = cursor; + return tok; + } + + str_free(&res_str); + } + + if (lexer->cursor < len && lexer->err) + { + 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); + } + + return NULL; +} diff --git a/lib/lexer.h b/lib/lexer.h new file mode 100644 index 0000000..179cce6 --- /dev/null +++ b/lib/lexer.h @@ -0,0 +1,20 @@ +#ifndef RZ_LEXER_H +#define RZ_LEXER_H + +#include "commons.h" +#include "node.h" +#include "err.h" + +typedef struct { + err_t* err; + char* source; + size_t cursor; + int line; +} lexer_t; + +void lexer_init(lexer_t* lexer, char const* source, err_t* err); +void lexer_free(lexer_t* lexer); + +node_t* lexer_try_new_next(lexer_t* lexer); + +#endif diff --git a/lib/loader.c b/lib/loader.c new file mode 100644 index 0000000..a5109a5 --- /dev/null +++ b/lib/loader.c @@ -0,0 +1,124 @@ +#include "lexer.h" +#include "parser.h" +#include "loader.h" +#include "compiler.h" +#include "mod.h" +#include "vm.h" + +void loader_init(loader_t* loader) +{ + assert(loader); +} + +void loader_free(loader_t* loader) +{ + assert(loader); +} + +void loader_ldfile(loader_t* loader, char const* path, int debug) +{ + assert(loader); + str_t source; + str_init(&source); + + // read sources + { + FILE* file = fopen(path, "r"); + + if (!file) + { + fprintf(stderr, "Cannot open file '%s'.\n", path); + abort(); + } + + char buf; + size_t sz; + + while ( (sz = fread(&buf, sizeof(char), 1, file)) ) + { + str_push(&source, buf); + } + + fclose(file); + } + + if (str_empty(&source)) + { + str_free(&source); + return; + } + + tysy_t tysy; + tysy_init(&tysy); + + err_t err; + err_init(&err); + + lexer_t lex; + lexer_init(&lex, source.data, &err); + + parser_t parser; + parser_init(&parser, &lex, &err); + + node_t* node = parser_try_new_tree(&parser); + + if (node) + { + mod_t mod; + mod_init(&mod); + + // compile + { + compiler_t compiler; + compiler_init(&compiler, &mod, &tysy, &err); + compiler_run(&compiler, node); + compiler_free(&compiler); + node_free(node); + free(node); + } + + // execute + { + vm_t vm; + vm_init(&vm); + + vm_exec_mod(&vm, &mod); + + if (debug) + { + { + char msg[RZ_STR_LIMIT]; + mod_str(&mod, msg, RZ_STR_LIMIT); + printf("%s", msg); + } + + { + char msg[RZ_STR_LIMIT]; + vm_stack_str(&vm, msg, RZ_STR_LIMIT); + printf("\n======== STACK ========\n"); + printf("%s\n", msg); + } + } + + vm_free(&vm); + } + + mod_free(&mod); + } + + // free + parser_free(&parser); + lexer_free(&lex); + str_free(&source); + + if (err.size > 0) + { + err_abort(&err); + } + else + { + err_free(&err); + } + + tysy_free(&tysy); +} diff --git a/lib/loader.h b/lib/loader.h new file mode 100644 index 0000000..91f39f7 --- /dev/null +++ b/lib/loader.h @@ -0,0 +1,15 @@ +#ifndef RZ_LOADER_H +#define RZ_LOADER_H + +#include "commons.h" + +typedef struct { + +} loader_t; + +void loader_init(loader_t* loader); +void loader_free(loader_t* loader); + +void loader_ldfile(loader_t* loader, char const* path, int debug); + +#endif diff --git a/lib/mod.c b/lib/mod.c new file mode 100644 index 0000000..9f5ca93 --- /dev/null +++ b/lib/mod.c @@ -0,0 +1,118 @@ +#include "mod.h" + +void mod_init(mod_t* mod) +{ + assert(mod); + + mod->program.size = 0; + mod->program.cap = 0; + mod->program.ops = NULL; + mod->program.params = NULL; + + mod->values.size = 0; + mod->values.cap = 0; + mod->values.data = NULL; +} + +void mod_free(mod_t* mod) +{ + assert(mod); + free(mod->program.ops); + free(mod->program.params); + mod->program.size = 0; + mod->program.cap = 0; + + for (size_t i=0; ivalues.size; i++) + { + value_free(mod->values.data[i]); + free(mod->values.data[i]); + } + + free(mod->values.data); + mod->values.data = NULL; + mod->values.size = 0; + mod->values.cap = 0; +} + +void mod_push_instr(mod_t* mod, Opcode op, param_t param) +{ + assert(mod); + + if (mod->program.cap == 0) + { + mod->program.cap = 2; + mod->program.ops = malloc(sizeof(Opcode) * mod->program.cap); + mod->program.params = malloc(sizeof(param_t) * mod->program.cap); + } + + if (mod->program.size >= mod->program.cap) + { + mod->program.cap *= 2; + mod->program.ops = realloc(mod->program.ops, + sizeof(Opcode) * mod->program.cap); + mod->program.params = realloc(mod->program.params, + sizeof(param_t) * mod->program.cap); + } + + mod->program.ops[mod->program.size] = op; + mod->program.params[mod->program.size] = param; + mod->program.size++; +} + +size_t mod_str(mod_t* mod, char* buffer, size_t size) +{ + size_t sz = 0; + + sz += snprintf(buffer + sz, size - sz, "======== VALUES ========\n"); + for (size_t i=0; ivalues.size; i++) + { + sz += snprintf(buffer + sz, size - sz, "%d| ", (int) i); + sz += value_str(mod->values.data[i], buffer + sz, size - sz); + sz += snprintf(buffer + sz, size - sz, "\n"); + } + + sz += snprintf(buffer + sz, size - sz, "\n======== PROGRAM ========\n"); + for (size_t i=0; iprogram.size; i++) + { + sz += snprintf(buffer + sz, size - sz, "%d| %s %d\n", + (int) i, + OpcodeStr[mod->program.ops[i]] + strlen("OP_"), + mod->program.params[i]); + } + + return sz; +} + +size_t mod_push_new_value(mod_t* mod, value_t* value) +{ + assert(mod); + assert(value); + + for (size_t i=0; ivalues.size; i++) + { + if (value_eq(value, mod->values.data[i])) + { + value_free(value); + free(value); + return i; + } + } + + if (mod->values.size == 0) + { + mod->values.cap = 2; + mod->values.data = malloc(sizeof(value_t*) * mod->values.cap); + } + + if (mod->values.size >= mod->values.cap) + { + mod->values.cap *= 2; + mod->values.data = realloc(mod->values.data, + sizeof(value_t*) * mod->values.cap); + } + + mod->values.data[mod->values.size] = value; + mod->values.size++; + + return mod->values.size - 1; +} diff --git a/lib/mod.h b/lib/mod.h new file mode 100644 index 0000000..1af1ab7 --- /dev/null +++ b/lib/mod.h @@ -0,0 +1,31 @@ +#ifndef RZ_MOD_H +#define RZ_MOD_H + +#include "commons.h" +#include "opcodes.h" +#include "value.h" + +typedef struct { + struct { + size_t size; + size_t cap; + Opcode* ops; + param_t* params; + } program; + + struct { + size_t size; + size_t cap; + value_t** data; + } values; +} mod_t; + +void mod_init(mod_t* mod); +void mod_free(mod_t* mod); + +void mod_push_instr(mod_t* mod, Opcode op, param_t param); +size_t mod_str(mod_t* mod, char* buffer, size_t size); + +size_t mod_push_new_value(mod_t* mod, value_t* value); + +#endif diff --git a/lib/node.c b/lib/node.c new file mode 100644 index 0000000..f186cdc --- /dev/null +++ b/lib/node.c @@ -0,0 +1,91 @@ +#include "node.h" + +RZ_ENUM_C(NodeType, NODE_TYPE); + +void node_init(node_t* node, NodeType type, char* value, int line) +{ + assert(node); + assert(value); + + node->type = type; + str_init(&node->value); + str_append(&node->value, value); + + node->line = line; + + node->children.size = 0; + node->children.cap = 0; + node->children.data = NULL; +} + +void node_free(node_t* node) +{ + assert(node); + str_free(&node->value); + + for (size_t i=0; ichildren.size; i++) + { + node_free((node_t*) node->children.data[i]); + free(node->children.data[i]); + } + + free(node->children.data); + node->children.size = 0; + node->children.cap = 0; +} + +void node_add_child(node_t* node, node_t* child) +{ + assert(node); + assert(child); + + if (node->children.cap == 0) + { + node->children.cap = 2; + node->children.data = malloc(sizeof(node_t) * node->children.cap); + } + + if (node->children.size >= node->children.cap) + { + node->children.cap *= 2; + node->children.data = realloc(node->children.data, + sizeof(node_t) * node->children.cap); + } + + size_t sz = node->children.size; + node->children.data[sz] = (struct node_t*) child; + node->children.size++; +} + +size_t node_str(node_t* node, char* buffer, size_t size) +{ + assert(node); + size_t sz = 0; + + sz += snprintf(buffer + sz, size - sz, "%s", + NodeTypeStr[node->type] + strlen("NODE_")); + + if (!str_empty(&node->value)) + { + sz += snprintf(buffer + sz, size - sz, "[%s]", node->value.data); + } + + if (node->children.size > 0) + { + sz += snprintf(buffer +sz, size - sz, "("); + + for (size_t i=0; ichildren.size; i++) + { + if (i > 0) + { + sz += snprintf(buffer +sz, size - sz, ","); + } + + sz += node_str((node_t*) node->children.data[i], buffer + sz, size - sz); + } + + sz += snprintf(buffer +sz, size - sz, ")"); + } + + return sz; +} diff --git a/lib/node.h b/lib/node.h new file mode 100644 index 0000000..92e4787 --- /dev/null +++ b/lib/node.h @@ -0,0 +1,31 @@ +#ifndef RZ_NODE_H +#define RZ_NODE_H + +#include "commons.h" + +#define NODE_TYPE(G) \ + G(NODE_MOD), \ + G(NODE_NUM) + +RZ_ENUM_H(NodeType, NODE_TYPE); + +typedef struct { + NodeType type; + str_t value; + int line; + + struct { + struct node_t** data; + size_t size; + size_t cap; + } children; +} node_t; + +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); + +size_t node_str(node_t* node, char* buffer, size_t size); + +#endif diff --git a/lib/opcodes.c b/lib/opcodes.c new file mode 100644 index 0000000..77da13a --- /dev/null +++ b/lib/opcodes.c @@ -0,0 +1,3 @@ +#include "opcodes.h" + +RZ_ENUM_C(Opcode, OPCODES); diff --git a/lib/opcodes.h b/lib/opcodes.h new file mode 100644 index 0000000..77c7040 --- /dev/null +++ b/lib/opcodes.h @@ -0,0 +1,12 @@ +#ifndef RZ_OPCODES_H +#define RZ_OPCODES_H + +#include "commons.h" + +#define OPCODES(G) \ + G(OP_HALT), \ + G(OP_PUSH), G(OP_POP) + +RZ_ENUM_H(Opcode, OPCODES); + +#endif diff --git a/lib/parser.c b/lib/parser.c new file mode 100644 index 0000000..6583eca --- /dev/null +++ b/lib/parser.c @@ -0,0 +1,83 @@ +#include "parser.h" +#include "lib/lexer.h" +#include "lib/node.h" + +void parser_init(parser_t* parser, lexer_t* lexer, err_t* err) +{ + assert(parser); + parser->lexer = lexer; + parser->err = err; +} + +void parser_free(parser_t* parser) +{ + assert(parser); +} + +node_t* parser_try_new_tree(parser_t* parser) +{ + assert(parser); + return parser_try_new_mod(parser); +} + +node_t* parser_try_new_mod(parser_t* parser) +{ + assert(parser); + node_t* mod = malloc(sizeof(node_t)); + node_init(mod, NODE_MOD, "", parser->lexer->line); + size_t len = strlen(parser->lexer->source); + + while (parser->lexer->cursor < len) + { + node_t* expr = parser_try_new_expr(parser); + + if (expr) + { + node_add_child(mod, expr); + } + else + { + node_free(mod); + free(mod); + return NULL; + } + } + + return mod; +} + +node_t* parser_try_new_expr(parser_t* parser) +{ + assert(parser); + return parser_try_new_num(parser); +} + +node_t* parser_try_new_num(parser_t* parser) +{ + assert(parser); + return parser_try_new_consume(parser, NODE_NUM); +} + +node_t* parser_try_new_consume(parser_t* parser, NodeType type) +{ + assert(parser); + node_t* next = lexer_try_new_next(parser->lexer); + + if (!next) + { + return NULL; + } + + 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_error(parser->err, err_msg, next->line); + node_free(next); + free(next); + return NULL; + } + + return next; +} diff --git a/lib/parser.h b/lib/parser.h new file mode 100644 index 0000000..d259c72 --- /dev/null +++ b/lib/parser.h @@ -0,0 +1,23 @@ +#ifndef RZ_PARSER_H +#define RZ_PARSER_H + +#include "commons.h" +#include "err.h" +#include "lexer.h" + +typedef struct { + lexer_t* lexer; + err_t* err; +} parser_t; + +void parser_init(parser_t* parser, lexer_t* lexer, err_t* err); +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_num(parser_t* parser); + +node_t* parser_try_new_consume(parser_t* parser, NodeType type); + +#endif diff --git a/lib/str.c b/lib/str.c new file mode 100644 index 0000000..17548f8 --- /dev/null +++ b/lib/str.c @@ -0,0 +1,67 @@ +#include "str.h" + +void str_init(str_t* str) +{ + assert(str); + str->data = NULL; + str->size = 0; + str->cap = 0; +} + +void str_free(str_t* str) +{ + assert(str); + + free(str->data); + str->data = NULL; + str->size = 0; + str->cap = 0; +} + +void str_resize(str_t* str) +{ + assert(str); + + if (str->data == NULL) + { + str->cap = 2; + str->data = calloc(str->cap + 1, sizeof(char)); + } + + if (str->size >= str->cap) + { + str->cap *= 2; + str->data = realloc(str->data, sizeof(char) * (str->cap + 1)); + } +} + +void str_push(str_t* str, char c) +{ + assert(str); + + str_resize(str); + str->data[str->size] = c; + + str_resize(str); + str->data[str->size + 1] = '\0'; + str->size++; +} + +void str_append(str_t* str, char const* src) +{ + assert(str); + assert(src); + + size_t len = strlen(src); + + for (size_t i=0; isize == 0; +} diff --git a/lib/str.h b/lib/str.h new file mode 100644 index 0000000..6b70696 --- /dev/null +++ b/lib/str.h @@ -0,0 +1,22 @@ +#ifndef RZ_STR_H +#define RZ_STR_H + +#include +#include +#include + +typedef struct { + char* data; + size_t size; + size_t cap; +} str_t; + +void str_init(str_t* str); +void str_free(str_t* str); + +void str_resize(str_t* str); +void str_push(str_t* str, char c); +void str_append(str_t* str, char const* src); +int str_empty(str_t* str); + +#endif diff --git a/lib/type.c b/lib/type.c new file mode 100644 index 0000000..bda4886 --- /dev/null +++ b/lib/type.c @@ -0,0 +1,22 @@ +#include "type.h" + +RZ_ENUM_C(TypeKind, TYPE_KIND); + +void type_init(type_t* type, TypeKind kind) +{ + assert(type); + type->kind = kind; +} + +void type_free(type_t* type) +{ + assert(type); +} + +int type_eq(type_t* type, type_t* rhs) +{ + assert(type); + assert(rhs); + + return type->kind == rhs->kind; +} diff --git a/lib/type.h b/lib/type.h new file mode 100644 index 0000000..f0e4331 --- /dev/null +++ b/lib/type.h @@ -0,0 +1,20 @@ +#ifndef RZ_TYPE_H +#define RZ_TYPE_H + +#include "commons.h" + +#define TYPE_KIND(G) \ + G(TYPE_NUM) + +RZ_ENUM_H(TypeKind, TYPE_KIND); + +typedef struct { + TypeKind kind; +} type_t; + +void type_init(type_t* type, TypeKind kind); +void type_free(type_t* type); + +int type_eq(type_t* type, type_t* rhs); + +#endif diff --git a/lib/tysy.c b/lib/tysy.c new file mode 100644 index 0000000..5984655 --- /dev/null +++ b/lib/tysy.c @@ -0,0 +1,67 @@ +#include "tysy.h" + +void tysy_init(tysy_t* tysy) +{ + assert(tysy); + tysy->size = 0; + + // num + { + type_t* num = malloc(sizeof(type_t)); + type_init(num, TYPE_NUM); + tysy_register_new_type(tysy, "num", num); + } +} + +void tysy_free(tysy_t* tysy) +{ + assert(tysy); + + for (size_t i=0; isize; i++) + { + type_free(tysy->types[i]); + free(tysy->types[i]); + free(tysy->names[i]); + } + + tysy->size = 0; +} + +void tysy_register_new_type(tysy_t* tysy, char* name, type_t* type) +{ + assert(tysy); + assert(name); + assert(type); + assert(tysy->size + 1 < RZ_MAX_TYPES); + + tysy->types[tysy->size] = type; + tysy->names[tysy->size] = strdup(name); + tysy->size++; +} + +type_t* tysy_try_find_type(tysy_t* tysy, char* name) +{ + assert(tysy); + assert(name); + + for (size_t i=0; isize; i++) + { + if (strcmp(name, tysy->names[i]) == 0) + { + return tysy->types[i]; + } + } + + return NULL; +} + +value_t* tysy_new_num(tysy_t* tysy, double value) +{ + assert(tysy); + + value_t* val = malloc(sizeof(value_t)); + value_init(val, tysy_try_find_type(tysy, "num")); + val->value.num = value; + + return val; +} diff --git a/lib/tysy.h b/lib/tysy.h new file mode 100644 index 0000000..1719120 --- /dev/null +++ b/lib/tysy.h @@ -0,0 +1,21 @@ +#ifndef RZ_TYSY_H +#define RZ_TYSY_H + +#include "commons.h" +#include "value.h" + +typedef struct { + type_t* types[RZ_MAX_TYPES]; + char* names[RZ_MAX_TYPES]; + size_t size; +} tysy_t; + +void tysy_init(tysy_t* tysy); +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); + +#endif diff --git a/lib/value.c b/lib/value.c new file mode 100644 index 0000000..44bb89a --- /dev/null +++ b/lib/value.c @@ -0,0 +1,52 @@ +#include "value.h" + +void value_init(value_t* value, type_t* type) +{ + assert(value); + assert(type); + + value->type = type; +} + +void value_free(value_t* value) +{ + assert(value); +} + +int value_eq(value_t* value, value_t* rhs) +{ + if (!type_eq(value->type, rhs->type)) + { + return 0; + } + + switch (value->type->kind) + { + case TYPE_NUM: { + return value->value.num == rhs->value.num; + } break; + + default: { + fprintf(stderr, "Cannot compare value of type '%s'.\n", + TypeKindStr[value->type->kind]); + abort(); + }; + } + + return 0; +} + +size_t value_str(value_t* value, char* buffer, size_t size) +{ + assert(value); + assert(buffer); + + switch (value->type->kind) + { + case TYPE_NUM: + return snprintf(buffer, size, "%lf", value->value.num); + break; + + default: return 0; + } +} diff --git a/lib/value.h b/lib/value.h new file mode 100644 index 0000000..bc96092 --- /dev/null +++ b/lib/value.h @@ -0,0 +1,22 @@ +#ifndef RZ_VALUE_H +#define RZ_VALUE_H + +#include "type.h" + +typedef struct { + type_t* type; + + union { + double num; + } value; + +} value_t; + +void value_init(value_t* value, type_t* type); +void value_free(value_t* value); + +int value_eq(value_t* value, value_t* rhs); + +size_t value_str(value_t* value, char* buffer, size_t size); + +#endif diff --git a/lib/vm.c b/lib/vm.c new file mode 100644 index 0000000..f977ca9 --- /dev/null +++ b/lib/vm.c @@ -0,0 +1,70 @@ +#include "vm.h" +#include "lib/commons.h" + +void vm_init(vm_t* vm) +{ + assert(vm); + + vm->pc = 0; + vm->bp = 0; + vm->sp = 0; +} + +void vm_free(vm_t* vm) +{ + assert(vm); +} + +size_t vm_stack_str(vm_t* vm, char* buffer, size_t size) +{ + assert(vm); + assert(buffer); + + size_t sz = 0; + + for (size_t i=0; isp; i++) + { + sz += snprintf(buffer + sz, size - sz, "| %d |\n", vm->stack[vm->sp - 1 - i]); + } + + return sz; +} + +void vm_exec_mod(vm_t* vm, mod_t* mod) +{ + assert(vm); + vm->pc = 0; + + while (vm->pc < mod->program.size) + { + vm_exec_instr(vm, mod->program.ops[vm->pc], + mod->program.params[vm->pc]); + } +} + +void vm_exec_instr(vm_t* vm, Opcode op, param_t param) +{ + assert(vm); + + switch (op) + { + + case OP_PUSH: { + if (vm->sp + 1 >= RZ_STACK_LIMIT) + { + fprintf(stderr, "Stack overflow\n"); + abort(); + } + + vm->stack[vm->sp] = param; + vm->sp++; + vm->pc++; + } break; + + default: { + fprintf(stderr, "Cannot execute unknown opcode '%s'.\n", + OpcodeStr[op]); + abort(); + }; + } +} diff --git a/lib/vm.h b/lib/vm.h new file mode 100644 index 0000000..ace7d1a --- /dev/null +++ b/lib/vm.h @@ -0,0 +1,23 @@ +#ifndef RZ_VM_H +#define RZ_VM_H + +#include "commons.h" +#include "opcodes.h" +#include "mod.h" + +typedef struct { + param_t stack[RZ_STACK_LIMIT]; + size_t pc; + size_t bp; + size_t sp; +} vm_t; + +void vm_init(vm_t* vm); +void vm_free(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); + +#endif diff --git a/meson.build b/meson.build index 32e4d00..3f41da5 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,28 @@ configure_file( roza_lib = static_library( 'roza', sources: [ + # utils + 'lib/str.c', + + # core + 'lib/err.c', + 'lib/loader.c', + + # lang + 'lib/node.c', + 'lib/lexer.c', + 'lib/parser.c', + 'lib/type.c', + 'lib/value.c', + 'lib/tysy.c', + + # comp + 'lib/opcodes.c', + 'lib/mod.c', + 'lib/compiler.c', + + # exec + 'lib/vm.c', ] ) @@ -35,7 +57,8 @@ executable('roza', executable('roza-tests', sources: [ - 'tests/units/trivial.c' + 'tests/units/lexer.c', + 'tests/units/parser.c', ], dependencies: [ roza_dep, diff --git a/src/main.c b/src/main.c index 4e40e7e..b2e9863 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,64 @@ -#include -#include +#include +#include +#include -int main() +int main(int argc, char** argv) { - printf("Roza v%s\n", ROZA_VERSION); + int index = 0; + int debug = 0; + + while (1) + { + static struct option opts[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {"debug", no_argument, 0, 'd'}, + {0, 0, 0, 0} + }; + + char c = getopt_long(argc, argv, "hvd", opts, &index); + + if (c == -1) { break; } + + switch (c) + { + case 'h': { + printf("Usage: roza [OPTIONS] [SOURCE]\n"); + printf("OPTIONS:\n"); + printf("\t-d, --debug\tshow roza debug messages\n"); + printf("\t-h, --help\tshow this message\n"); + printf("\t-v, --version\tshow roza version\n"); + exit(0); + } break; + + case 'v': { + printf("Roza v%s\n", ROZA_VERSION); + printf("License GPLv3+\n"); + exit(0); + } break; + + case 'd': { + printf("Roza v%s: Debug mode enabled\n", ROZA_VERSION); + debug = 1; + } break; + + default: + break; + } + } + + if (optind < argc) + { + loader_t loader; + loader_init(&loader); + + while (optind < argc) + { + loader_ldfile(&loader, argv[optind++], debug); + } + + loader_free(&loader); + } + return 0; } diff --git a/tests/units/lexer.c b/tests/units/lexer.c new file mode 100644 index 0000000..458eab6 --- /dev/null +++ b/tests/units/lexer.c @@ -0,0 +1,83 @@ +#include +#include +#include + +static void test_lexer_ok(char const* oracle, char const* source) +{ + lexer_t lex; + lexer_init(&lex, source, NULL); + + node_t* tok = lexer_try_new_next(&lex); + cr_assert(tok != NULL); + + size_t const SZ = 512; + char str[SZ]; + node_str(tok, str, SZ); + + cr_assert_str_eq(str, oracle); + + node_free(tok); + lexer_free(&lex); +} + +static void test_lexer_ko(char const* source) +{ + lexer_t lex; + err_t err; + err_init(&err); + + lexer_init(&lex, source, &err); + + node_t* tok = lexer_try_new_next(&lex); + + cr_assert(tok == NULL); + cr_assert(err.size > 0); + + err_free(&err); + lexer_free(&lex); +} + +static void test_lexer(char const* source, size_t n, ...) +{ + va_list lst; + va_start(lst, n); + + lexer_t lexer; + lexer_init(&lexer, source, NULL); + + for (size_t i=0; i +#include +#include + +static void test_parser_ok(char const* oracle, char const* source) +{ + err_t err; + err_init(&err); + + lexer_t lex; + lexer_init(&lex, source, &err); + + parser_t parser; + parser_init(&parser, &lex, &err); + + node_t* node = parser_try_new_tree(&parser); + + cr_assert(NULL != node); + + size_t const SZ = 256; + char msg[SZ]; + node_str(node, msg, SZ); + + cr_assert_str_eq(msg, oracle); + + node_free(node); + free(node); + + parser_free(&parser); + lexer_free(&lex); + err_free(&err); +} + +static void test_parser_ko(char const* source) +{ + err_t err; + err_init(&err); + + lexer_t lex; + lexer_init(&lex, source, &err); + + parser_t parser; + parser_init(&parser, &lex, &err); + + node_t* node = parser_try_new_tree(&parser); + + cr_assert(NULL == node); + cr_assert_gt(err.size, 0); + + parser_free(&parser); + lexer_free(&lex); + err_free(&err); +} + +Test(parser, num) { + test_parser_ok("MOD", ""); + test_parser_ok("MOD(NUM[37],NUM[29])", "37 29"); + + test_parser_ko(" ยง "); +} diff --git a/tests/units/trivial.c b/tests/units/trivial.c deleted file mode 100644 index a89a2e0..0000000 --- a/tests/units/trivial.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -Test(trivial, trivial) { - cr_assert(1 + 1 == 2); -}