diff --git a/CMakeLists.txt b/CMakeLists.txt index 7807249..fa89e5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,11 +15,13 @@ add_executable(fzc src/compiler/fzl_walker.c src/compiler/fzl_writer.c src/compiler/utils/fzl_const_pool.c + src/compiler/utils/fzl_symtable.c src/compiler/main.c) # Virtual machine add_executable(fz src/vm/fzl_vm.c src/vm/fzl_vm_env.c + src/vm/fzl_vm_debug.c src/vm/main.c) diff --git a/include/common/fzl_types.h b/include/common/fzl_types.h index cde0312..6e85456 100644 --- a/include/common/fzl_types.h +++ b/include/common/fzl_types.h @@ -7,6 +7,7 @@ typedef enum FZL_TYPE_STRING, FZL_TYPE_CHAR, + FZL_TYPE_BOOL, FZL_TYPE_INT8, FZL_TYPE_UINT8, @@ -16,10 +17,8 @@ typedef enum FZL_TYPE_UINT32, FZL_TYPE_INT64, FZL_TYPE_UINT64, - FZL_TYPE_INT, // machine word int - FZL_TYPE_UINT, // machine word unsigned int - - FZL_TYPE_BOOL + FZL_TYPE_INT, // machine word int + FZL_TYPE_UINT // machine word unsigned int } fzl_type_t; #endif // FZL_TYPES_H diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index cd733c7..e51a34c 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,9 @@ typedef struct uint64_t *ops; size_t op_count; size_t op_remainder; + + fzl_symtable_t *syms[64]; + size_t sym_top; } fzl_walker_t; // Allocate new walker diff --git a/include/compiler/utils/fzl_symtable.h b/include/compiler/utils/fzl_symtable.h new file mode 100644 index 0000000..e81f0dc --- /dev/null +++ b/include/compiler/utils/fzl_symtable.h @@ -0,0 +1,44 @@ +#ifndef FZL_SYMTABLE_H +#define FZL_SYMTABLE_H + +#include +#include +#include + +typedef enum +{ + FZL_SYMTABLE_RESULT_OK, + FZL_SYMTABLE_RESULT_ERR, + FZL_SYMTABLE_RESULT_EXISTS +} fzl_symtable_res_t; + +// Symbols table entry descriptor +typedef struct +{ + char *name; + fzl_type_t type; +} fzl_sym_t; + +// Symbols table descriptor +typedef struct +{ + size_t size; + size_t remainder; + + fzl_sym_t *symbols; +} fzl_symtable_t; + +// Allocate new symbtable +fzl_symtable_t *fzl_mksymt(); + +// Put new entry into symtable +fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, + fzl_type_t type); + +// Get entry from specified symtable +bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res); + +// Free symbtable +void fzl_freesymt(fzl_symtable_t *symt); + +#endif // FZL_SYMTABLE_H diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index ca55dcb..e772fdd 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -27,7 +27,7 @@ typedef struct size_t ctop; // Environment stack - fzl_vm_env_t estack[256]; + fzl_vm_env_t *estack[256]; size_t etop; } fzl_vm_t; @@ -40,6 +40,9 @@ int fzl_vm_run(fzl_vm_t *vm); // Get last VM error message char *fzl_vm_geterror(); +// Print opcodes sequence +void fzl_vm_print_ops(fzl_vm_t *vm); + // Free VM void fzl_freevm(fzl_vm_t *vm); diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index f8303a7..ddcffc6 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -43,11 +44,63 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) walker->op_remainder = 2; walker->ops = malloc(walker->op_remainder * sizeof(uint64_t)); + walker->sym_top = 0; + walker->syms[walker->sym_top++] = fzl_mksymt(); + if (!walker->syms[walker->sym_top - 1]) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to create global symtable"); + return NULL; + } + return walker; } // --- Walking zone --- +// Convert type token to `fzl_type_t` +const fzl_type_t tok_to_type(fzl_token_t *tok) +{ + if (strcmp((char *)tok->value, "i8") == 0) + return FZL_TYPE_INT8; + if (strcmp((char *)tok->value, "i16") == 0) + return FZL_TYPE_INT16; + if (strcmp((char *)tok->value, "i32") == 0) + return FZL_TYPE_INT32; + if (strcmp((char *)tok->value, "i64") == 0) + return FZL_TYPE_INT64; + + if (strcmp((char *)tok->value, "u8") == 0) + return FZL_TYPE_UINT8; + if (strcmp((char *)tok->value, "u16") == 0) + return FZL_TYPE_UINT16; + if (strcmp((char *)tok->value, "u32") == 0) + return FZL_TYPE_UINT32; + if (strcmp((char *)tok->value, "u64") == 0) + return FZL_TYPE_UINT64; + + if (strcmp((char *)tok->value, "int") == 0) + return FZL_TYPE_INT; + if (strcmp((char *)tok->value, "uint") == 0) + return FZL_TYPE_UINT; + + if (strcmp((char *)tok->value, "bool") == 0) + return FZL_TYPE_BOOL; + if (strcmp((char *)tok->value, "str") == 0) + return FZL_TYPE_STRING; + if (strcmp((char *)tok->value, "char") == 0) + return FZL_TYPE_CHAR; + + return FZL_TYPE_NULL; +} + +static bool is_compatible(fzl_type_t t1, fzl_type_t t2) +{ + if (t1 == t2) + return true; + + return t1 >= FZL_TYPE_INT8 && t2 >= FZL_TYPE_INT8; +} + // Add const to consts table size_t add_const(fzl_walker_t *walker, fzl_const_t c) { @@ -94,8 +147,6 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (!walker || !node) return (fzl_walk_res_t){.error = true, .error_msg = "walker is null"}; - push_op(walker, FZL_OPCODE_MK_ENVTABLE); - fzl_walk_res_t current_res; while (node != NULL) { @@ -111,13 +162,28 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) .as.string.chars = node->token->value, .as.string.length = node->token->value_sz}); + fzl_sym_t sym; + bool exists = fzl_symt_get(walker->syms[walker->sym_top - 1], + node->token->value, &sym); + if (!exists) + { + snprintf(wlast_err, sizeof(wlast_err), + "cannot find symbol '%s' at %ld:%ld", + (char *)node->token->value, node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, n); - current_res = (fzl_walk_res_t){.type = FZL_TYPE_STRING, .error = false}; + push_op(walker, FZL_OPCODE_LOOKUP_ENV); + + current_res = (fzl_walk_res_t){.type = sym.type, .error = false}; break; } case FZL_NODETYPE_BLOCK: + push_op(walker, FZL_OPCODE_MK_ENVTABLE); if (node->list.entry_count > 0) for (size_t i = 0; i < node->list.entry_count; i++) { @@ -125,10 +191,11 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (res.error) return res; } + push_op(walker, FZL_OPCODE_RM_ENVTABLE); break; case FZL_NODETYPE_CALL: - if (node->list.entry_count > 0) + if (node->call.args_count > 0) { for (size_t i = 0; i < node->call.args_count; i++) { @@ -198,7 +265,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (rres.error) return rres; - if (lres.type == FZL_TYPE_STRING) + if (lres.type < FZL_TYPE_INT8) { fzl_token_t *ltok = node->binary_op.left->token; snprintf(wlast_err, sizeof(wlast_err), @@ -207,7 +274,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - if (rres.type == FZL_TYPE_STRING) + if (rres.type < FZL_TYPE_INT8) { fzl_token_t *rtok = node->binary_op.right->token; snprintf( @@ -250,7 +317,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (res.error) return res; - if (res.type == FZL_TYPE_STRING) + if (res.type < FZL_TYPE_INT8) { fzl_token_t *tok = node->unary_op.operand->token; snprintf(wlast_err, sizeof(wlast_err), @@ -266,13 +333,22 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) case FZL_NODETYPE_DECLARATION: { - fzl_walk_res_t rres = walk(walker, node->var.identifier); - if (rres.error) - return rres; + fzl_symtable_res_t res = fzl_symt_put( + walker->syms[walker->sym_top - 1], + node->var.identifier->token->value, tok_to_type(node->var.type)); + + fzl_token_t *id_tok = node->var.identifier->token; + size_t const_n = add_const( + walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = id_tok->value, + .as.string.length = id_tok->value_sz}); + + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, const_n); push_op(walker, FZL_OPCODE_STORE); - - // NOTE: ignoring type field for now + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, const_n); current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; break; @@ -280,16 +356,48 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) case FZL_NODETYPE_ASSIGN: { - fzl_walk_res_t lres = walk(walker, node->binary_op.left); - if (lres.error) - return lres; - - push_op(walker, FZL_OPCODE_LOOKUP_ENV); - fzl_walk_res_t rres = walk(walker, node->binary_op.right); if (rres.error) return rres; + if (node->binary_op.left->type == FZL_NODETYPE_DECLARATION) + { + fzl_walk_res_t lres = walk(walker, node->binary_op.left); + if (lres.error) + return lres; + } + else if (node->binary_op.left->type == FZL_NODETYPE_IDENTIFIER) + { + fzl_sym_t sym; + bool res = fzl_symt_get(walker->syms[walker->sym_top - 1], + node->binary_op.left->token->value, &sym); + if (!res) // NOTE: refactor this before implement blocks, fns, etc + { + fzl_token_t *tok = node->binary_op.left->token->value; + snprintf(wlast_err, sizeof(wlast_err), + "name '%s' is not exists at %ld:%ld", (char *)tok->value, + tok->ln, tok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (!is_compatible(sym.type, rres.type)) + { + fzl_token_t *tok = node->binary_op.right->token; + snprintf(wlast_err, sizeof(wlast_err), + "R-Value is not compatible with L-Value at %ld:%ld", + tok->ln, tok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + size_t const_n = add_const( + walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = sym.name, + .as.string.length = + node->binary_op.left->token->value_sz}); + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, const_n); + } + push_op(walker, FZL_OPCODE_WRITE); current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; break; @@ -311,7 +419,9 @@ int fzl_walk(fzl_walker_t *walker) return -1; } + push_op(walker, FZL_OPCODE_MK_ENVTABLE); fzl_walk_res_t result = walk(walker, walker->root); + push_op(walker, FZL_OPCODE_RM_ENVTABLE); if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); diff --git a/src/compiler/utils/fzl_symtable.c b/src/compiler/utils/fzl_symtable.c new file mode 100644 index 0000000..3d4afa8 --- /dev/null +++ b/src/compiler/utils/fzl_symtable.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +fzl_symtable_t *fzl_mksymt() +{ + fzl_symtable_t *tbl = malloc(sizeof(fzl_symtable_t)); + if (!tbl) + return NULL; + + tbl->remainder = 2; + tbl->size = 0; + tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t)); + if (!tbl->symbols) + return NULL; + + return tbl; +} + +fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, + fzl_type_t type) +{ + if (!symt) + return FZL_SYMTABLE_RESULT_ERR; + + for (size_t i = 0; i < symt->size; i++) + if (strcmp(symt->symbols[i].name, name) == 0) + return FZL_SYMTABLE_RESULT_EXISTS; + + if (symt->remainder <= symt->size) + { + symt->remainder *= 2; + symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t)); + } + + symt->symbols[symt->size++] = (fzl_sym_t){.name = name, .type = type}; + return FZL_SYMTABLE_RESULT_OK; +} + +bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res) +{ + if (!symt) + return false; + + for (size_t i = 0; i < symt->size; i++) + { + if (strcmp(symt->symbols[i].name, name) == 0) + { + *res = symt->symbols[i]; + return true; + } + } + + return false; +} + +void fzl_freesymt(fzl_symtable_t *symt) +{ + if (!symt) + return; + + if (symt->symbols) + free(symt->symbols); + + free(symt); +} diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 7175726..480dd14 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -1,3 +1,4 @@ +#include "vm/fzl_vm_env.h" #include #include #include @@ -122,11 +123,87 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { - case FZL_OPCODE_MK_ENVTABLE: case FZL_OPCODE_STORE: - case FZL_OPCODE_WRITE: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected var name in data stack"); + return -1; + } + + fzl_value_t varname = vm->dstack[--vm->dtop]; + fzl_env_storevalue(vm->estack[vm->etop - 1], (char *)varname.as.s.value, + (fzl_value_t){}); + break; + case FZL_OPCODE_WRITE: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in data stack"); + return -1; + } + + fzl_value_t writevar = vm->dstack[--vm->dtop]; + fzl_value_t value = vm->dstack[--vm->dtop]; + + fzl_vm_env_wresult_t wresult = fzl_env_writevalue( + vm->estack[vm->etop - 1], (char *)writevar.as.s.value, value); + if (wresult != FZL_VM_ENV_WRESULT_OK) + { + switch (wresult) + { + case FZL_VM_ENV_WRESULT_ERR: + snprintf(last_err, sizeof(last_err), + "runtime error - failed to write variable"); + return -1; + + case FZL_VM_ENV_WRESULT_ERR_EXISTS: + snprintf(last_err, sizeof(last_err), + "runtime error - redefenition of '%s'", + writevar.as.s.value); + return -1; + + case FZL_VM_ENV_WRESULT_ERR_NOT_FOUND: + snprintf(last_err, sizeof(last_err), + "runtime error - cannot find name '%s'", + writevar.as.s.value); + return -1; + + default: + return -1; + } + } + + vm->dstack[vm->dtop++] = value; + break; + + case FZL_OPCODE_MK_ENVTABLE: + vm->estack[vm->etop++] = fzl_mkenv(); + break; + + case FZL_OPCODE_RM_ENVTABLE: + fzl_freeenv(vm->estack[--vm->etop]); + break; + + case FZL_OPCODE_LOOKUP_ENV: + { + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - LOOKUP_ENV: empty data stack"); + return -1; + } + + fzl_value_t val = vm->dstack[--vm->dtop]; + fzl_value_t env_val = + fzl_env_lookup(vm->estack[vm->etop - 1], (char *)val.as.s.value); + vm->dstack[vm->dtop++] = env_val; + + break; + } + case FZL_OPCODE_CALL_BUILTIN: { uint64_t fn_id = vm->ops[++vm->ip]; @@ -143,6 +220,8 @@ int fzl_vm_run(fzl_vm_t *vm) printf("%lu\n", arg.as.u); else if (arg.type == FZL_TYPE_BOOL) printf("%b\n", arg.as.b); + else if (arg.type == FZL_TYPE_NULL) + printf("\n"); } else { diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c new file mode 100644 index 0000000..e22ebd1 --- /dev/null +++ b/src/vm/fzl_vm_debug.c @@ -0,0 +1,69 @@ +#include "common/fzl_opcodes.h" +#include +#include + +void fzl_vm_print_ops(fzl_vm_t *vm) +{ + if (!vm) + return; + + if (vm->ops_count < 1) + { + printf("no opcodes\n"); + return; + } + + for (size_t i = 0; i < vm->ops_count; i++) + { + switch (vm->ops[i]) + { + case FZL_OPCODE_LOADCONST: + printf("LOADCONST #%lu\n", vm->ops[++i]); + break; + + case FZL_OPCODE_CALL_BUILTIN: + printf("CALL_BUILTIN #%lu\n", vm->ops[++i]); + break; + + case FZL_OPCODE_PUSHNUM: + printf("PUSHNUM %lu\n", vm->ops[++i]); + break; + + case FZL_OPCODE_MK_ENVTABLE: + printf("MK_ENVTABLE\n"); + break; + + case FZL_OPCODE_RM_ENVTABLE: + printf("RM_ENVTABLE\n"); + break; + + case FZL_OPCODE_STORE: + printf("STORE\n"); + break; + + case FZL_OPCODE_LOOKUP_ENV: + printf("LOOKUP_ENV\n"); + break; + + case FZL_OPCODE_WRITE: + printf("WRITE\n"); + break; + + case FZL_OPCODE_ADD: + case FZL_OPCODE_SUB: + case FZL_OPCODE_MUL: + case FZL_OPCODE_DIV: + case FZL_OPCODE_POW: + case FZL_OPCODE_MOD: + printf("Math opcode\n"); + break; + + case FZL_OPCODE_NEG: + printf("NEG\n"); + break; + + default: + printf("Unknown"); + } + } +} diff --git a/src/vm/fzl_vm_env.c b/src/vm/fzl_vm_env.c index f2ff358..974851f 100644 --- a/src/vm/fzl_vm_env.c +++ b/src/vm/fzl_vm_env.c @@ -62,7 +62,7 @@ fzl_vm_env_wresult_t fzl_env_storevalue(fzl_vm_env_t *env, char *name, if (fzl_env_contains(env, name)) return FZL_VM_ENV_WRESULT_ERR_EXISTS; - env->table[env->size] = (fzl_vm_env_entry_t){.name = name, .value = val}; + env->table[env->size++] = (fzl_vm_env_entry_t){.name = name, .value = val}; return FZL_VM_ENV_WRESULT_OK; } diff --git a/src/vm/main.c b/src/vm/main.c index c12a00f..55c85d4 100644 --- a/src/vm/main.c +++ b/src/vm/main.c @@ -9,6 +9,7 @@ typedef struct { char *filepath; bool help; + bool debug; } run_args_t; void print_hint() @@ -28,8 +29,9 @@ run_args_t *parse_args(int argc, char **argv) } run_args_t *args = malloc(sizeof(run_args_t)); - args->filepath = NULL; - args->help = false; + args->filepath = NULL; + args->help = false; + args->debug = false; for (size_t i = 0; i < argc; i++) { @@ -41,6 +43,8 @@ run_args_t *parse_args(int argc, char **argv) args->help = true; return args; } + else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) + args->debug = true; else { printf("Error: unknown flag '%s'.\n", argv[i]); @@ -79,11 +83,16 @@ int main(int argc, char **argv) return -1; } - int result = fzl_vm_run(vm); - if (result != 0) + if (args->debug) + fzl_vm_print_ops(vm); + else { - printf("Error: %s\n", fzl_vm_geterror()); - return -1; + int result = fzl_vm_run(vm); + if (result != 0) + { + printf("Error: %s\n", fzl_vm_geterror()); + return -1; + } } free(args);