From cdf929dfc4d4099f818982a03f40ca60ecb49c9d Mon Sep 17 00:00:00 2001 From: samsonium Date: Sat, 10 Oct 2026 05:38:16 +0300 Subject: [PATCH 01/34] feat: implement vm --- CMakeLists.txt | 7 +- include/vm/fzl_vm.h | 38 +++++++++ src/vm/fzl_vm.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ src/vm/main.c | 90 ++++++++++++++++++++++ 4 files changed, 315 insertions(+), 3 deletions(-) create mode 100644 include/vm/fzl_vm.h create mode 100644 src/vm/fzl_vm.c create mode 100644 src/vm/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0013498..1086f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,8 @@ add_executable(fzc src/compiler/utils/fzl_const_pool.c src/compiler/main.c) -# VM -# add_executable(fzvm -# src/vm/main.c) +# Virtual machine +add_executable(fz + src/vm/fzl_vm.c + src/vm/main.c) diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h new file mode 100644 index 0000000..811c3ea --- /dev/null +++ b/include/vm/fzl_vm.h @@ -0,0 +1,38 @@ +#ifndef FZL_VM_H +#define FZL_VM_H + +#include // NOTE: move outside compiler scope to common +#include +#include + +// VM descriptor +typedef struct +{ + fzl_const_t *consts; + size_t consts_count; + + uint64_t *ops; + size_t ops_count; + + size_t ip; + + fzl_const_t stack[256]; + size_t stack_top; + + fzl_const_t arg_stack[256]; + size_t arg_top; +} fzl_vm_t; + +// Allocate FZL VM +fzl_vm_t *fzl_mkvm(const char *filepath); + +// Load and run code +int fzl_vm_run(fzl_vm_t *vm); + +// Get last VM error message +char *fzl_vm_geterror(); + +// Free VM +void fzl_freevm(fzl_vm_t *vm); + +#endif // FZL_VM_H diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c new file mode 100644 index 0000000..defbf98 --- /dev/null +++ b/src/vm/fzl_vm.c @@ -0,0 +1,183 @@ +#include "common/fzl_builtins.h" +#include "common/fzl_opcodes.h" +#include "compiler/utils/fzl_const_pool.h" +#include +#include +#include +#include +#include + +char last_err[256]; + +static char *expect_hdr = "FZL\0\1"; + +fzl_vm_t *fzl_mkvm(const char *filepath) +{ + fzl_vm_t *vm = malloc(sizeof(fzl_vm_t)); + if (!vm) + { + snprintf(last_err, sizeof(last_err), "failed to allocate vm"); + return NULL; + } + + vm->consts = NULL; + vm->consts_count = 0; + vm->ops = NULL; + vm->ops_count = 0; + vm->ip = 0; + memset(vm->stack, 0, sizeof(vm->stack)); + vm->stack_top = 0; + memset(vm->arg_stack, 0, sizeof(vm->arg_stack)); + vm->arg_top = 0; + + FILE *fd = fopen(filepath, "rb"); + if (!fd) + { + snprintf(last_err, sizeof(last_err), "failed to open file '%s'", filepath); + return NULL; + } + + char hdr[5]; + fread(hdr, sizeof(char), 5, fd); + if (memcmp(hdr, expect_hdr, 5) != 0) + { + snprintf(last_err, sizeof(last_err), "magic check error"); + return NULL; + } + + size_t consts_count, ops_count; + fread(&consts_count, sizeof(size_t), 1, fd); + fread(&ops_count, sizeof(size_t), 1, fd); + + if (ops_count < 1) + { + snprintf(last_err, sizeof(last_err), "there is nothing to do (0 ops)"); + return NULL; + } + + if (consts_count > 0) + { + vm->consts_count = consts_count; + vm->consts = malloc(consts_count * sizeof(fzl_const_t)); + + for (size_t i = 0; i < consts_count; i++) + { + fzl_consttype_t type; + fread(&type, sizeof(fzl_consttype_t), 1, fd); + + if (type == FZL_CONSTTYPE_STRING) + { + size_t length; + char *chars; + + fread(&length, sizeof(size_t), 1, fd); + + chars = malloc(length * sizeof(char) + 1); + fread(chars, sizeof(char), length, fd); + chars[length] = 0; + + vm->consts[i] = (fzl_const_t){ + .type = type, .as.string.length = length, .as.string.chars = chars}; + } + else + { + snprintf(last_err, sizeof(last_err), "unknown const type at index %ld", + i); + return NULL; + } + } + } + + vm->ops_count = ops_count; + vm->ops = malloc(ops_count * sizeof(uint64_t)); + for (size_t i = 0; i < ops_count; i++) + { + uint64_t op; + fread(&op, sizeof(uint64_t), 1, fd); + vm->ops[i] = op; + } + + return vm; +} + +int fzl_vm_run(fzl_vm_t *vm) +{ + if (!vm) + { + snprintf(last_err, sizeof(last_err), "vm is null"); + return -1; + } + + vm->ip = 0; + while (vm->ip < vm->ops_count) + { + uint64_t opcode = vm->ops[vm->ip]; + + switch (opcode) + { + case FZL_OPCODE_CALL_BUILTIN: + uint64_t fn_id = vm->ops[++vm->ip]; + if (fn_id == FZL_BUILTIN_PRINTLINE) + { + fzl_const_t arg = vm->arg_stack[--vm->arg_top]; + printf("%s\n", arg.as.string.chars); + } + else + { + snprintf(last_err, sizeof(last_err), + "runtime error - undefined builtin %lu", fn_id); + return -1; + } + + break; + + case FZL_OPCODE_LOADCONST: + uint64_t cn = vm->ops[++vm->ip]; + if (vm->consts_count <= cn) + { + snprintf(last_err, sizeof(last_err), "runtime error - unknown const"); + return -1; + } + fzl_const_t c = vm->consts[cn]; + vm->stack[vm->stack_top++] = c; + break; + + case FZL_OPCODE_PUSHARG: + if (vm->stack_top < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot push argument: stack is empty"); + return -1; + } + vm->arg_stack[vm->arg_top++] = vm->stack[--vm->stack_top]; + break; + + default: + snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %lu", + opcode); + return -1; + } + + vm->ip++; + } + + return 0; +} + +char *fzl_vm_geterror() { return last_err; } + +void fzl_freevm(fzl_vm_t *vm) +{ + if (!vm) + return; + + for (size_t i = 0; i < vm->consts_count; i++) + { + if (vm->consts[i].type == FZL_CONSTTYPE_STRING) + free((void *)vm->consts[i].as.string.chars); + } + + free(vm->consts); + free(vm->ops); + free(vm); +} diff --git a/src/vm/main.c b/src/vm/main.c new file mode 100644 index 0000000..d1e09c0 --- /dev/null +++ b/src/vm/main.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +typedef struct +{ + char *filepath; + bool help; +} run_args_t; + +void print_hint() +{ + printf("Usage: fz [flags] \n"); + printf("Flags:\n"); + printf("\tno flags available yet :(\n"); +} + +// Parse passed arguments and build args setup structure +run_args_t *parse_args(int argc, char **argv) +{ + if (argc < 2) + { + print_hint(); + return NULL; + } + + run_args_t *args = malloc(sizeof(run_args_t)); + args->filepath = NULL; + args->help = false; + + for (size_t i = 0; i < argc; i++) + { + if (argv[i][0] != '-') + continue; + + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) + { + args->help = true; + return args; + } + else + { + printf("Error: unknown flag '%s'.\n", argv[i]); + printf("Use 'fz --help' to read the usage\n"); + return NULL; + } + } + + if (argv[argc - 1][0] == '-') + { + printf("Error: last argument MUST be a file path\n"); + printf("Use 'fz --help' for help\n"); + return NULL; + } + + args->filepath = argv[argc - 1]; + + return args; +} + +int main(int argc, char **argv) +{ + run_args_t *args = parse_args(argc, argv); + if (!args) + { + return -1; + } + + fzl_vm_t *vm = fzl_mkvm(args->filepath); + if (!vm) + { + 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); + fzl_freevm(vm); + + return 0; +} -- 2.54.0 From 92ec929e9f0974e5c3f73eb35a22bfa3772dbe4a Mon Sep 17 00:00:00 2001 From: samsonium Date: Sat, 10 Oct 2026 19:47:53 +0300 Subject: [PATCH 02/34] fix: help hint and `--h`/`-h` flags --- src/vm/main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vm/main.c b/src/vm/main.c index d1e09c0..c12a00f 100644 --- a/src/vm/main.c +++ b/src/vm/main.c @@ -15,7 +15,7 @@ void print_hint() { printf("Usage: fz [flags] \n"); printf("Flags:\n"); - printf("\tno flags available yet :(\n"); + printf("\t-h\t--help\tprint usage information\n"); } // Parse passed arguments and build args setup structure @@ -65,8 +65,11 @@ int main(int argc, char **argv) { run_args_t *args = parse_args(argc, argv); if (!args) - { return -1; + else if (args->help) + { + print_hint(); + return 0; } fzl_vm_t *vm = fzl_mkvm(args->filepath); -- 2.54.0 From 3f76cdcefbc8c4f55f74ae5a504bef9280da220a Mon Sep 17 00:00:00 2001 From: samsonium Date: Sat, 10 Oct 2026 20:55:39 +0300 Subject: [PATCH 03/34] wip: wokring on grammar and spec --- .gitignore | 1 + docs/grammar.ebnf | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 docs/grammar.ebnf diff --git a/.gitignore b/.gitignore index 889eacd..53455e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /* # Dirs +!/docs !/exl !/include !/src diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf new file mode 100644 index 0000000..7d2d23e --- /dev/null +++ b/docs/grammar.ebnf @@ -0,0 +1,6 @@ +program := statement* +statement := expression +expression := call | string_literal +call := name "(" argument-list? ")" +argument-list := string_literal ("," string_literal)* + -- 2.54.0 From f49a54c878cc947d0ae74b427af6bc8d7ae74d02 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sat, 10 Oct 2026 20:56:21 +0300 Subject: [PATCH 04/34] upd+feat: change source file ext, add new examples - UPD: now using `.fzs` (fromzero-source) file extension instead of `.fzc` - FEAT: add new examples: problematic hello world with unterminated string and empty printline --- exl/{hw.fzl => hw.fzs} | 0 exl/print_empty.fzs | 1 + exl/problematic_hw.fzs | 1 + 3 files changed, 2 insertions(+) rename exl/{hw.fzl => hw.fzs} (100%) create mode 100644 exl/print_empty.fzs create mode 100644 exl/problematic_hw.fzs diff --git a/exl/hw.fzl b/exl/hw.fzs similarity index 100% rename from exl/hw.fzl rename to exl/hw.fzs diff --git a/exl/print_empty.fzs b/exl/print_empty.fzs new file mode 100644 index 0000000..a9baaa0 --- /dev/null +++ b/exl/print_empty.fzs @@ -0,0 +1 @@ +printline("") diff --git a/exl/problematic_hw.fzs b/exl/problematic_hw.fzs new file mode 100644 index 0000000..88c437d --- /dev/null +++ b/exl/problematic_hw.fzs @@ -0,0 +1 @@ +printline("hello) -- 2.54.0 From 1db985ecb5a8afd2e9a3447b1718a76bdf8bca6b Mon Sep 17 00:00:00 2001 From: samsonium Date: Sat, 10 Oct 2026 21:03:23 +0300 Subject: [PATCH 05/34] feat+fix: make compiler STRONGAAA - FEAT: add source code length field in `fzl_reader_t` struct - FIX: check name, lpair (lparen) and rpair (rparen) when lexing call - FIX: check string literal termination - FIX: increment position even on empty string to prevent infinite lexing loop - FIX: add string terminator in `fzl_mktoken` when allocating space for string value - FIX: add check for errors in recursevly called `walk` - FIX: change source code holder from stack-stored `char[8192]` to dynamically allocated with actual file size holder - FIX: correct constant hasing util to hash actual string value instead of link --- include/compiler/fzl_reader.h | 1 + src/compiler/fzl_parser.c | 13 +++++++++++-- src/compiler/fzl_reader.c | 21 +++++++++++++++------ src/compiler/fzl_token.c | 3 ++- src/compiler/fzl_walker.c | 12 ++++++++++-- src/compiler/main.c | 9 +++++++-- src/compiler/utils/fzl_const_pool.c | 2 +- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/include/compiler/fzl_reader.h b/include/compiler/fzl_reader.h index 818459b..73898ee 100644 --- a/include/compiler/fzl_reader.h +++ b/include/compiler/fzl_reader.h @@ -20,6 +20,7 @@ typedef struct fzl_reader_ll_t *last; char *source; + size_t size; size_t position; bool at_end; } fzl_reader_t; diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index b93ab88..704ac2d 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -1,3 +1,4 @@ +#include "compiler/fzl_token.h" #include #include #include @@ -92,7 +93,12 @@ fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence); fzl_node_t *parse_call(fzl_parser_t *parser) { fzl_token_t *tok = consume(parser); // - consume(parser); // ( + if (tok->type != FZL_TOKTYPE_NAME) + return NULL; + + fzl_token_t *lpair = consume(parser); // ( + if (lpair->type != FZL_TOKTYPE_LPAIR) + return NULL; fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, tok); node->list.entry_count = 0; @@ -132,7 +138,10 @@ fzl_node_t *parse_call(fzl_parser_t *parser) } } - consume(parser); + fzl_token_t *rpair = consume(parser); + if (rpair->type != FZL_TOKTYPE_RPAIR) + return NULL; + return node; } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 31f7437..a4c3dcb 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -16,6 +17,7 @@ fzl_reader_t *fzl_mkreader(char *source) reader->first = first_node; reader->last = first_node; reader->source = source; + reader->size = strlen(source); reader->position = 0; reader->at_end = true; @@ -60,18 +62,27 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) return NULL; char open_symbol = reader->source[lpos]; + char close_symbol = '\0'; do { lpos++; size++; - } while (reader->source[lpos] != open_symbol); + + if (reader->source[lpos] == open_symbol) + close_symbol = open_symbol; + + } while (reader->source[lpos] != open_symbol && lpos < reader->size); size++; + if (close_symbol != open_symbol) + return NULL; + if (size == 2) { fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->position, reader->position + 1, "", 0); + reader->position += 2; return tok; } @@ -88,8 +99,7 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) int fzl_read(fzl_reader_t *reader) { - const size_t len = strlen(reader->source); - if (reader->position >= len) + if (reader->position >= reader->size) return 0; do @@ -139,8 +149,7 @@ int fzl_read(fzl_reader_t *reader) linepos++; } - sprintf(last_err, "error parsing token (position %ld of line %ld)", line, - linepos); + sprintf(last_err, "cannot parse token (%ld:%ld)", line, linepos); return -1; } @@ -149,7 +158,7 @@ int fzl_read(fzl_reader_t *reader) reader->last->next = tok_node; reader->last = tok_node; - } while (reader->position < len); + } while (reader->position < reader->size); reader->at_end = true; return 0; diff --git a/src/compiler/fzl_token.c b/src/compiler/fzl_token.c index f11da36..67f6660 100644 --- a/src/compiler/fzl_token.c +++ b/src/compiler/fzl_token.c @@ -5,8 +5,9 @@ fzl_token_t *fzl_mktoken(const fzl_toktype_t type, uint16_t from, uint16_t to, void *value, size_t value_len) { - void *valholder = malloc(value_len); + char *valholder = malloc(value_len + 1); memcpy(valholder, value, value_len); + valholder[value_len] = '\0'; fzl_token_t *token = malloc(sizeof(fzl_token_t)); token->type = type; diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 46ad2cd..e4b3112 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -100,7 +100,8 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) // NOTE: create block env here if (node->list.entry_count > 0) for (size_t i = 0; i < node->list.entry_count; i++) - walk(walker, node->list.entries[i]); + if (!walk(walker, node->list.entries[i])) + return false; // NOTE: destroy block env here break; @@ -109,7 +110,8 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) { for (size_t i = 0; i < node->list.entry_count; i++) { - walk(walker, node->list.entries[i]); + if (!walk(walker, node->list.entries[i])) + return false; push_op(walker, FZL_OPCODE_PUSHARG); } } @@ -146,6 +148,12 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) // Walk entire AST recursevily int fzl_walk(fzl_walker_t *walker) { + if (!walker) + { + snprintf(wlast_err, sizeof(wlast_err), "walker is null"); + return -1; + } + bool result = walk(walker, walker->root); return !result && wlast_err[0] ? -1 : 0; } diff --git a/src/compiler/main.c b/src/compiler/main.c index 46c0689..612cda5 100644 --- a/src/compiler/main.c +++ b/src/compiler/main.c @@ -102,7 +102,7 @@ int main(int argc, char **argv) if (args->verbose) printf("> analyze\n-> reading file..."); - char source[8192]; + char *source; FILE *fd = fopen(args->filepath, "r"); if (!fd) { @@ -114,11 +114,15 @@ int main(int argc, char **argv) size_t fsz = ftell(fd); rewind(fd); + source = malloc(fsz + 1); fread(source, fsz, 1, fd); fclose(fd); + source[fsz] = '\0'; + if (args->verbose) { printf("ok!\n"); + printf("--> Red %ld bytes\n", fsz); printf("-> tokenizing..."); } @@ -218,10 +222,11 @@ int main(int argc, char **argv) if (args->verbose) printf("ok!\n"); + free(source); free(args); fzl_freereader(rd); - fzl_freeparser(parser); fzl_freeast(parser->root); + fzl_freeparser(parser); fzl_freewalker(walker); fzl_freewriter(writer); diff --git a/src/compiler/utils/fzl_const_pool.c b/src/compiler/utils/fzl_const_pool.c index abb0d0b..39c4c28 100644 --- a/src/compiler/utils/fzl_const_pool.c +++ b/src/compiler/utils/fzl_const_pool.c @@ -33,7 +33,7 @@ static uint32_t fzl_hash_const(fzl_const_t c) switch (c.type) { case FZL_CONSTTYPE_STRING: - data = (const uint8_t *)&c.as.string.chars; + data = (const uint8_t *)c.as.string.chars; size = c.as.string.length; break; } -- 2.54.0 From a815cd1549cca3e01483116409eb77e2a3604310 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 02:10:30 +0300 Subject: [PATCH 06/34] feat: add debug target --- .gdbinit | 4 ++++ .gitignore | 1 + Makefile | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .gdbinit diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..c0863a3 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,4 @@ +skip peek +skip consume +skip next + diff --git a/.gitignore b/.gitignore index 53455e4..2db0286 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ # Files !/.clang-format +!/.gdbinit !/.gitignore !/CMakeLists.txt !/Makefile diff --git a/Makefile b/Makefile index 6f024a9..d9bf425 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: configure build +.PHONY: configure build debug-c configure: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug @@ -6,3 +6,6 @@ configure: build: configure cmake --build build +debug-c: build + gdb --args ./build/fzc ${FILE} + -- 2.54.0 From 981603105b5e510b2d0d700cf8b7013161f8d224 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 02:10:37 +0300 Subject: [PATCH 07/34] feat: add math example --- exl/base_math.fzs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 exl/base_math.fzs diff --git a/exl/base_math.fzs b/exl/base_math.fzs new file mode 100644 index 0000000..e02d17c --- /dev/null +++ b/exl/base_math.fzs @@ -0,0 +1,7 @@ +printline(2 + 2) +printline(2 + 2 * 2) +printline(-1 + 1) +printline(2 ^ 2) +printline(1 + 1 * 1 / 1) +printline(4 % 2) + -- 2.54.0 From b9c43c6ad2d04a017c7a0a6fe2da2ab5b4ae8b10 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 02:10:54 +0300 Subject: [PATCH 08/34] feat: implement math in compiler --- include/common/fzl_opcodes.h | 15 +- include/compiler/fzl_parser.h | 34 ++++ include/compiler/fzl_token.h | 21 ++- src/compiler/fzl_debug.c | 23 ++- src/compiler/fzl_parser.c | 346 ++++++++++++++++++++++++++++------ src/compiler/fzl_reader.c | 66 ++++++- src/compiler/fzl_walker.c | 74 +++++++- src/compiler/main.c | 10 +- 8 files changed, 506 insertions(+), 83 deletions(-) diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index aab733f..53976d3 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -4,9 +4,18 @@ // Available opcodes typedef enum { - FZL_OPCODE_LOADCONST, // Load const into T (temp) reg - FZL_OPCODE_PUSHARG, // Push argument from T into args stack - FZL_OPCODE_CALL_BUILTIN // Call builtin function + FZL_OPCODE_LOADCONST, // Load const into T (temp) reg + FZL_OPCODE_PUSHARG, // Push argument from T into args stack + FZL_OPCODE_CALL_BUILTIN, // Call builtin function + FZL_OPCODE_PUSHNUM, // Push number into stack + + FZL_OPCODE_ADD, // Sum two numbers in arg strack + FZL_OPCODE_SUB, // Subract two numbers in arg stack + FZL_OPCODE_MUL, // Multiply two numbers in arg stack + FZL_OPCODE_DIV, // Divide two numbers in arg stack + FZL_OPCODE_POW, // Power first number in arg stack by second + FZL_OPCODE_MOD, // Get module of two numbers in arg stack + FZL_OPCODE_NEG, // Negotiate number in arg stack } fzl_opcode_t; #endif // FZL_OPCODES_H diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index 023ea0d..ce2c31e 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -11,6 +11,17 @@ typedef enum FZL_NODETYPE_BLOCK, FZL_NODETYPE_CALL, FZL_NODETYPE_LITERAL, + FZL_NODETYPE_IDENTIFIER, + + FZL_NODETYPE_ADD, + FZL_NODETYPE_SUB, + FZL_NODETYPE_MUL, + FZL_NODETYPE_DIV, + FZL_NODETYPE_POW, + FZL_NODETYPE_MOD, + + FZL_NODETYPE_UNARY_PLUS, + FZL_NODETYPE_UNARY_MINUS } fzl_nodetype_t; // AST node descriptor @@ -23,11 +34,34 @@ typedef struct fzl_node union { + // Array of nested nodes (for blocks, etc) struct { struct fzl_node **entries; size_t entry_count; } list; + + // Call data + struct + { + struct fzl_node *callee; + + struct fzl_node **args; + size_t args_count; + } call; + + // Binary operations + struct + { + struct fzl_node *left; + struct fzl_node *right; + } binary_op; + + // Unary operations + struct + { + struct fzl_node *operand; + } unary_op; }; } fzl_node_t; diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index 3bc5432..1abf141 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -8,11 +8,22 @@ // Token type typedef enum { - FZL_TOKTYPE_NAME, - FZL_TOKTYPE_LPAIR, - FZL_TOKTYPE_RPAIR, - FZL_TOKTYPE_COMMA, - FZL_TOKTYPE_STR_LITERAL + FZL_TOKTYPE_NAME, // abc + + FZL_TOKTYPE_LPAREN, // ( + FZL_TOKTYPE_RPAREN, // ) + + FZL_TOKTYPE_COMMA, // , + + FZL_TOKTYPE_STR_LITERAL, // "" + FZL_TOKTYPE_NUM_LITERAL, // 123 + + FZL_TOKTYPE_STAR, // * + FZL_TOKTYPE_SLASH, // / + FZL_TOKTYPE_PLUS, // + + FZL_TOKTYPE_MINUS, // - + FZL_TOKTYPE_CARET, // ^ + FZL_TOKTYPE_PERCENT, // % } fzl_toktype_t; // Token descriptor diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index 8744dac..3e40d63 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -1,3 +1,4 @@ +#include "compiler/fzl_parser.h" #include #include #include @@ -32,12 +33,12 @@ void print_node(fzl_node_t *node, int level) switch (node->type) { case FZL_NODETYPE_CALL: - if (node->list.entry_count > 0) + if (node->call.args_count > 0) { printf("%s Call fn '%s' with args:\n", levelbuf, (char *)node->token->value); - for (size_t i = 0; i < node->list.entry_count; i++) - print_node(node->list.entries[i], level + 1); + for (size_t i = 0; i < node->call.args_count; i++) + print_node(node->call.args[i], level + 1); } else printf("%s Call fn '%s' w/o args\n", levelbuf, @@ -62,6 +63,22 @@ void print_node(fzl_node_t *node, int level) print_node(node->list.entries[i], level + 1); break; + case FZL_NODETYPE_ADD: + case FZL_NODETYPE_SUB: + case FZL_NODETYPE_MUL: + case FZL_NODETYPE_DIV: + case FZL_NODETYPE_POW: + case FZL_NODETYPE_MOD: + printf("%s Binary op (%d)\n", levelbuf, node->type); + printf("-%s LEFT:\n", levelbuf); + print_node(node->binary_op.left, level + 2); + printf("-%s RIGHT:\n", levelbuf); + print_node(node->binary_op.right, level + 2); + break; + + case FZL_NODETYPE_IDENTIFIER: + printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value); + break; default: printf("%s Secure node\n", levelbuf); diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 704ac2d..5ba7e26 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -1,5 +1,7 @@ #include "compiler/fzl_token.h" #include +#include +#include #include #include @@ -45,7 +47,7 @@ fzl_node_t *fzl_mknode(fzl_nodetype_t type, fzl_token_t *token) // Get token at cursor static fzl_token_t *peek(fzl_parser_t *parser) { - if (!parser->cursor) + if (!parser || !parser->cursor) return NULL; return parser->cursor->token; } @@ -53,7 +55,7 @@ static fzl_token_t *peek(fzl_parser_t *parser) // Get token at (cursor + 1) static fzl_token_t *next(fzl_parser_t *parser) { - if (!parser->cursor) + if (!parser || !parser->cursor) return NULL; fzl_reader_ll_t *next = parser->cursor->next; @@ -66,105 +68,315 @@ static fzl_token_t *next(fzl_parser_t *parser) // Consume token at cursor and move to the next static fzl_token_t *consume(fzl_parser_t *parser) { - if (!parser->cursor) + if (!parser || !parser->cursor) return NULL; fzl_token_t *tok = parser->cursor->token; parser->cursor = parser->cursor->next; return tok; } +// Check if current token have the same type as specified +static bool is(fzl_parser_t *parser, fzl_toktype_t type) +{ + if (!parser) + return false; + + fzl_token_t *tok = peek(parser); + if (!tok) + return false; + + return tok->type == type; +} + +// Convert arithmetic operator token type to node type +static fzl_nodetype_t math_op_to_type(fzl_toktype_t type) +{ + switch (type) + { + case FZL_TOKTYPE_PLUS: + return FZL_NODETYPE_ADD; + case FZL_TOKTYPE_MINUS: + return FZL_NODETYPE_SUB; + case FZL_TOKTYPE_STAR: + return FZL_NODETYPE_MUL; + case FZL_TOKTYPE_SLASH: + return FZL_NODETYPE_DIV; + case FZL_TOKTYPE_CARET: + return FZL_NODETYPE_POW; + case FZL_TOKTYPE_PERCENT: + return FZL_NODETYPE_MOD; + + default: + return 0; + } +} + +static fzl_nodetype_t unary_op_to_type(fzl_toktype_t type) +{ + switch (type) + { + case FZL_TOKTYPE_PLUS: + return FZL_NODETYPE_UNARY_PLUS; + case FZL_TOKTYPE_MINUS: + return FZL_NODETYPE_UNARY_MINUS; + + default: + return 0; + } +} + +fzl_node_t *parse_expression(fzl_parser_t *parser); + // Parse primary fzl_node_t *parse_primary(fzl_parser_t *parser) { - if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL) + if (!parser || !peek(parser)) + return NULL; + + if (is(parser, FZL_TOKTYPE_STR_LITERAL)) { fzl_token_t *tok = consume(parser); return fzl_mknode(FZL_NODETYPE_LITERAL, tok); } + else if (is(parser, FZL_TOKTYPE_NUM_LITERAL)) + { + fzl_token_t *tok = consume(parser); + return fzl_mknode(FZL_NODETYPE_LITERAL, tok); + } + else if (is(parser, FZL_TOKTYPE_LPAREN)) + { + consume(parser); + fzl_node_t *node = parse_expression(parser); + if (!is(parser, FZL_TOKTYPE_RPAREN)) + { + snprintf(plast_err, sizeof(plast_err), "expected rparen at %d", + parser->cursor->token->i0); + return NULL; + } + consume(parser); + return node; + } + else if (is(parser, FZL_TOKTYPE_NAME)) + { + fzl_token_t *name = consume(parser); + return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); + } snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %d", peek(parser)->i0); return NULL; } -fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence); - -// Parse call expression -fzl_node_t *parse_call(fzl_parser_t *parser) +// Parse postfix expressions (parens, index access, dots) +fzl_node_t *parse_postfix(fzl_parser_t *parser) { - fzl_token_t *tok = consume(parser); // - if (tok->type != FZL_TOKTYPE_NAME) + if (!parser) return NULL; - fzl_token_t *lpair = consume(parser); // ( - if (lpair->type != FZL_TOKTYPE_LPAIR) + fzl_node_t *left = parse_primary(parser); + fzl_node_t *right = NULL; + + if (!left) + { + snprintf(plast_err, sizeof(plast_err), "failed to parse postfix at %d", + peek(parser)->i0); return NULL; + } - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, tok); - node->list.entry_count = 0; - - size_t capacity = 4; - node->list.entries = malloc(capacity * sizeof(fzl_node_t *)); - node->list.entry_count = 0; - - if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_RPAIR) + while (is(parser, FZL_TOKTYPE_LPAREN)) { consume(parser); - return node; - } - while (peek(parser) && peek(parser)->type != FZL_TOKTYPE_RPAIR) - { - fzl_node_t *arg = parse_expression(parser, 0); - if (!arg) - return NULL; + size_t args_capacity = 2; + size_t args_count = 0; + fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); - if (node->list.entry_count == capacity) + while (!is(parser, FZL_TOKTYPE_RPAREN)) { - capacity *= 2; - node->list.entries = - realloc(node->list.entries, capacity * sizeof(fzl_node_t *)); + fzl_node_t *arg = parse_expression(parser); + if (!arg) + { + free(args); + return NULL; + } + + if (args_capacity <= args_count) + { + args_capacity *= 2; + args = realloc(args, args_capacity * sizeof(fzl_node_t)); + } + + args[args_count] = arg; + args_count++; + + if (is(parser, FZL_TOKTYPE_COMMA)) + consume(parser); } - node->list.entries[node->list.entry_count++] = arg; - - if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_COMMA) - consume(parser); - else if (peek(parser) && peek(parser)->type != FZL_TOKTYPE_RPAIR) + fzl_token_t *rparen = consume(parser); + if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN) { + free(args); snprintf(plast_err, sizeof(plast_err), - "expected ',' or ')' after arg list"); + "expected ')' after argument list at %d", peek(parser)->i0); return NULL; } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); + node->call.callee = left; + node->call.args = args; + node->call.args_count = args_count; + + left = node; } - fzl_token_t *rpair = consume(parser); - if (rpair->type != FZL_TOKTYPE_RPAIR) + return left; +} + +// Parse unary operations +fzl_node_t *parse_unary(fzl_parser_t *parser) +{ + if (!parser) return NULL; - return node; + if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS)) + return parse_postfix(parser); + + fzl_token_t *op = consume(parser); + fzl_node_t *operand = parse_unary(parser); + + if (!operand) + return NULL; + + fzl_node_t *result = fzl_mknode(unary_op_to_type(op->type), op); + result->unary_op.operand = operand; + + return result; +} + +fzl_node_t *parse_power(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_unary(parser); + fzl_node_t *right = NULL; + + if (!left) + { + snprintf(plast_err, sizeof(plast_err), "failed to parse power at %d", + peek(parser)->i0); + return NULL; + } + + if (is(parser, FZL_TOKTYPE_CARET)) + { + fzl_token_t *caret = consume(parser); + + right = parse_unary(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), + "failed to parse right side of power at %d", peek(parser)->i0); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_POW, caret); + node->binary_op.left = left; + node->binary_op.right = right; + + left = node; + } + + return left; +} + +// Parse FACTOR: multiplication and division +fzl_node_t *parse_factor(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_power(parser); + fzl_node_t *right = NULL; + + if (!left) + { + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), "invalid expression at %u", + parser->cursor->token->i0); + return NULL; + } + + while (is(parser, FZL_TOKTYPE_STAR) || is(parser, FZL_TOKTYPE_SLASH) || + is(parser, FZL_TOKTYPE_PERCENT)) + { + fzl_token_t *op = consume(parser); + + right = parse_factor(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), + "unfinished factor (mul/div) expression at %u", left->token->i0); + return NULL; + } + + fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; +} + +// Parse TERM: addition and subtraction +fzl_node_t *parse_term(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_factor(parser); + fzl_node_t *right = NULL; + + if (!left) + { + snprintf(plast_err, sizeof(plast_err), "invalid expression at %u", + parser->cursor->token->i0); + return NULL; + } + + while (is(parser, FZL_TOKTYPE_PLUS) || is(parser, FZL_TOKTYPE_MINUS)) + { + fzl_token_t *op = consume(parser); + + right = parse_factor(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), + "unfinished term (add/sub) expression at %u", left->token->i0); + return NULL; + } + + fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; } // Expression parsing -fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence) +fzl_node_t *parse_expression(fzl_parser_t *parser) { - if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_NAME) - { - if (next(parser) && next(parser)->type == FZL_TOKTYPE_LPAIR) - return parse_call(parser); - } - else if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL) - return parse_primary(parser); - - snprintf(plast_err, sizeof(plast_err), "failed to parse expression at %d", - peek(parser)->i0); - return NULL; + return parse_term(parser); } // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { - return parse_expression(parser, 0); + return parse_expression(parser); } // Internal parsing entry point @@ -229,7 +441,7 @@ void fzl_freeast(fzl_node_t *root) while (root) { - if (root->type == FZL_NODETYPE_BLOCK || root->type == FZL_NODETYPE_CALL) + if (root->type == FZL_NODETYPE_BLOCK) { if (root->list.entries) { @@ -240,6 +452,32 @@ void fzl_freeast(fzl_node_t *root) free(root->list.entries); } } + else if (root->type == FZL_NODETYPE_CALL) + { + if (root->call.args_count) + { + for (size_t i = 0; i < root->call.args_count; i++) + if (root->call.args[i]) + fzl_freeast(root->call.args[i]); + + free(root->call.args); + } + } + else if (root->type == FZL_NODETYPE_ADD || root->type == FZL_NODETYPE_SUB || + root->type == FZL_NODETYPE_MUL || root->type == FZL_NODETYPE_DIV || + root->type == FZL_NODETYPE_POW || root->type == FZL_NODETYPE_MOD) + { + if (root->binary_op.left) + fzl_freeast(root->binary_op.left); + if (root->binary_op.right) + fzl_freeast(root->binary_op.right); + } + else if (root->type == FZL_NODETYPE_UNARY_PLUS || + root->type == FZL_NODETYPE_UNARY_MINUS) + { + if (root->unary_op.operand) + fzl_freeast(root->unary_op.operand); + } fzl_freenode(root); root = root->next; diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index a4c3dcb..35c6c1e 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -1,5 +1,7 @@ +#include "compiler/fzl_token.h" #include #include +#include #include #include #include @@ -97,6 +99,44 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) return tok; } +// Parse numeric literal +fzl_token_t *parse_number(fzl_reader_t *reader) +{ + if (!reader) + return NULL; + + size_t size = 0; + size_t lpos = reader->position; + + do + { + size++; + lpos++; + } while ((reader->source[lpos] >= '0' && reader->source[lpos] <= '9') && + lpos < reader->size); + + char *value = malloc(size * sizeof(char) + 1); + memcpy(value, reader->source + reader->position, size); + value[size] = '\0'; + + fzl_token_t *tok = + fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, reader->position, lpos, value, size); + free(value); + + if (!tok) + return NULL; + + reader->position += size; + return tok; +} + +// Add single-char token +fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type) +{ + size_t p = reader->position++; + return fzl_mktoken(type, p, p, NULL, 0); +} + int fzl_read(fzl_reader_t *reader) { if (reader->position >= reader->size) @@ -117,17 +157,27 @@ int fzl_read(fzl_reader_t *reader) if (curr == '\'' || curr == '"') token = parse_string_literal(reader); else if (curr == '(') - { - token = fzl_mktoken(FZL_TOKTYPE_LPAIR, p, p, NULL, 0); - reader->position++; - } + token = add_single(reader, FZL_TOKTYPE_LPAREN); else if (curr == ')') - { - token = fzl_mktoken(FZL_TOKTYPE_RPAIR, p, p, NULL, 0); - reader->position++; - } + token = add_single(reader, FZL_TOKTYPE_RPAREN); + else if (curr == ',') + token = add_single(reader, FZL_TOKTYPE_COMMA); + else if (curr == '*') + token = add_single(reader, FZL_TOKTYPE_STAR); + else if (curr == '/') + token = add_single(reader, FZL_TOKTYPE_SLASH); + else if (curr == '+') + token = add_single(reader, FZL_TOKTYPE_PLUS); + else if (curr == '-') + token = add_single(reader, FZL_TOKTYPE_MINUS); + else if (curr == '^') + token = add_single(reader, FZL_TOKTYPE_CARET); + else if (curr == '%') + token = add_single(reader, FZL_TOKTYPE_PERCENT); else if (curr >= 'A' && curr <= 'Z' || curr >= 'a' && curr <= 'z') token = parse_name(reader); + else if (curr >= '0' && curr <= '9') + token = parse_number(reader); else { sprintf(last_err, "undefined token '%c'", curr); diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index e4b3112..f26726b 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -108,9 +109,9 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) case FZL_NODETYPE_CALL: if (node->list.entry_count > 0) { - for (size_t i = 0; i < node->list.entry_count; i++) + for (size_t i = 0; i < node->call.args_count; i++) { - if (!walk(walker, node->list.entries[i])) + if (!walk(walker, node->call.args[i])) return false; push_op(walker, FZL_OPCODE_PUSHARG); } @@ -129,13 +130,70 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) break; case FZL_NODETYPE_LITERAL: - size_t n = add_const( - walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, - .as.string.chars = node->token->value, - .as.string.length = node->token->value_sz}); + if (node->token->type == FZL_TOKTYPE_STR_LITERAL) + { + size_t n = add_const( + walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = node->token->value, + .as.string.length = node->token->value_sz}); - push_op(walker, FZL_OPCODE_LOADCONST); - push_word(walker, n); + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, n); + } + else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL) + { + push_op(walker, FZL_OPCODE_PUSHNUM); + + uint64_t num = strtol(node->token->value, NULL, 10); + push_word(walker, num); + } + break; + + case FZL_NODETYPE_ADD: + case FZL_NODETYPE_SUB: + case FZL_NODETYPE_MUL: + case FZL_NODETYPE_DIV: + case FZL_NODETYPE_POW: + case FZL_NODETYPE_MOD: + if (!walk(walker, node->binary_op.left)) + return false; + push_op(walker, FZL_OPCODE_PUSHARG); + + if (!walk(walker, node->binary_op.right)) + return false; + push_op(walker, FZL_OPCODE_PUSHARG); + + switch (node->type) + { + case FZL_NODETYPE_ADD: + push_op(walker, FZL_OPCODE_ADD); + break; + case FZL_NODETYPE_SUB: + push_op(walker, FZL_OPCODE_SUB); + break; + case FZL_NODETYPE_MUL: + push_op(walker, FZL_OPCODE_MUL); + break; + case FZL_NODETYPE_DIV: + push_op(walker, FZL_OPCODE_DIV); + break; + case FZL_NODETYPE_POW: + push_op(walker, FZL_OPCODE_POW); + break; + case FZL_NODETYPE_MOD: + push_op(walker, FZL_OPCODE_MOD); + break; + + default: + return false; + } + break; + + case FZL_NODETYPE_UNARY_MINUS: + if (!walk(walker, node->unary_op.operand)) + return false; + + push_op(walker, FZL_OPCODE_NEG); break; } diff --git a/src/compiler/main.c b/src/compiler/main.c index 612cda5..e96d586 100644 --- a/src/compiler/main.c +++ b/src/compiler/main.c @@ -193,8 +193,14 @@ int main(int argc, char **argv) printf("---> %s\n", walker->consts->array[i].as.string.chars); printf("--> Ops:\n"); + printf("---> "); for (size_t i = 0; i < walker->op_count; i++) - printf("---> %lu\n", walker->ops[i]); + { + if (i > 0 && i < walker->op_count) + printf("->"); + printf("%lu", walker->ops[i]); + } + printf("\n"); printf("-> preparing final write\n"); } @@ -207,7 +213,7 @@ int main(int argc, char **argv) if (args->verbose) { - printf("-> output file path is: %s", outpath); + printf("-> output file path is: %s\n", outpath); printf("-> writing file..."); } -- 2.54.0 From 4577b541c3e1a3c553dd18cd1c3f03e7ae23655d Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 03:12:27 +0300 Subject: [PATCH 09/34] feat: add debug-vm target --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d9bf425..29f7c94 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: configure build debug-c +.PHONY: configure build debug-c debug-vm configure: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug @@ -9,3 +9,6 @@ build: configure debug-c: build gdb --args ./build/fzc ${FILE} +debug-vm: build + gdb --args ./build/fz ${FILE} + -- 2.54.0 From 8696d5bfe7092359b085e4399061c4791e55a31b Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 03:12:35 +0300 Subject: [PATCH 10/34] feat: implement math in vm --- exl/base_math.fzs | 2 +- include/compiler/utils/fzl_const_pool.h | 8 +- src/compiler/fzl_walker.c | 1 + src/vm/fzl_vm.c | 123 ++++++++++++++++++++++-- 4 files changed, 126 insertions(+), 8 deletions(-) diff --git a/exl/base_math.fzs b/exl/base_math.fzs index e02d17c..b5e368c 100644 --- a/exl/base_math.fzs +++ b/exl/base_math.fzs @@ -1,7 +1,7 @@ -printline(2 + 2) printline(2 + 2 * 2) printline(-1 + 1) printline(2 ^ 2) printline(1 + 1 * 1 / 1) printline(4 % 2) +printline(10 / 2) diff --git a/include/compiler/utils/fzl_const_pool.h b/include/compiler/utils/fzl_const_pool.h index 402a934..0d1b128 100644 --- a/include/compiler/utils/fzl_const_pool.h +++ b/include/compiler/utils/fzl_const_pool.h @@ -8,7 +8,8 @@ // Constant types typedef enum { - FZL_CONSTTYPE_STRING + FZL_CONSTTYPE_STRING, + FZL_CONSTTYPE_NUMBER } fzl_consttype_t; // Constant descriptor @@ -23,6 +24,11 @@ typedef struct const char *chars; size_t length; } string; + + struct + { + uint64_t value; + } num; } as; } fzl_const_t; diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index f26726b..8b20abf 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -193,6 +193,7 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) if (!walk(walker, node->unary_op.operand)) return false; + push_op(walker, FZL_OPCODE_PUSHARG); push_op(walker, FZL_OPCODE_NEG); break; } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index defbf98..dd4c14f 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -1,6 +1,6 @@ -#include "common/fzl_builtins.h" -#include "common/fzl_opcodes.h" -#include "compiler/utils/fzl_const_pool.h" +#include +#include +#include #include #include #include @@ -111,7 +111,7 @@ int fzl_vm_run(fzl_vm_t *vm) vm->ip = 0; while (vm->ip < vm->ops_count) { - uint64_t opcode = vm->ops[vm->ip]; + fzl_opcode_t opcode = vm->ops[vm->ip]; switch (opcode) { @@ -120,7 +120,10 @@ int fzl_vm_run(fzl_vm_t *vm) if (fn_id == FZL_BUILTIN_PRINTLINE) { fzl_const_t arg = vm->arg_stack[--vm->arg_top]; - printf("%s\n", arg.as.string.chars); + if (arg.type == FZL_CONSTTYPE_STRING) + printf("%s\n", arg.as.string.chars); + else if (arg.type == FZL_CONSTTYPE_NUMBER) + printf("%lu\n", arg.as.num.value); } else { @@ -152,8 +155,116 @@ int fzl_vm_run(fzl_vm_t *vm) vm->arg_stack[vm->arg_top++] = vm->stack[--vm->stack_top]; break; + case FZL_OPCODE_ADD: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t add1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t add2 = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, + .as.num.value = add1.as.num.value + add2.as.num.value}; + break; + + case FZL_OPCODE_SUB: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t sub1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t sub2 = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, + .as.num.value = sub1.as.num.value - sub2.as.num.value}; + break; + + case FZL_OPCODE_MUL: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t mul1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t mul2 = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, + .as.num.value = mul1.as.num.value * mul2.as.num.value}; + break; + + case FZL_OPCODE_DIV: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t div1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t div2 = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, + .as.num.value = div2.as.num.value / div1.as.num.value}; + break; + + case FZL_OPCODE_POW: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t pow1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t pow2 = vm->arg_stack[--vm->arg_top]; + + uint64_t pow_n = pow1.as.num.value; + uint64_t pow_res = pow_n; + uint64_t pow_times = pow2.as.num.value; + for (size_t i = 1; i < pow_times; i++) + pow_res *= pow_n; + + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = pow_res}; + break; + + case FZL_OPCODE_MOD: + if (vm->arg_top < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in argstack"); + return -1; + } + fzl_const_t mod1 = vm->arg_stack[--vm->arg_top]; + fzl_const_t mod2 = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, + .as.num.value = mod2.as.num.value % mod1.as.num.value}; + break; + + case FZL_OPCODE_NEG: + if (vm->arg_top < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in argstack"); + return -1; + } + fzl_const_t neg_n = vm->arg_stack[--vm->arg_top]; + vm->stack[vm->stack_top++] = (fzl_const_t){ + .type = FZL_CONSTTYPE_NUMBER, .as.num.value = -neg_n.as.num.value}; + break; + + case FZL_OPCODE_PUSHNUM: + uint64_t push_n = vm->ops[++vm->ip]; + vm->stack[vm->stack_top++] = + (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = push_n}; + break; + default: - snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %lu", + snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %u", opcode); return -1; } -- 2.54.0 From 96cd855c5d91ae55d8ec53ef309390af912802bb Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 03:59:15 +0300 Subject: [PATCH 11/34] upd: change binary file default output extension --- src/compiler/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/main.c b/src/compiler/main.c index e96d586..f8b52e3 100644 --- a/src/compiler/main.c +++ b/src/compiler/main.c @@ -209,7 +209,7 @@ int main(int argc, char **argv) if (args->outpath) strcpy(outpath, args->outpath); else - strcpy(outpath, "a.fzlc"); + strcpy(outpath, "a.fzb"); if (args->verbose) { -- 2.54.0 From e01bf6e4ab85cae6cc76af9709a707f411960868 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 03:59:19 +0300 Subject: [PATCH 12/34] feat: add README --- .gitignore | 1 + README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ README.ru.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 README.ru.md diff --git a/.gitignore b/.gitignore index 2db0286..bd3a3a7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ !/CMakeLists.txt !/Makefile !/README.md +!/README.ru.md diff --git a/README.md b/README.md index e69de29..21d6de7 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,49 @@ +# FromZero / Language + +[README in Russian](./README.ru.md) + +**FromZero/Language** is a package consisting of a JIT compiler and a virtual machine for the FZL language. + +## Features + +At the moment, the language has the following capabilities: + +- Builtin `printline` function for printing argument to stdout +- String and numeric literals +- Arithmetic operations: addition, subtraction, multiplication, division, modulo, power + + +## File extensions + +Standard file extensions: + +- `.fzs` - source code +- `.fzb` - compiled binary file + + +## Building + +1. Clone the repository: `git clone https://git.samsonium.dev/samsonium/fz-lang.git` +2. Build: `make build` + +After this, a "build" directory will appear in the project root. It will contain two executable files: + +- `fzc` - a compiler +- `fz` - virtual machine + +Both executables have usage help that can be displayed using `--help` flag (or `-h`). + + +## Development guideline + +You need a code editor with Clang LSP support because of `.clang-format` file in the project root. +This file provides set up codestyle rules. + +There is two targets in Makefile for debugging purposes: `build-c` for the compiler and `build-vm` for the VM. +Each target can be called with `FILE` variable. For example: + + +```shell +$ make debug-c FILE=exl/hw.fzs +``` + diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..0307cbd --- /dev/null +++ b/README.ru.md @@ -0,0 +1,48 @@ +# FromZero / Language + +[README in English](./README.md) + +**FromZero/Language** - пакет из JIT-компилятора и виртуальной машины для языка FZL. + +## Текущие возможности + +На данный момент язык имеет: + +- Встроенную функцию `printline` для вывода аргумента в stdout +- Строковый и числовой литералы +- Арифметические операции: сложение, вычитание, умножение, деление, модуло, возведение в степень + + +## Расширения файлов + +Стандартными для языка являются два расширения файлов: + +- `.fzs` - исходный код +- `.fzb` - скомпилированный бинарный файл + + +## Инструкции по сборке + +1. Склонировать проект: `git clone https://git.samsonium.dev/samsonium/fz-lang.git` +2. Собрать проект: `make build` + +После этого в корне появится директория **"build"**. В ней будут лежать два скомпилированных бинарных файла: + +- `fzc` - компилятор +- `fz` - виртуальная машина + +Оба бинарных файла предоставляют справку по использованию при запуске с флагом `--help` (или `-h`). + + +## Гайдлайн по разработке + +Желательно иметь редактор кода, поддерживающий Clang LSP, так как в проекте есть готовые настройки для +форматтера в виде файла `.clang-format`. + +Для дебаггинга есть два таргета в CMake: `build-c` для компилятора и `build-vm` для виртуальной машины. +Оба таргета позволяют указать переменную с путём файла. Например, так: + +```shell +$ make debug-c FILE=exl/hw.fzs +``` + -- 2.54.0 From 028c8270f75ac0d6648470774a5e4b8408cc5e5e Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 06:00:26 +0300 Subject: [PATCH 13/34] ref: merge stack and arg_stack into single stack --- include/common/fzl_opcodes.h | 1 - include/common/fzl_types.h | 10 ++++++ include/vm/fzl_vm.h | 3 -- src/compiler/fzl_walker.c | 4 --- src/vm/fzl_vm.c | 68 +++++++++++++++--------------------- 5 files changed, 38 insertions(+), 48 deletions(-) create mode 100644 include/common/fzl_types.h diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 53976d3..625883b 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -5,7 +5,6 @@ typedef enum { FZL_OPCODE_LOADCONST, // Load const into T (temp) reg - FZL_OPCODE_PUSHARG, // Push argument from T into args stack FZL_OPCODE_CALL_BUILTIN, // Call builtin function FZL_OPCODE_PUSHNUM, // Push number into stack diff --git a/include/common/fzl_types.h b/include/common/fzl_types.h new file mode 100644 index 0000000..82fe549 --- /dev/null +++ b/include/common/fzl_types.h @@ -0,0 +1,10 @@ +#ifndef FZL_TYPES_H +#define FZL_TYPES_H + +typedef enum +{ + FZL_TYPE_STRING, + FZL_TYPE_NUMBER +} fzl_type_t; + +#endif // FZL_TYPES_H diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index 811c3ea..6d8e54e 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -18,9 +18,6 @@ typedef struct fzl_const_t stack[256]; size_t stack_top; - - fzl_const_t arg_stack[256]; - size_t arg_top; } fzl_vm_t; // Allocate FZL VM diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 8b20abf..87aff38 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -113,7 +113,6 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) { if (!walk(walker, node->call.args[i])) return false; - push_op(walker, FZL_OPCODE_PUSHARG); } } // NOTE: you need to implement dynamic names call @@ -157,11 +156,9 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) case FZL_NODETYPE_MOD: if (!walk(walker, node->binary_op.left)) return false; - push_op(walker, FZL_OPCODE_PUSHARG); if (!walk(walker, node->binary_op.right)) return false; - push_op(walker, FZL_OPCODE_PUSHARG); switch (node->type) { @@ -193,7 +190,6 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node) if (!walk(walker, node->unary_op.operand)) return false; - push_op(walker, FZL_OPCODE_PUSHARG); push_op(walker, FZL_OPCODE_NEG); break; } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index dd4c14f..d666bd2 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -27,8 +27,6 @@ fzl_vm_t *fzl_mkvm(const char *filepath) vm->ip = 0; memset(vm->stack, 0, sizeof(vm->stack)); vm->stack_top = 0; - memset(vm->arg_stack, 0, sizeof(vm->arg_stack)); - vm->arg_top = 0; FILE *fd = fopen(filepath, "rb"); if (!fd) @@ -119,7 +117,7 @@ int fzl_vm_run(fzl_vm_t *vm) uint64_t fn_id = vm->ops[++vm->ip]; if (fn_id == FZL_BUILTIN_PRINTLINE) { - fzl_const_t arg = vm->arg_stack[--vm->arg_top]; + fzl_const_t arg = vm->stack[--vm->stack_top]; if (arg.type == FZL_CONSTTYPE_STRING) printf("%s\n", arg.as.string.chars); else if (arg.type == FZL_CONSTTYPE_NUMBER) @@ -145,81 +143,71 @@ int fzl_vm_run(fzl_vm_t *vm) vm->stack[vm->stack_top++] = c; break; - case FZL_OPCODE_PUSHARG: - if (vm->stack_top < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - cannot push argument: stack is empty"); - return -1; - } - vm->arg_stack[vm->arg_top++] = vm->stack[--vm->stack_top]; - break; - case FZL_OPCODE_ADD: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t add1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t add2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t add1 = vm->stack[--vm->stack_top]; + fzl_const_t add2 = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = add1.as.num.value + add2.as.num.value}; break; case FZL_OPCODE_SUB: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t sub1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t sub2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t sub1 = vm->stack[--vm->stack_top]; + fzl_const_t sub2 = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = sub1.as.num.value - sub2.as.num.value}; break; case FZL_OPCODE_MUL: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t mul1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t mul2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t mul1 = vm->stack[--vm->stack_top]; + fzl_const_t mul2 = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = mul1.as.num.value * mul2.as.num.value}; break; case FZL_OPCODE_DIV: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t div1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t div2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t div1 = vm->stack[--vm->stack_top]; + fzl_const_t div2 = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = div2.as.num.value / div1.as.num.value}; break; case FZL_OPCODE_POW: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t pow1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t pow2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t pow1 = vm->stack[--vm->stack_top]; + fzl_const_t pow2 = vm->stack[--vm->stack_top]; uint64_t pow_n = pow1.as.num.value; uint64_t pow_res = pow_n; @@ -232,27 +220,27 @@ int fzl_vm_run(fzl_vm_t *vm) break; case FZL_OPCODE_MOD: - if (vm->arg_top < 2) + if (vm->stack_top < 2) { snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in argstack"); + "runtime error - expected 2 values in stack"); return -1; } - fzl_const_t mod1 = vm->arg_stack[--vm->arg_top]; - fzl_const_t mod2 = vm->arg_stack[--vm->arg_top]; + fzl_const_t mod1 = vm->stack[--vm->stack_top]; + fzl_const_t mod2 = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = mod2.as.num.value % mod1.as.num.value}; break; case FZL_OPCODE_NEG: - if (vm->arg_top < 1) + if (vm->stack_top < 1) { snprintf(last_err, sizeof(last_err), - "runtime error - expected value in argstack"); + "runtime error - expected value in stack"); return -1; } - fzl_const_t neg_n = vm->arg_stack[--vm->arg_top]; + fzl_const_t neg_n = vm->stack[--vm->stack_top]; vm->stack[vm->stack_top++] = (fzl_const_t){ .type = FZL_CONSTTYPE_NUMBER, .as.num.value = -neg_n.as.num.value}; break; -- 2.54.0 From e3b8ec6b7d70921ddf00a6d7a83389c5e1406fb6 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 08:51:05 +0300 Subject: [PATCH 14/34] upd: update fmt Add rules for more comfortable work with switch-case blocks --- .clang-format | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-format b/.clang-format index e8f8d8a..68a9601 100644 --- a/.clang-format +++ b/.clang-format @@ -5,3 +5,5 @@ BreakBeforeBraces: Allman IndentWidth: 2 TabWidth: 2 UseTab: Never +AlignConsecutiveAssignments: Consecutive +IndentCaseLabels: True -- 2.54.0 From 83e20d78f3f32f7b10de81fcf60be71bec81fefc Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 08:51:54 +0300 Subject: [PATCH 15/34] feat+ref: refactor + new types --- include/common/fzl_opcodes.h | 20 +-- include/common/fzl_types.h | 17 +- include/common/fzl_value.h | 42 +++++ include/compiler/fzl_parser.h | 5 +- include/compiler/fzl_reader.h | 4 +- include/compiler/fzl_token.h | 10 +- include/compiler/fzl_walker.h | 11 ++ include/vm/fzl_vm.h | 13 +- src/compiler/fzl_parser.c | 94 +++++----- src/compiler/fzl_reader.c | 97 ++++++----- src/compiler/fzl_token.c | 13 +- src/compiler/fzl_walker.c | 230 +++++++++++++++--------- src/vm/fzl_vm.c | 317 ++++++++++++++++++---------------- 13 files changed, 532 insertions(+), 341 deletions(-) create mode 100644 include/common/fzl_value.h diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 625883b..cd1c0e2 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -4,17 +4,17 @@ // Available opcodes typedef enum { - FZL_OPCODE_LOADCONST, // Load const into T (temp) reg - FZL_OPCODE_CALL_BUILTIN, // Call builtin function - FZL_OPCODE_PUSHNUM, // Push number into stack + FZL_OPCODE_LOADCONST = 0xF00001, // Load const into T (temp) reg + FZL_OPCODE_CALL_BUILTIN = 0xF00002, // Call builtin function + FZL_OPCODE_PUSHNUM = 0xF00003, // Push number into stack - FZL_OPCODE_ADD, // Sum two numbers in arg strack - FZL_OPCODE_SUB, // Subract two numbers in arg stack - FZL_OPCODE_MUL, // Multiply two numbers in arg stack - FZL_OPCODE_DIV, // Divide two numbers in arg stack - FZL_OPCODE_POW, // Power first number in arg stack by second - FZL_OPCODE_MOD, // Get module of two numbers in arg stack - FZL_OPCODE_NEG, // Negotiate number in arg stack + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in arg strack + FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in arg stack + FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in arg stack + FZL_OPCODE_DIV = 0xA00004, // Divide two numbers in arg stack + FZL_OPCODE_POW = 0xA00005, // Power first number in arg stack by second + FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in arg stack + FZL_OPCODE_NEG = 0xA00007, // Negotiate number in arg stack } fzl_opcode_t; #endif // FZL_OPCODES_H diff --git a/include/common/fzl_types.h b/include/common/fzl_types.h index 82fe549..cde0312 100644 --- a/include/common/fzl_types.h +++ b/include/common/fzl_types.h @@ -3,8 +3,23 @@ typedef enum { + FZL_TYPE_NULL, + FZL_TYPE_STRING, - FZL_TYPE_NUMBER + FZL_TYPE_CHAR, + + FZL_TYPE_INT8, + FZL_TYPE_UINT8, + FZL_TYPE_INT16, + FZL_TYPE_UINT16, + FZL_TYPE_INT32, + 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_t; #endif // FZL_TYPES_H diff --git a/include/common/fzl_value.h b/include/common/fzl_value.h new file mode 100644 index 0000000..df8d68c --- /dev/null +++ b/include/common/fzl_value.h @@ -0,0 +1,42 @@ +#ifndef FZL_VALUE_H +#define FZL_VALUE_H + +#include +#include +#include +#include + +// Value container descriptor +typedef struct +{ + fzl_type_t type; + + union + { + struct + { + const char *value; + size_t length; + } s; + + char c; + bool b; + + int8_t i8; + uint8_t u8; + + int16_t i16; + uint16_t u16; + + int32_t i32; + uint32_t u32; + + int64_t i64; + uint64_t u64; + + intptr_t i; + uintptr_t u; + } as; +} fzl_value_t; + +#endif // FZL_VALUE_H diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index ce2c31e..a0064e2 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -21,7 +21,10 @@ typedef enum FZL_NODETYPE_MOD, FZL_NODETYPE_UNARY_PLUS, - FZL_NODETYPE_UNARY_MINUS + FZL_NODETYPE_UNARY_MINUS, + + FZL_NODETYPE_DECLARATION, + FZL_NODETYPE_ASSIGN } fzl_nodetype_t; // AST node descriptor diff --git a/include/compiler/fzl_reader.h b/include/compiler/fzl_reader.h index 73898ee..2a85dac 100644 --- a/include/compiler/fzl_reader.h +++ b/include/compiler/fzl_reader.h @@ -21,8 +21,10 @@ typedef struct char *source; size_t size; + size_t position; - bool at_end; + size_t line_start; + size_t line_n; } fzl_reader_t; // Allocate new reader diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index 1abf141..a289fe5 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -9,11 +9,13 @@ typedef enum { FZL_TOKTYPE_NAME, // abc + FZL_TOKTYPE_TYPE, // i16 FZL_TOKTYPE_LPAREN, // ( FZL_TOKTYPE_RPAREN, // ) FZL_TOKTYPE_COMMA, // , + FZL_TOKTYPE_DOT, // . FZL_TOKTYPE_STR_LITERAL, // "" FZL_TOKTYPE_NUM_LITERAL, // 123 @@ -24,21 +26,23 @@ typedef enum FZL_TOKTYPE_MINUS, // - FZL_TOKTYPE_CARET, // ^ FZL_TOKTYPE_PERCENT, // % + + FZL_TOKTYPE_EQ // = } fzl_toktype_t; // Token descriptor typedef struct { fzl_toktype_t type; - uint16_t i0; - uint16_t i1; + size_t ln; + size_t cn; bool store_value; void *value; uint16_t value_sz; } fzl_token_t; // Allocate new token -fzl_token_t *fzl_mktoken(const fzl_toktype_t type, uint16_t from, uint16_t to, +fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col, void *value, size_t value_sz); // Free token and its contents diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index 0300873..cd733c7 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -2,10 +2,21 @@ #define FZL_WALKER_H #include +#include #include #include +#include #include +// Internal walk function result +typedef struct +{ + fzl_type_t type; + + char *error_msg; + bool error; +} fzl_walk_res_t; + // Walker descriptor typedef struct { diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index 6d8e54e..31fa17f 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -1,6 +1,7 @@ #ifndef FZL_VM_H #define FZL_VM_H +#include #include // NOTE: move outside compiler scope to common #include #include @@ -16,8 +17,16 @@ typedef struct size_t ip; - fzl_const_t stack[256]; - size_t stack_top; + // Data stack + fzl_value_t dstack[256]; + size_t dtop; + + // Call stack + size_t cstack[256]; + size_t ctop; + + // Environment stack + // TODO: implement `estack` and `etop` } fzl_vm_t; // Allocate FZL VM diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 5ba7e26..c311aa3 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -19,7 +19,7 @@ fzl_parser_t *fzl_mkparser(fzl_reader_ll_t *first) if (!parser) return NULL; - parser->root = NULL; + parser->root = NULL; parser->cursor = first->next; if (!parser->cursor) return NULL; @@ -36,10 +36,10 @@ fzl_node_t *fzl_mknode(fzl_nodetype_t type, fzl_token_t *token) if (!node) return NULL; - node->type = type; - node->token = token; + node->type = type; + node->token = token; node->parent = NULL; - node->next = NULL; + node->next = NULL; return node; } @@ -71,7 +71,7 @@ static fzl_token_t *consume(fzl_parser_t *parser) if (!parser || !parser->cursor) return NULL; fzl_token_t *tok = parser->cursor->token; - parser->cursor = parser->cursor->next; + parser->cursor = parser->cursor->next; return tok; } @@ -149,8 +149,8 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) fzl_node_t *node = parse_expression(parser); if (!is(parser, FZL_TOKTYPE_RPAREN)) { - snprintf(plast_err, sizeof(plast_err), "expected rparen at %d", - parser->cursor->token->i0); + snprintf(plast_err, sizeof(plast_err), "expected ')' at %ld:%ld", + parser->cursor->token->ln, parser->cursor->token->cn); return NULL; } consume(parser); @@ -162,8 +162,8 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); } - snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %d", - peek(parser)->i0); + snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %ld:%ld", + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -173,13 +173,13 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) if (!parser) return NULL; - fzl_node_t *left = parse_primary(parser); + fzl_node_t *left = parse_primary(parser); fzl_node_t *right = NULL; if (!left) { - snprintf(plast_err, sizeof(plast_err), "failed to parse postfix at %d", - peek(parser)->i0); + snprintf(plast_err, sizeof(plast_err), "failed to parse postfix at %ld:%ld", + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -188,8 +188,8 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) consume(parser); size_t args_capacity = 2; - size_t args_count = 0; - fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); + size_t args_count = 0; + fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); while (!is(parser, FZL_TOKTYPE_RPAREN)) { @@ -218,13 +218,14 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) { free(args); snprintf(plast_err, sizeof(plast_err), - "expected ')' after argument list at %d", peek(parser)->i0); + "expected ')' after argument list at %ld:%ld", peek(parser)->ln, + peek(parser)->cn); return NULL; } - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); - node->call.callee = left; - node->call.args = args; + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); + node->call.callee = left; + node->call.args = args; node->call.args_count = args_count; left = node; @@ -242,13 +243,13 @@ fzl_node_t *parse_unary(fzl_parser_t *parser) if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS)) return parse_postfix(parser); - fzl_token_t *op = consume(parser); + fzl_token_t *op = consume(parser); fzl_node_t *operand = parse_unary(parser); if (!operand) return NULL; - fzl_node_t *result = fzl_mknode(unary_op_to_type(op->type), op); + fzl_node_t *result = fzl_mknode(unary_op_to_type(op->type), op); result->unary_op.operand = operand; return result; @@ -259,13 +260,13 @@ fzl_node_t *parse_power(fzl_parser_t *parser) if (!parser) return NULL; - fzl_node_t *left = parse_unary(parser); + fzl_node_t *left = parse_unary(parser); fzl_node_t *right = NULL; if (!left) { - snprintf(plast_err, sizeof(plast_err), "failed to parse power at %d", - peek(parser)->i0); + snprintf(plast_err, sizeof(plast_err), "failed to parse power at %ld:%ld", + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -277,12 +278,13 @@ fzl_node_t *parse_power(fzl_parser_t *parser) if (!right) { snprintf(plast_err, sizeof(plast_err), - "failed to parse right side of power at %d", peek(parser)->i0); + "failed to parse right side of power at %ld:%ld", + peek(parser)->ln, peek(parser)->cn); return NULL; } - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_POW, caret); - node->binary_op.left = left; + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_POW, caret); + node->binary_op.left = left; node->binary_op.right = right; left = node; @@ -297,14 +299,14 @@ fzl_node_t *parse_factor(fzl_parser_t *parser) if (!parser) return NULL; - fzl_node_t *left = parse_power(parser); + fzl_node_t *left = parse_power(parser); fzl_node_t *right = NULL; if (!left) { if (!plast_err[0]) - snprintf(plast_err, sizeof(plast_err), "invalid expression at %u", - parser->cursor->token->i0); + snprintf(plast_err, sizeof(plast_err), "invalid expression at %ld:%ld", + parser->cursor->token->ln, parser->cursor->token->cn); return NULL; } @@ -317,14 +319,15 @@ fzl_node_t *parse_factor(fzl_parser_t *parser) if (!right) { snprintf(plast_err, sizeof(plast_err), - "unfinished factor (mul/div) expression at %u", left->token->i0); + "unfinished factor (mul/div) expression at %ld:%ld", + left->token->ln, left->token->cn); return NULL; } - fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); - node->binary_op.left = left; + fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); + node->binary_op.left = left; node->binary_op.right = right; - left = node; + left = node; } return left; @@ -336,13 +339,13 @@ fzl_node_t *parse_term(fzl_parser_t *parser) if (!parser) return NULL; - fzl_node_t *left = parse_factor(parser); + fzl_node_t *left = parse_factor(parser); fzl_node_t *right = NULL; if (!left) { - snprintf(plast_err, sizeof(plast_err), "invalid expression at %u", - parser->cursor->token->i0); + snprintf(plast_err, sizeof(plast_err), "invalid expression at %ld:%ld", + parser->cursor->token->ln, parser->cursor->token->cn); return NULL; } @@ -354,14 +357,15 @@ fzl_node_t *parse_term(fzl_parser_t *parser) if (!right) { snprintf(plast_err, sizeof(plast_err), - "unfinished term (add/sub) expression at %u", left->token->i0); + "unfinished term (add/sub) expression at %ld:%ld", + left->token->ln, left->token->cn); return NULL; } - fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); - node->binary_op.left = left; + fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op); + node->binary_op.left = left; node->binary_op.right = right; - left = node; + left = node; } return left; @@ -383,8 +387,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) fzl_node_t *parse_program(fzl_parser_t *parser) { fzl_node_t *first = NULL; - fzl_node_t *last = NULL; - fzl_node_t *node = NULL; + fzl_node_t *last = NULL; + fzl_node_t *node = NULL; while (peek(parser) != NULL) { node = parse_statement(parser); @@ -394,18 +398,18 @@ fzl_node_t *parse_program(fzl_parser_t *parser) if (!last) { first = node; - last = node; + last = node; } else { last->next = node; - last = node; + last = node; } } if (!first) { - snprintf(plast_err, sizeof(plast_err), "empty ;("); + snprintf(plast_err, sizeof(plast_err), "nothing to parse >:("); return NULL; } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 35c6c1e..c7e4889 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -12,22 +12,42 @@ static char last_err[256] = {0}; fzl_reader_t *fzl_mkreader(char *source) { fzl_reader_ll_t *first_node = malloc(sizeof(fzl_reader_ll_t)); - first_node->token = NULL; - first_node->next = NULL; + first_node->token = NULL; + first_node->next = NULL; fzl_reader_t *reader = malloc(sizeof(fzl_reader_t)); - reader->first = first_node; - reader->last = first_node; - reader->source = source; - reader->size = strlen(source); - reader->position = 0; - reader->at_end = true; + reader->first = first_node; + reader->last = first_node; + reader->source = source; + reader->size = strlen(source); + reader->position = 0; + reader->line_n = 1; + reader->line_start = 0; return reader; } // --- Read zone --- +// Move cursor forward +static size_t move(fzl_reader_t *reader, size_t len) +{ + if (!reader) + return 0; + + char *src = reader->source; + for (size_t i = 0; i < len; i++) + { + if (src[reader->position + i] == '\n') + { + reader->line_n++; + reader->line_start = reader->position + i + 1; + } + } + + return reader->position += len; +} + // Try parse name fzl_token_t *parse_name(fzl_reader_t *reader) { @@ -45,10 +65,10 @@ fzl_token_t *parse_name(fzl_reader_t *reader) memcpy(value, reader->source + reader->position, size); size_t start = reader->position; - reader->position += size; + move(reader, size); - fzl_token_t *tok = - fzl_mktoken(FZL_TOKTYPE_NAME, start, reader->position - 1, value, size); + fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_NAME, reader->line_n, + start - reader->line_start, value, size); free(value); return tok; @@ -63,7 +83,7 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) if (reader->source[lpos] != '\'' && reader->source[lpos] != '"') return NULL; - char open_symbol = reader->source[lpos]; + char open_symbol = reader->source[lpos]; char close_symbol = '\0'; do @@ -78,23 +98,27 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) size++; if (close_symbol != open_symbol) + { + snprintf(last_err, sizeof(last_err), "unterminated string at %ld:%ld", + reader->line_n, reader->position - reader->line_start); return NULL; + } if (size == 2) { - fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->position, - reader->position + 1, "", 0); - reader->position += 2; + fzl_token_t *tok = + fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->line_n, + reader->position - reader->line_start, "", 0); + move(reader, 2); return tok; } char *value = malloc(size - 2); memcpy(value, reader->source + reader->position + 1, size - 2); - fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->position, - lpos, value, size - 2); + fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->line_n, + lpos - reader->line_start, value, size - 2); free(value); - - reader->position += size; + move(reader, size); return tok; } @@ -119,22 +143,22 @@ fzl_token_t *parse_number(fzl_reader_t *reader) memcpy(value, reader->source + reader->position, size); value[size] = '\0'; - fzl_token_t *tok = - fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, reader->position, lpos, value, size); + fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, reader->line_n, + lpos - reader->line_start, value, size); free(value); if (!tok) return NULL; - reader->position += size; + move(reader, size); return tok; } // Add single-char token fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type) { - size_t p = reader->position++; - return fzl_mktoken(type, p, p, NULL, 0); + size_t p = move(reader, 1); + return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); } int fzl_read(fzl_reader_t *reader) @@ -180,37 +204,26 @@ int fzl_read(fzl_reader_t *reader) token = parse_number(reader); else { - sprintf(last_err, "undefined token '%c'", curr); + sprintf(last_err, "undefined token '%c' at %ld:%ld", curr, reader->line_n, + p - reader->line_start); return -1; } if (!token) { - size_t line = 1, linepos = 1; - for (size_t i = 0; i < p; i++) - { - char sym = reader->source[i]; - if (sym == '\n') - { - linepos = 1; - line++; - } - else - linepos++; - } - - sprintf(last_err, "cannot parse token (%ld:%ld)", line, linepos); + if (!last_err[0]) + sprintf(last_err, "cannot parse token at %ld:%ld", reader->line_n, + p - reader->line_start); return -1; } fzl_reader_ll_t *tok_node = malloc(sizeof(fzl_reader_ll_t)); - tok_node->token = token; + tok_node->token = token; reader->last->next = tok_node; - reader->last = tok_node; + reader->last = tok_node; } while (reader->position < reader->size); - reader->at_end = true; return 0; } diff --git a/src/compiler/fzl_token.c b/src/compiler/fzl_token.c index 67f6660..96e761e 100644 --- a/src/compiler/fzl_token.c +++ b/src/compiler/fzl_token.c @@ -1,8 +1,9 @@ #include +#include #include #include -fzl_token_t *fzl_mktoken(const fzl_toktype_t type, uint16_t from, uint16_t to, +fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col, void *value, size_t value_len) { char *valholder = malloc(value_len + 1); @@ -10,12 +11,12 @@ fzl_token_t *fzl_mktoken(const fzl_toktype_t type, uint16_t from, uint16_t to, valholder[value_len] = '\0'; fzl_token_t *token = malloc(sizeof(fzl_token_t)); - token->type = type; - token->i0 = from; - token->i1 = to; + token->type = type; + token->ln = line; + token->cn = col; token->store_value = value_len > 0; - token->value = valholder; - token->value_sz = value_len; + token->value = valholder; + token->value_sz = value_len; return token; } diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 87aff38..2ba4717 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -1,3 +1,4 @@ +#include "common/fzl_types.h" #include #include #include @@ -35,12 +36,12 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } - walker->root = root; + walker->root = root; walker->consts = pool; - walker->op_count = 0; + walker->op_count = 0; walker->op_remainder = 2; - walker->ops = malloc(walker->op_remainder * sizeof(uint64_t)); + walker->ops = malloc(walker->op_remainder * sizeof(uint64_t)); return walker; } @@ -88,116 +89,171 @@ void push_word(fzl_walker_t *walker, uint64_t word) walker->ops[walker->op_count++] = word; } -bool walk(fzl_walker_t *walker, fzl_node_t *node) +fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { if (!walker || !node) - return false; + return (fzl_walk_res_t){.error = true, .error_msg = "walker is null"}; + fzl_walk_res_t current_res; while (node != NULL) { switch (node->type) { - case FZL_NODETYPE_BLOCK: - // NOTE: create block env here - if (node->list.entry_count > 0) - for (size_t i = 0; i < node->list.entry_count; i++) - if (!walk(walker, node->list.entries[i])) - return false; - // NOTE: destroy block env here - break; + case FZL_NODETYPE_UNARY_PLUS: + case FZL_NODETYPE_IDENTIFIER: + break; - case FZL_NODETYPE_CALL: - if (node->list.entry_count > 0) - { - for (size_t i = 0; i < node->call.args_count; i++) + case FZL_NODETYPE_BLOCK: + if (node->list.entry_count > 0) + for (size_t i = 0; i < node->list.entry_count; i++) + { + fzl_walk_res_t res = walk(walker, node->list.entries[i]); + if (res.error) + return res; + } + break; + + case FZL_NODETYPE_CALL: + if (node->list.entry_count > 0) { - if (!walk(walker, node->call.args[i])) - return false; + for (size_t i = 0; i < node->call.args_count; i++) + { + fzl_walk_res_t res = walk(walker, node->call.args[i]); + if (res.error) + return res; + } } - } - // NOTE: you need to implement dynamic names call - push_op(walker, FZL_OPCODE_CALL_BUILTIN); + // NOTE: you need to implement dynamic names call too + push_op(walker, FZL_OPCODE_CALL_BUILTIN); - if (strcmp(node->token->value, "printline") == 0) - push_word(walker, FZL_BUILTIN_PRINTLINE); - else - { - snprintf(wlast_err, sizeof(wlast_err), "call to unknown function '%s'", - (char *)node->token->value); - return false; - } - break; + if (strcmp(node->token->value, "printline") == 0) + { + push_word(walker, FZL_BUILTIN_PRINTLINE); + if (node->call.args_count < 1) + { + snprintf(wlast_err, sizeof(wlast_err), + "'printline' call with zero arguments at %ld:%ld", + node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + } + else + { + snprintf(wlast_err, sizeof(wlast_err), + "call to unknown function '%s'", (char *)node->token->value); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; - case FZL_NODETYPE_LITERAL: - if (node->token->type == FZL_TOKTYPE_STR_LITERAL) - { - size_t n = add_const( - walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, - .as.string.chars = node->token->value, - .as.string.length = node->token->value_sz}); + case FZL_NODETYPE_LITERAL: + if (node->token->type == FZL_TOKTYPE_STR_LITERAL) + { + size_t n = add_const( + walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = node->token->value, + .as.string.length = node->token->value_sz}); - push_op(walker, FZL_OPCODE_LOADCONST); - push_word(walker, n); - } - else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL) - { - push_op(walker, FZL_OPCODE_PUSHNUM); + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, n); + current_res = + (fzl_walk_res_t){.type = FZL_TYPE_STRING, .error = false}; + } + else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL) + { + push_op(walker, FZL_OPCODE_PUSHNUM); - uint64_t num = strtol(node->token->value, NULL, 10); - push_word(walker, num); - } - break; + uint64_t num = strtol(node->token->value, NULL, 10); + push_word(walker, num); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_INT, .error = false}; + } + break; - case FZL_NODETYPE_ADD: - case FZL_NODETYPE_SUB: - case FZL_NODETYPE_MUL: - case FZL_NODETYPE_DIV: - case FZL_NODETYPE_POW: - case FZL_NODETYPE_MOD: - if (!walk(walker, node->binary_op.left)) - return false; - - if (!walk(walker, node->binary_op.right)) - return false; - - switch (node->type) - { case FZL_NODETYPE_ADD: - push_op(walker, FZL_OPCODE_ADD); - break; case FZL_NODETYPE_SUB: - push_op(walker, FZL_OPCODE_SUB); - break; case FZL_NODETYPE_MUL: - push_op(walker, FZL_OPCODE_MUL); - break; case FZL_NODETYPE_DIV: - push_op(walker, FZL_OPCODE_DIV); - break; case FZL_NODETYPE_POW: - push_op(walker, FZL_OPCODE_POW); - break; case FZL_NODETYPE_MOD: - push_op(walker, FZL_OPCODE_MOD); + { + fzl_walk_res_t lres = walk(walker, node->binary_op.left); + if (lres.error) + return lres; + + fzl_walk_res_t rres = walk(walker, node->binary_op.right); + if (rres.error) + return rres; + + if (lres.type == FZL_TYPE_STRING) + { + fzl_token_t *ltok = node->binary_op.left->token; + snprintf(wlast_err, sizeof(wlast_err), + "left operand at %ld:%ld is not compatible to integer types", + ltok->ln, ltok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (rres.type == FZL_TYPE_STRING) + { + fzl_token_t *rtok = node->binary_op.right->token; + snprintf( + wlast_err, sizeof(wlast_err), + "right operand at %ld:%ld is not compatible to integer types", + rtok->ln, rtok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + switch (node->type) + { + case FZL_NODETYPE_ADD: + push_op(walker, FZL_OPCODE_ADD); + break; + case FZL_NODETYPE_SUB: + push_op(walker, FZL_OPCODE_SUB); + break; + case FZL_NODETYPE_MUL: + push_op(walker, FZL_OPCODE_MUL); + break; + case FZL_NODETYPE_DIV: + push_op(walker, FZL_OPCODE_DIV); + break; + case FZL_NODETYPE_POW: + push_op(walker, FZL_OPCODE_POW); + break; + case FZL_NODETYPE_MOD: + push_op(walker, FZL_OPCODE_MOD); + break; + default: + break; + } + current_res = (fzl_walk_res_t){.type = lres.type, .error = false}; break; - - default: - return false; } - break; - case FZL_NODETYPE_UNARY_MINUS: - if (!walk(walker, node->unary_op.operand)) - return false; + case FZL_NODETYPE_UNARY_MINUS: + { + fzl_walk_res_t res = walk(walker, node->unary_op.operand); + if (res.error) + return res; - push_op(walker, FZL_OPCODE_NEG); - break; + if (res.type == FZL_TYPE_STRING) + { + fzl_token_t *tok = node->unary_op.operand->token; + snprintf(wlast_err, sizeof(wlast_err), + "incompatible with unary operator type at %ld:%ld", tok->ln, + tok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + push_op(walker, FZL_OPCODE_NEG); + current_res = (fzl_walk_res_t){.type = res.type, .error = false}; + } } node = node->next; } - return true; + return current_res; } // Walk entire AST recursevily @@ -209,8 +265,12 @@ int fzl_walk(fzl_walker_t *walker) return -1; } - bool result = walk(walker, walker->root); - return !result && wlast_err[0] ? -1 : 0; + fzl_walk_res_t result = walk(walker, walker->root); + + if (result.error && !wlast_err[0]) + snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); + + return result.error ? -1 : 0; } // --- Walking zone end --- diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index d666bd2..7df9d0a 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -1,3 +1,5 @@ +#include "common/fzl_types.h" +#include "common/fzl_value.h" #include #include #include @@ -20,13 +22,13 @@ fzl_vm_t *fzl_mkvm(const char *filepath) return NULL; } - vm->consts = NULL; + vm->consts = NULL; vm->consts_count = 0; - vm->ops = NULL; - vm->ops_count = 0; - vm->ip = 0; - memset(vm->stack, 0, sizeof(vm->stack)); - vm->stack_top = 0; + vm->ops = NULL; + vm->ops_count = 0; + vm->ip = 0; + memset(vm->dstack, 0, sizeof(vm->dstack)); + vm->dtop = 0; FILE *fd = fopen(filepath, "rb"); if (!fd) @@ -56,7 +58,7 @@ fzl_vm_t *fzl_mkvm(const char *filepath) if (consts_count > 0) { vm->consts_count = consts_count; - vm->consts = malloc(consts_count * sizeof(fzl_const_t)); + vm->consts = malloc(consts_count * sizeof(fzl_value_t)); for (size_t i = 0; i < consts_count; i++) { @@ -87,7 +89,7 @@ fzl_vm_t *fzl_mkvm(const char *filepath) } vm->ops_count = ops_count; - vm->ops = malloc(ops_count * sizeof(uint64_t)); + vm->ops = malloc(ops_count * sizeof(uint64_t)); for (size_t i = 0; i < ops_count; i++) { uint64_t op; @@ -113,148 +115,173 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { - case FZL_OPCODE_CALL_BUILTIN: - uint64_t fn_id = vm->ops[++vm->ip]; - if (fn_id == FZL_BUILTIN_PRINTLINE) + case FZL_OPCODE_CALL_BUILTIN: { - fzl_const_t arg = vm->stack[--vm->stack_top]; - if (arg.type == FZL_CONSTTYPE_STRING) - printf("%s\n", arg.as.string.chars); - else if (arg.type == FZL_CONSTTYPE_NUMBER) - printf("%lu\n", arg.as.num.value); + uint64_t fn_id = vm->ops[++vm->ip]; + if (fn_id == FZL_BUILTIN_PRINTLINE) + { + fzl_value_t arg = vm->dstack[--vm->dtop]; + if (arg.type == FZL_TYPE_STRING) + printf("%s\n", arg.as.s.value); + else if (arg.type == FZL_TYPE_CHAR) + printf("%c\n", arg.as.c); + else if (arg.type == FZL_TYPE_INT8) + printf("%d\n", arg.as.i8); + else if (arg.type == FZL_TYPE_INT16) + printf("%d\n", arg.as.i16); + else if (arg.type == FZL_TYPE_INT32) + printf("%d\n", arg.as.i32); + else if (arg.type == FZL_TYPE_INT64) + printf("%ld\n", arg.as.i64); + else if (arg.type == FZL_TYPE_INT) + printf("%ld\n", arg.as.i); + else if (arg.type == FZL_TYPE_UINT8) + printf("%u\n", arg.as.u8); + else if (arg.type == FZL_TYPE_UINT16) + printf("%u\n", arg.as.u16); + else if (arg.type == FZL_TYPE_UINT32) + printf("%u\n", arg.as.u32); + else if (arg.type == FZL_TYPE_UINT64) + printf("%lu\n", arg.as.u64); + else if (arg.type == FZL_TYPE_UINT) + printf("%lu\n", arg.as.u); + else if (arg.type == FZL_TYPE_BOOL) + printf("%b\n", arg.as.b); + } + else + { + snprintf(last_err, sizeof(last_err), + "runtime error - undefined builtin %lu", fn_id); + return -1; + } } - else + break; + + case FZL_OPCODE_LOADCONST: { + uint64_t cn = vm->ops[++vm->ip]; + if (vm->consts_count <= cn) + { + snprintf(last_err, sizeof(last_err), "runtime error - unknown const"); + return -1; + } + fzl_const_t c = vm->consts[cn]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_STRING, + .as.s.length = c.as.string.length, + .as.s.value = c.as.string.chars}; + } + break; + + case FZL_OPCODE_ADD: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t add1 = vm->dstack[--vm->dtop]; + fzl_value_t add2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = add1.type, .as.u = add1.as.u + add2.as.u}; + break; + + case FZL_OPCODE_SUB: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t sub1 = vm->dstack[--vm->dtop]; + fzl_value_t sub2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = add1.type, .as.u = sub1.as.u - sub2.as.u}; + break; + + case FZL_OPCODE_MUL: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t mul1 = vm->dstack[--vm->dtop]; + fzl_value_t mul2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = mul1.type, .as.u = mul1.as.u * mul2.as.u}; + break; + + case FZL_OPCODE_DIV: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t div1 = vm->dstack[--vm->dtop]; + fzl_value_t div2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = div1.type, .as.u = div2.as.u / div1.as.u}; + break; + + case FZL_OPCODE_POW: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t pow1 = vm->dstack[--vm->dtop]; + fzl_value_t pow2 = vm->dstack[--vm->dtop]; + + uint64_t pow_n = pow1.as.u; + uint64_t pow_res = pow_n; + uint64_t pow_times = pow2.as.u; + for (size_t i = 1; i < pow_times; i++) + pow_res *= pow_n; + + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = pow1.type, .as.u = pow_res}; + break; + + case FZL_OPCODE_MOD: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + return -1; + } + fzl_value_t mod1 = vm->dstack[--vm->dtop]; + fzl_value_t mod2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = mod1.type, .as.u = mod2.as.u % mod1.as.u}; + break; + + case FZL_OPCODE_NEG: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in stack"); + return -1; + } + fzl_value_t neg_n = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = neg_n.type, .as.u = -neg_n.as.u}; + break; + + case FZL_OPCODE_PUSHNUM: + { + uint64_t push_n = vm->ops[++vm->ip]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_UINT, .as.u = push_n}; + } + break; + + default: snprintf(last_err, sizeof(last_err), - "runtime error - undefined builtin %lu", fn_id); + "runtime error - unknown opcode %u", opcode); return -1; - } - - break; - - case FZL_OPCODE_LOADCONST: - uint64_t cn = vm->ops[++vm->ip]; - if (vm->consts_count <= cn) - { - snprintf(last_err, sizeof(last_err), "runtime error - unknown const"); - return -1; - } - fzl_const_t c = vm->consts[cn]; - vm->stack[vm->stack_top++] = c; - break; - - case FZL_OPCODE_ADD: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t add1 = vm->stack[--vm->stack_top]; - fzl_const_t add2 = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, - .as.num.value = add1.as.num.value + add2.as.num.value}; - break; - - case FZL_OPCODE_SUB: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t sub1 = vm->stack[--vm->stack_top]; - fzl_const_t sub2 = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, - .as.num.value = sub1.as.num.value - sub2.as.num.value}; - break; - - case FZL_OPCODE_MUL: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t mul1 = vm->stack[--vm->stack_top]; - fzl_const_t mul2 = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, - .as.num.value = mul1.as.num.value * mul2.as.num.value}; - break; - - case FZL_OPCODE_DIV: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t div1 = vm->stack[--vm->stack_top]; - fzl_const_t div2 = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, - .as.num.value = div2.as.num.value / div1.as.num.value}; - break; - - case FZL_OPCODE_POW: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t pow1 = vm->stack[--vm->stack_top]; - fzl_const_t pow2 = vm->stack[--vm->stack_top]; - - uint64_t pow_n = pow1.as.num.value; - uint64_t pow_res = pow_n; - uint64_t pow_times = pow2.as.num.value; - for (size_t i = 1; i < pow_times; i++) - pow_res *= pow_n; - - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = pow_res}; - break; - - case FZL_OPCODE_MOD: - if (vm->stack_top < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - return -1; - } - fzl_const_t mod1 = vm->stack[--vm->stack_top]; - fzl_const_t mod2 = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, - .as.num.value = mod2.as.num.value % mod1.as.num.value}; - break; - - case FZL_OPCODE_NEG: - if (vm->stack_top < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in stack"); - return -1; - } - fzl_const_t neg_n = vm->stack[--vm->stack_top]; - vm->stack[vm->stack_top++] = (fzl_const_t){ - .type = FZL_CONSTTYPE_NUMBER, .as.num.value = -neg_n.as.num.value}; - break; - - case FZL_OPCODE_PUSHNUM: - uint64_t push_n = vm->ops[++vm->ip]; - vm->stack[vm->stack_top++] = - (fzl_const_t){.type = FZL_CONSTTYPE_NUMBER, .as.num.value = push_n}; - break; - - default: - snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %u", - opcode); - return -1; } vm->ip++; -- 2.54.0 From 01afe2e9724cd544930064a561c82bae9d022fc6 Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 09:22:33 +0300 Subject: [PATCH 16/34] upd: add hint in fzl_vm --- src/vm/fzl_vm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 7df9d0a..045cc76 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -1,7 +1,7 @@ -#include "common/fzl_types.h" -#include "common/fzl_value.h" #include #include +#include +#include #include #include #include @@ -9,6 +9,8 @@ #include #include +// NOTE: need to add optimized arithmetics in near future + char last_err[256]; static char *expect_hdr = "FZL\0\1"; -- 2.54.0 From 2c76277a68e217aa87b9356c03ddac7261f1d38b Mon Sep 17 00:00:00 2001 From: samsonium Date: Sun, 11 Oct 2026 19:11:49 +0300 Subject: [PATCH 17/34] wip: working on variables --- .gdbinit | 1 + exl/variables.fzs | 12 ++++ include/common/fzl_opcodes.h | 6 ++ include/compiler/fzl_parser.h | 7 +++ src/compiler/fzl_debug.c | 104 +++++++++++++++++++--------------- src/compiler/fzl_parser.c | 84 ++++++++++++++++++++------- src/compiler/fzl_reader.c | 27 +++++++-- src/compiler/fzl_walker.c | 46 ++++++++++++++- 8 files changed, 215 insertions(+), 72 deletions(-) create mode 100644 exl/variables.fzs diff --git a/.gdbinit b/.gdbinit index c0863a3..b48dd6a 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,4 +1,5 @@ skip peek skip consume skip next +skip is diff --git a/exl/variables.fzs b/exl/variables.fzs new file mode 100644 index 0000000..95c091f --- /dev/null +++ b/exl/variables.fzs @@ -0,0 +1,12 @@ +uint work_it = 21 * 2 ^ 2 +int make_it = 10 + +u64 do_it +do_it = 15 + +printline(work_it) +printline(make_it + do_it) + +char make_us +string floyd = "hello, Pink Floyd!" + diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index cd1c0e2..4bc136b 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -8,6 +8,12 @@ typedef enum FZL_OPCODE_CALL_BUILTIN = 0xF00002, // Call builtin function FZL_OPCODE_PUSHNUM = 0xF00003, // Push number into stack + FZL_OPCODE_MK_ENVTABLE = 0xF10001, // Allocate new environment table + FZL_OPCODE_RM_ENVTABLE = 0xF10002, // Free environment table + FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table + FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table + FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in arg strack FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in arg stack FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in arg stack diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index a0064e2..b8bee88 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -65,6 +65,13 @@ typedef struct fzl_node { struct fzl_node *operand; } unary_op; + + // Variables declaration and initializing + struct + { + fzl_token_t *type; + struct fzl_node *identifier; + } var; }; } fzl_node_t; diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index 3e40d63..5df5833 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -32,56 +32,70 @@ void print_node(fzl_node_t *node, int level) switch (node->type) { - case FZL_NODETYPE_CALL: - if (node->call.args_count > 0) - { - printf("%s Call fn '%s' with args:\n", levelbuf, - (char *)node->token->value); - for (size_t i = 0; i < node->call.args_count; i++) - print_node(node->call.args[i], level + 1); - } - else - printf("%s Call fn '%s' w/o args\n", levelbuf, - (char *)node->token->value); - break; - - case FZL_NODETYPE_LITERAL: - printf("%s Literal '%s'\n", levelbuf, (char *)node->token->value); - break; - - case FZL_NODETYPE_BLOCK: - printf("%s Block: ", levelbuf); - - if (node->list.entry_count < 1) - { - printf("\n"); + case FZL_NODETYPE_DECLARATION: + printf("%s Declare variable with type %d:\n", levelbuf, + node->var.type->type); + print_node(node->var.identifier, level + 1); break; - } - printf("\n"); - for (size_t i = 0; i < node->list.entry_count; i++) - print_node(node->list.entries[i], level + 1); + case FZL_NODETYPE_ASSIGN: + printf("%s Assign:\n", levelbuf); + printf("-%s Target:\n", levelbuf); + print_node(node->binary_op.left, level + 2); + printf("-%s Source:\n", levelbuf); + print_node(node->binary_op.right, level + 2); + break; - break; - case FZL_NODETYPE_ADD: - case FZL_NODETYPE_SUB: - case FZL_NODETYPE_MUL: - case FZL_NODETYPE_DIV: - case FZL_NODETYPE_POW: - case FZL_NODETYPE_MOD: - printf("%s Binary op (%d)\n", levelbuf, node->type); - printf("-%s LEFT:\n", levelbuf); - print_node(node->binary_op.left, level + 2); - printf("-%s RIGHT:\n", levelbuf); - print_node(node->binary_op.right, level + 2); - break; + case FZL_NODETYPE_CALL: + if (node->call.args_count > 0) + { + printf("%s Call fn '%s' with args:\n", levelbuf, + (char *)node->token->value); + for (size_t i = 0; i < node->call.args_count; i++) + print_node(node->call.args[i], level + 1); + } + else + printf("%s Call fn '%s' w/o args\n", levelbuf, + (char *)node->token->value); + break; - case FZL_NODETYPE_IDENTIFIER: - printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value); - break; + case FZL_NODETYPE_LITERAL: + printf("%s Literal '%s'\n", levelbuf, (char *)node->token->value); + break; - default: - printf("%s Secure node\n", levelbuf); + case FZL_NODETYPE_BLOCK: + printf("%s Block: ", levelbuf); + + if (node->list.entry_count < 1) + { + printf("\n"); + break; + } + + printf("\n"); + for (size_t i = 0; i < node->list.entry_count; i++) + print_node(node->list.entries[i], level + 1); + + break; + case FZL_NODETYPE_ADD: + case FZL_NODETYPE_SUB: + case FZL_NODETYPE_MUL: + case FZL_NODETYPE_DIV: + case FZL_NODETYPE_POW: + case FZL_NODETYPE_MOD: + printf("%s Binary op (%d)\n", levelbuf, node->type); + printf("-%s LEFT:\n", levelbuf); + print_node(node->binary_op.left, level + 2); + printf("-%s RIGHT:\n", levelbuf); + print_node(node->binary_op.right, level + 2); + break; + + case FZL_NODETYPE_IDENTIFIER: + printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value); + break; + + default: + printf("%s Secure node\n", levelbuf); } } diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index c311aa3..d814953 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -4,6 +4,7 @@ #include #include #include +#include char plast_err[256]; @@ -93,21 +94,21 @@ static fzl_nodetype_t math_op_to_type(fzl_toktype_t type) { switch (type) { - case FZL_TOKTYPE_PLUS: - return FZL_NODETYPE_ADD; - case FZL_TOKTYPE_MINUS: - return FZL_NODETYPE_SUB; - case FZL_TOKTYPE_STAR: - return FZL_NODETYPE_MUL; - case FZL_TOKTYPE_SLASH: - return FZL_NODETYPE_DIV; - case FZL_TOKTYPE_CARET: - return FZL_NODETYPE_POW; - case FZL_TOKTYPE_PERCENT: - return FZL_NODETYPE_MOD; + case FZL_TOKTYPE_PLUS: + return FZL_NODETYPE_ADD; + case FZL_TOKTYPE_MINUS: + return FZL_NODETYPE_SUB; + case FZL_TOKTYPE_STAR: + return FZL_NODETYPE_MUL; + case FZL_TOKTYPE_SLASH: + return FZL_NODETYPE_DIV; + case FZL_TOKTYPE_CARET: + return FZL_NODETYPE_POW; + case FZL_TOKTYPE_PERCENT: + return FZL_NODETYPE_MOD; - default: - return 0; + default: + return 0; } } @@ -115,13 +116,13 @@ static fzl_nodetype_t unary_op_to_type(fzl_toktype_t type) { switch (type) { - case FZL_TOKTYPE_PLUS: - return FZL_NODETYPE_UNARY_PLUS; - case FZL_TOKTYPE_MINUS: - return FZL_NODETYPE_UNARY_MINUS; + case FZL_TOKTYPE_PLUS: + return FZL_NODETYPE_UNARY_PLUS; + case FZL_TOKTYPE_MINUS: + return FZL_NODETYPE_UNARY_MINUS; - default: - return 0; + default: + return 0; } } @@ -380,6 +381,49 @@ fzl_node_t *parse_expression(fzl_parser_t *parser) // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { + if (is(parser, FZL_TOKTYPE_TYPE) || is(parser, FZL_TOKTYPE_NAME)) + { + fzl_token_t *type = peek(parser); + if (type->type == FZL_TOKTYPE_TYPE) + consume(parser); + + if (type->type == FZL_TOKTYPE_TYPE || next(parser)->type == FZL_TOKTYPE_EQ) + { + + fzl_node_t *identifier = parse_primary(parser); + if (!identifier || identifier->type != FZL_NODETYPE_IDENTIFIER) + { + snprintf(plast_err, sizeof(plast_err), "expected identifier at %ld:%ld", + type->ln, type->cn); + return NULL; + } + + fzl_node_t *left = fzl_mknode(FZL_NODETYPE_DECLARATION, type); + left->var.type = type; + left->var.identifier = identifier; + + if (!is(parser, FZL_TOKTYPE_EQ)) + return left; + + fzl_node_t *right; + while (is(parser, FZL_TOKTYPE_EQ)) + { + fzl_token_t *asn = consume(parser); + right = parse_expression(parser); + if (!right) + return NULL; + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_ASSIGN, asn); + node->binary_op.left = left; + node->binary_op.right = right; + + left = node; + } + + return left; + } + } + return parse_expression(parser); } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index c7e4889..9c28c97 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -59,16 +59,30 @@ fzl_token_t *parse_name(fzl_reader_t *reader) lpos++; size++; } while ((reader->source[lpos] >= 'A' && reader->source[lpos] <= 'Z') || - (reader->source[lpos] >= 'a' && reader->source[lpos] <= 'z')); + (reader->source[lpos] >= 'a' && reader->source[lpos] <= 'z') || + (reader->source[lpos] >= '0' && reader->source[lpos] <= '9') || + (reader->source[lpos] == '_')); - char *value = malloc(size); + char *value = malloc(size + 1); memcpy(value, reader->source + reader->position, size); + value[size] = '\0'; size_t start = reader->position; move(reader, size); - fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_NAME, reader->line_n, - start - reader->line_start, value, size); + fzl_token_t *tok; + size_t line = reader->line_n; + size_t pos = start - reader->line_start; + + if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || + strcmp(value, "uint") == 0 || strcmp(value, "int") == 0 || + strcmp(value, "i8") == 0 || strcmp(value, "i16") == 0 || + strcmp(value, "i32") == 0 || strcmp(value, "i64") == 0 || + strcmp(value, "u8") == 0 || strcmp(value, "u16") == 0 || + strcmp(value, "u32") == 0 || strcmp(value, "u64") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_TYPE, line, pos, value, size); + else + tok = fzl_mktoken(FZL_TOKTYPE_NAME, line, pos, value, size); free(value); return tok; @@ -198,7 +212,10 @@ int fzl_read(fzl_reader_t *reader) token = add_single(reader, FZL_TOKTYPE_CARET); else if (curr == '%') token = add_single(reader, FZL_TOKTYPE_PERCENT); - else if (curr >= 'A' && curr <= 'Z' || curr >= 'a' && curr <= 'z') + else if (curr == '=') + token = add_single(reader, FZL_TOKTYPE_EQ); + else if (curr >= 'A' && curr <= 'Z' || curr >= 'a' && curr <= 'z' || + curr == '_') token = parse_name(reader); else if (curr >= '0' && curr <= '9') token = parse_number(reader); diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 2ba4717..fb79258 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -1,6 +1,6 @@ -#include "common/fzl_types.h" #include #include +#include #include #include #include @@ -94,15 +94,28 @@ 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) { switch (node->type) { case FZL_NODETYPE_UNARY_PLUS: - case FZL_NODETYPE_IDENTIFIER: break; + case FZL_NODETYPE_IDENTIFIER: + { + size_t n = add_const( + walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = node->token->value, + .as.string.length = node->token->value_sz}); + + push_op(walker, FZL_OPCODE_LOADCONST); + push_word(walker, n); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_STRING, .error = false}; + } + case FZL_NODETYPE_BLOCK: if (node->list.entry_count > 0) for (size_t i = 0; i < node->list.entry_count; i++) @@ -248,6 +261,35 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_NEG); current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } + + case FZL_NODETYPE_DECLARATION: + { + fzl_walk_res_t rres = walk(walker, node->var.identifier); + if (rres.error) + return rres; + + push_op(walker, FZL_OPCODE_STORE); + + // NOTE: ignoring type field for now + + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + } + + 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; + + push_op(walker, FZL_OPCODE_WRITE); + current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; + } } node = node->next; -- 2.54.0 From e7573743aadb2a39584b956ba3ea9a88e61523cf Mon Sep 17 00:00:00 2001 From: samsonium Date: Mon, 12 Oct 2026 17:20:41 +0300 Subject: [PATCH 18/34] feat: implement environment table for vm --- CMakeLists.txt | 1 + include/vm/fzl_vm.h | 4 +- include/vm/fzl_vm_env.h | 52 ++++++++++++++++++++++ src/vm/fzl_vm.c | 28 +++++------- src/vm/fzl_vm_env.c | 97 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 include/vm/fzl_vm_env.h create mode 100644 src/vm/fzl_vm_env.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1086f30..7807249 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,5 +20,6 @@ add_executable(fzc # Virtual machine add_executable(fz src/vm/fzl_vm.c + src/vm/fzl_vm_env.c src/vm/main.c) diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index 31fa17f..ca55dcb 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -5,6 +5,7 @@ #include // NOTE: move outside compiler scope to common #include #include +#include // VM descriptor typedef struct @@ -26,7 +27,8 @@ typedef struct size_t ctop; // Environment stack - // TODO: implement `estack` and `etop` + fzl_vm_env_t estack[256]; + size_t etop; } fzl_vm_t; // Allocate FZL VM diff --git a/include/vm/fzl_vm_env.h b/include/vm/fzl_vm_env.h new file mode 100644 index 0000000..a50831a --- /dev/null +++ b/include/vm/fzl_vm_env.h @@ -0,0 +1,52 @@ +#ifndef FZL_VM_ENV_H +#define FZL_VM_ENV_H + +#include +#include +#include + +typedef enum +{ + FZL_VM_ENV_WRESULT_OK, + FZL_VM_ENV_WRESULT_ERR, + FZL_VM_ENV_WRESULT_ERR_EXISTS, + FZL_VM_ENV_WRESULT_ERR_NOT_FOUND +} fzl_vm_env_wresult_t; + +// Environment table entry descriptor +typedef struct +{ + char *name; + fzl_value_t value; +} fzl_vm_env_entry_t; + +// Environment table descriptor +typedef struct +{ + size_t size; + size_t remainder; + + fzl_vm_env_entry_t *table; +} fzl_vm_env_t; + +// Allocate env table +fzl_vm_env_t *fzl_mkenv(); + +// Try get value by name +fzl_value_t fzl_env_lookup(fzl_vm_env_t *env, char *name); + +// Check name in env table +bool fzl_env_contains(fzl_vm_env_t *env, char *name); + +// Store new value +fzl_vm_env_wresult_t fzl_env_storevalue(fzl_vm_env_t *env, char *name, + fzl_value_t val); + +// Overwrite existing value +fzl_vm_env_wresult_t fzl_env_writevalue(fzl_vm_env_t *env, char *name, + fzl_value_t newval); + +// Free env table +void fzl_freeenv(fzl_vm_env_t *env); + +#endif // FZL_VM_ENV_H diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 045cc76..7175726 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -29,8 +29,13 @@ fzl_vm_t *fzl_mkvm(const char *filepath) vm->ops = NULL; vm->ops_count = 0; vm->ip = 0; + vm->dtop = 0; + vm->ctop = 0; + vm->etop = 0; + memset(vm->dstack, 0, sizeof(vm->dstack)); - vm->dtop = 0; + memset(vm->cstack, 0, sizeof(vm->cstack)); + memset(vm->estack, 0, sizeof(vm->estack)); FILE *fd = fopen(filepath, "rb"); if (!fd) @@ -117,6 +122,11 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_MK_ENVTABLE: + case FZL_OPCODE_STORE: + case FZL_OPCODE_WRITE: + break; + case FZL_OPCODE_CALL_BUILTIN: { uint64_t fn_id = vm->ops[++vm->ip]; @@ -127,24 +137,8 @@ int fzl_vm_run(fzl_vm_t *vm) printf("%s\n", arg.as.s.value); else if (arg.type == FZL_TYPE_CHAR) printf("%c\n", arg.as.c); - else if (arg.type == FZL_TYPE_INT8) - printf("%d\n", arg.as.i8); - else if (arg.type == FZL_TYPE_INT16) - printf("%d\n", arg.as.i16); - else if (arg.type == FZL_TYPE_INT32) - printf("%d\n", arg.as.i32); - else if (arg.type == FZL_TYPE_INT64) - printf("%ld\n", arg.as.i64); else if (arg.type == FZL_TYPE_INT) printf("%ld\n", arg.as.i); - else if (arg.type == FZL_TYPE_UINT8) - printf("%u\n", arg.as.u8); - else if (arg.type == FZL_TYPE_UINT16) - printf("%u\n", arg.as.u16); - else if (arg.type == FZL_TYPE_UINT32) - printf("%u\n", arg.as.u32); - else if (arg.type == FZL_TYPE_UINT64) - printf("%lu\n", arg.as.u64); else if (arg.type == FZL_TYPE_UINT) printf("%lu\n", arg.as.u); else if (arg.type == FZL_TYPE_BOOL) diff --git a/src/vm/fzl_vm_env.c b/src/vm/fzl_vm_env.c new file mode 100644 index 0000000..f2ff358 --- /dev/null +++ b/src/vm/fzl_vm_env.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +static const fzl_value_t null = {.type = FZL_TYPE_NULL}; + +fzl_vm_env_t *fzl_mkenv() +{ + fzl_vm_env_t *env = malloc(sizeof(fzl_vm_env_t)); + env->remainder = 2; + env->size = 0; + env->table = malloc(env->remainder * sizeof(fzl_vm_env_entry_t)); + + return env; +} + +// Resize table if size eq to remainder +static void resize_env_if_needed(fzl_vm_env_t *env) +{ + if (env->remainder > env->size) + return; + + env->remainder *= 2; + env->table = realloc(env->table, env->remainder * sizeof(fzl_vm_env_entry_t)); +} + +fzl_value_t fzl_env_lookup(fzl_vm_env_t *env, char *name) +{ + if (!env) + return null; + + for (size_t i = 0; i < env->size; i++) + if (strcmp(env->table[i].name, name) == 0) + return env->table[i].value; + + return (fzl_value_t){.type = FZL_TYPE_NULL}; +} + +bool fzl_env_contains(fzl_vm_env_t *env, char *name) +{ + if (!env) + return false; + + for (size_t i = 0; i < env->size; i++) + if (strcmp(env->table[i].name, name) == 0) + return true; + + return false; +} + +fzl_vm_env_wresult_t fzl_env_storevalue(fzl_vm_env_t *env, char *name, + fzl_value_t val) +{ + if (!env) + return FZL_VM_ENV_WRESULT_ERR; + + resize_env_if_needed(env); + 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}; + + return FZL_VM_ENV_WRESULT_OK; +} + +fzl_vm_env_wresult_t fzl_env_writevalue(fzl_vm_env_t *env, char *name, + fzl_value_t newval) +{ + if (!env) + return FZL_VM_ENV_WRESULT_ERR; + + for (size_t i = 0; i < env->size; i++) + { + if (strcmp(env->table[i].name, name) == 0) + { + env->table[i].value = newval; + return FZL_VM_ENV_WRESULT_OK; + } + } + + return FZL_VM_ENV_WRESULT_ERR_NOT_FOUND; +} + +void fzl_freeenv(fzl_vm_env_t *env) +{ + if (!env) + return; + + if (env->table) + free(env->table); + + free(env); +} -- 2.54.0 From d86d95f4fa6a4694cf4f3ba39db404b0cb909aae Mon Sep 17 00:00:00 2001 From: samsonium Date: Mon, 12 Oct 2026 17:20:59 +0300 Subject: [PATCH 19/34] fix: add missing breaks in walker switch-block --- src/compiler/fzl_walker.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index fb79258..f8303a7 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -114,6 +114,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, n); current_res = (fzl_walk_res_t){.type = FZL_TYPE_STRING, .error = false}; + break; } case FZL_NODETYPE_BLOCK: @@ -260,6 +261,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_NEG); current_res = (fzl_walk_res_t){.type = res.type, .error = false}; + break; } case FZL_NODETYPE_DECLARATION: @@ -273,6 +275,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) // NOTE: ignoring type field for now current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; } case FZL_NODETYPE_ASSIGN: @@ -289,6 +292,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_WRITE); current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; + break; } } -- 2.54.0 From b8769ac81c97b8c50c41cae9f59c32eaea0cba65 Mon Sep 17 00:00:00 2001 From: samsonium Date: Mon, 12 Oct 2026 17:21:34 +0300 Subject: [PATCH 20/34] fix: reader lines counter fix --- src/compiler/fzl_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 9c28c97..c9d3a1b 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -188,7 +188,7 @@ int fzl_read(fzl_reader_t *reader) if (curr == '\n' || curr == ' ' || curr == '\r') { - reader->position++; + move(reader, 1); continue; } -- 2.54.0 From 8043c30d9d57f002de74c1a1be2a22bae58a9362 Mon Sep 17 00:00:00 2001 From: samsonium Date: Mon, 12 Oct 2026 17:29:52 +0300 Subject: [PATCH 21/34] fix: add separate assign to identifier statement --- src/compiler/fzl_parser.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index d814953..9c69b77 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -381,7 +381,7 @@ fzl_node_t *parse_expression(fzl_parser_t *parser) // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { - if (is(parser, FZL_TOKTYPE_TYPE) || is(parser, FZL_TOKTYPE_NAME)) + if (is(parser, FZL_TOKTYPE_TYPE)) { fzl_token_t *type = peek(parser); if (type->type == FZL_TOKTYPE_TYPE) @@ -424,6 +424,29 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) } } + if (is(parser, FZL_TOKTYPE_NAME) && next(parser)->type == FZL_TOKTYPE_EQ) + { + fzl_token_t *name = consume(parser); + fzl_node_t *left = fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); + + while (is(parser, FZL_TOKTYPE_EQ)) + { + fzl_token_t *eq = consume(parser); + + fzl_node_t *right = parse_expression(parser); + if (!right) + return NULL; + + fzl_node_t *assn = fzl_mknode(FZL_NODETYPE_ASSIGN, eq); + assn->binary_op.left = left; + assn->binary_op.right = right; + + left = assn; + } + + return left; + } + return parse_expression(parser); } -- 2.54.0 From 7e52c671aa649384782f17ca31d8de673b6b8041 Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 00:59:31 +0300 Subject: [PATCH 22/34] upd: add printline for string example --- exl/variables.fzs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exl/variables.fzs b/exl/variables.fzs index 95c091f..18216b8 100644 --- a/exl/variables.fzs +++ b/exl/variables.fzs @@ -8,5 +8,6 @@ printline(work_it) printline(make_it + do_it) char make_us -string floyd = "hello, Pink Floyd!" +str floyd = "hello, Pink Floyd!" +printline(floyd) -- 2.54.0 From c0dc14a0c7650dd8ffd5c6cfb0288f2862625d46 Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 01:00:01 +0300 Subject: [PATCH 23/34] feat: finished variables declaration / assign --- CMakeLists.txt | 2 + include/common/fzl_types.h | 7 +- include/compiler/fzl_walker.h | 4 + include/compiler/utils/fzl_symtable.h | 44 ++++++++ include/vm/fzl_vm.h | 5 +- src/compiler/fzl_walker.c | 146 ++++++++++++++++++++++---- src/compiler/utils/fzl_symtable.c | 68 ++++++++++++ src/vm/fzl_vm.c | 83 ++++++++++++++- src/vm/fzl_vm_debug.c | 69 ++++++++++++ src/vm/fzl_vm_env.c | 2 +- src/vm/main.c | 21 ++-- 11 files changed, 419 insertions(+), 32 deletions(-) create mode 100644 include/compiler/utils/fzl_symtable.h create mode 100644 src/compiler/utils/fzl_symtable.c create mode 100644 src/vm/fzl_vm_debug.c 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); -- 2.54.0 From 4a908f9fd8d0d621c34f93dd5c5161ac4e50297b Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 01:09:55 +0300 Subject: [PATCH 24/34] upd: update README --- README.md | 9 +++++++++ README.ru.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 21d6de7..e965f7c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,15 @@ At the moment, the language has the following capabilities: - Builtin `printline` function for printing argument to stdout - String and numeric literals - Arithmetic operations: addition, subtraction, multiplication, division, modulo, power +- Variables declaration with following types: + - `uint`, `int` - for the machine-word size integers + - `i8`..`i64` - integers with specified size + - `u8`..`u64` - unsigned integers with specified size + - `str` - strings + - `bool` - boolean +- Assignments + +Working examples of code can be found in `exl/` directory. ## File extensions diff --git a/README.ru.md b/README.ru.md index 0307cbd..28d32c5 100644 --- a/README.ru.md +++ b/README.ru.md @@ -11,6 +11,15 @@ - Встроенную функцию `printline` для вывода аргумента в stdout - Строковый и числовой литералы - Арифметические операции: сложение, вычитание, умножение, деление, модуло, возведение в степень +- Декларация переменных со следующими типами: + - `uint`, `int` - целые числа размером с машинное слово + - `i8`..`i64` - целые числа со знаком + - `u8`..`u64` - целые числа без знака + - `str` - строки + - `bool` - булевы +- Присваивание + +Примеры кода лежат в директории `exl/`. ## Расширения файлов -- 2.54.0 From 83ff0b57747bdb22f2e49485913a849633448212 Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 19:56:27 +0300 Subject: [PATCH 25/34] feat: implement conditions --- exl/conditions.fzs | 16 +++ include/common/fzl_opcodes.h | 30 +++-- include/compiler/fzl_parser.h | 21 ++++ include/compiler/fzl_token.h | 26 +++- include/compiler/fzl_walker.h | 3 + src/compiler/fzl_debug.c | 25 ++++ src/compiler/fzl_parser.c | 217 +++++++++++++++++++++++++++++++++- src/compiler/fzl_reader.c | 50 +++++++- src/compiler/fzl_walker.c | 157 +++++++++++++++++++++++- src/vm/fzl_vm.c | 153 +++++++++++++++++++++++- src/vm/fzl_vm_debug.c | 58 ++++++++- 11 files changed, 731 insertions(+), 25 deletions(-) create mode 100644 exl/conditions.fzs diff --git a/exl/conditions.fzs b/exl/conditions.fzs new file mode 100644 index 0000000..4e7dbf4 --- /dev/null +++ b/exl/conditions.fzs @@ -0,0 +1,16 @@ +bool flag = true + +if (!flag) + printline("buhhh, flag is not true") +else if (2 + 2 >= 4) +{ + printline ("two plus two is four") +} +else { + printline("gotcha") +} + +{ + printline("block after") +} + diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 4bc136b..2ac85ae 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -7,6 +7,7 @@ typedef enum FZL_OPCODE_LOADCONST = 0xF00001, // Load const into T (temp) reg FZL_OPCODE_CALL_BUILTIN = 0xF00002, // Call builtin function FZL_OPCODE_PUSHNUM = 0xF00003, // Push number into stack + FZL_OPCODE_PUSHBOOL = 0xF00004, // Push boolean into stack FZL_OPCODE_MK_ENVTABLE = 0xF10001, // Allocate new environment table FZL_OPCODE_RM_ENVTABLE = 0xF10002, // Free environment table @@ -14,13 +15,28 @@ typedef enum FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable - FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in arg strack - FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in arg stack - FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in arg stack - FZL_OPCODE_DIV = 0xA00004, // Divide two numbers in arg stack - FZL_OPCODE_POW = 0xA00005, // Power first number in arg stack by second - FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in arg stack - FZL_OPCODE_NEG = 0xA00007, // Negotiate number in arg stack + FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address + FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero + + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in dstack + FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in dstack + FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in dstack + FZL_OPCODE_DIV = 0xA00004, // Divide two numbers in dstack + FZL_OPCODE_POW = 0xA00005, // Power first number in dstack by second + FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in dstack + FZL_OPCODE_NEG = 0xA00007, // Negotiate number in dstack + + FZL_OPCODE_CMP_EQ = 0xB00001, // Equal + FZL_OPCODE_CMP_NEQ = 0xB00002, // Not equal + FZL_OPCODE_CMP_LT = 0xB00003, // Less than + FZL_OPCODE_CMP_LTEQ = 0xB00004, // Less than or equal + FZL_OPCODE_CMP_GT = 0xB00005, // Greater than + FZL_OPCODE_CMP_GTEQ = 0xB00006, // Greater than or equal + + FZL_OPCODE_AND = 0xC00001, // A && B + FZL_OPCODE_OR = 0xC00002, // A || B + + FZL_OPCODE_NOT = 0xD00001, // !A } fzl_opcode_t; #endif // FZL_OPCODES_H diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index b8bee88..21b361f 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -12,6 +12,7 @@ typedef enum FZL_NODETYPE_CALL, FZL_NODETYPE_LITERAL, FZL_NODETYPE_IDENTIFIER, + FZL_NODETYPE_CONDITIONAL, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, @@ -23,6 +24,18 @@ typedef enum FZL_NODETYPE_UNARY_PLUS, FZL_NODETYPE_UNARY_MINUS, + FZL_NODETYPE_OR, + FZL_NODETYPE_AND, + + FZL_NODETYPE_EQUAL, + FZL_NODETYPE_NEQUAL, + FZL_NODETYPE_LESS, + FZL_NODETYPE_LESS_EQUAL, + FZL_NODETYPE_GREATER, + FZL_NODETYPE_GREATER_EQUAL, + + FZL_NODETYPE_NOT, + FZL_NODETYPE_DECLARATION, FZL_NODETYPE_ASSIGN } fzl_nodetype_t; @@ -72,6 +85,14 @@ typedef struct fzl_node fzl_token_t *type; struct fzl_node *identifier; } var; + + // Conditional blocks + struct + { + struct fzl_node *condition; + struct fzl_node *true_block; + struct fzl_node *else_block; + } cond; }; } fzl_node_t; diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index a289fe5..911a76d 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -8,17 +8,35 @@ // Token type typedef enum { + FZL_TOKTYPE_IF, // if + FZL_TOKTYPE_ELSE, // else + FZL_TOKTYPE_NAME, // abc FZL_TOKTYPE_TYPE, // i16 FZL_TOKTYPE_LPAREN, // ( FZL_TOKTYPE_RPAREN, // ) + FZL_TOKTYPE_LBRACE, // { + FZL_TOKTYPE_RBRACE, // } - FZL_TOKTYPE_COMMA, // , - FZL_TOKTYPE_DOT, // . + FZL_TOKTYPE_COMMA, // , + FZL_TOKTYPE_DOT, // . + FZL_TOKTYPE_SEMICOLON, // ; - FZL_TOKTYPE_STR_LITERAL, // "" - FZL_TOKTYPE_NUM_LITERAL, // 123 + FZL_TOKTYPE_STR_LITERAL, // "" + FZL_TOKTYPE_NUM_LITERAL, // 123 + FZL_TOKTYPE_BOOL_LITERAL, // true + + FZL_TOKTYPE_DAMPERSAND, // && + FZL_TOKTYPE_DVBAR, // || + + FZL_TOKTYPE_EQEQ, // == + FZL_TOKTYPE_REQ, // >= + FZL_TOKTYPE_LEQ, // <= + FZL_TOKTYPE_NEQ, // != + FZL_TOKTYPE_LANGLE, // < + FZL_TOKTYPE_RANGLE, // > + FZL_TOKTYPE_NOT, // ! FZL_TOKTYPE_STAR, // * FZL_TOKTYPE_SLASH, // / diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index e51a34c..d44cb4c 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -30,6 +30,9 @@ typedef struct fzl_symtable_t *syms[64]; size_t sym_top; + + size_t bp_table[64]; + size_t bp_top; } fzl_walker_t; // Allocate new walker diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index 5df5833..14e5f95 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -63,6 +63,19 @@ void print_node(fzl_node_t *node, int level) printf("%s Literal '%s'\n", levelbuf, (char *)node->token->value); break; + case FZL_NODETYPE_CONDITIONAL: + printf("%s Conditional block:\n", levelbuf); + printf("-%s Condition:\n", levelbuf); + print_node(node->cond.condition, level + 2); + printf("-%s True block:\n", levelbuf); + print_node(node->cond.true_block, level + 2); + if (node->cond.else_block) + { + printf("-%s Else block:\n", levelbuf); + print_node(node->cond.else_block, level + 2); + } + break; + case FZL_NODETYPE_BLOCK: printf("%s Block: ", levelbuf); @@ -77,6 +90,18 @@ void print_node(fzl_node_t *node, int level) print_node(node->list.entries[i], level + 1); break; + + case FZL_NODETYPE_NOT: + printf("%s Inverse:\n", levelbuf); + print_node(node->unary_op.operand, level + 1); + break; + + case FZL_NODETYPE_EQUAL: + case FZL_NODETYPE_NEQUAL: + case FZL_NODETYPE_LESS: + case FZL_NODETYPE_LESS_EQUAL: + case FZL_NODETYPE_GREATER: + case FZL_NODETYPE_GREATER_EQUAL: case FZL_NODETYPE_ADD: case FZL_NODETYPE_SUB: case FZL_NODETYPE_MUL: diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 9c69b77..373d106 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -120,6 +120,8 @@ static fzl_nodetype_t unary_op_to_type(fzl_toktype_t type) return FZL_NODETYPE_UNARY_PLUS; case FZL_TOKTYPE_MINUS: return FZL_NODETYPE_UNARY_MINUS; + case FZL_TOKTYPE_NOT: + return FZL_NODETYPE_NOT; default: return 0; @@ -144,6 +146,11 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) fzl_token_t *tok = consume(parser); return fzl_mknode(FZL_NODETYPE_LITERAL, tok); } + else if (is(parser, FZL_TOKTYPE_BOOL_LITERAL)) + { + fzl_token_t *tok = consume(parser); + return fzl_mknode(FZL_NODETYPE_LITERAL, tok); + } else if (is(parser, FZL_TOKTYPE_LPAREN)) { consume(parser); @@ -241,7 +248,8 @@ fzl_node_t *parse_unary(fzl_parser_t *parser) if (!parser) return NULL; - if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS)) + if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS) && + !is(parser, FZL_TOKTYPE_NOT)) return parse_postfix(parser); fzl_token_t *op = consume(parser); @@ -372,12 +380,131 @@ fzl_node_t *parse_term(fzl_parser_t *parser) return left; } -// Expression parsing -fzl_node_t *parse_expression(fzl_parser_t *parser) +// Parse conditional operations +fzl_node_t *parse_cond(fzl_parser_t *parser) { - return parse_term(parser); + if (!parser) + return NULL; + + fzl_node_t *left = parse_term(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_EQEQ) || is(parser, FZL_TOKTYPE_NEQ) || + is(parser, FZL_TOKTYPE_LANGLE) || is(parser, FZL_TOKTYPE_LEQ) || + is(parser, FZL_TOKTYPE_RANGLE) || is(parser, FZL_TOKTYPE_REQ)) + { + fzl_token_t *op = consume(parser); + right = parse_term(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + op->ln, op->cn); + return NULL; + } + + fzl_node_t *result; + switch (op->type) + { + case FZL_TOKTYPE_EQEQ: + result = fzl_mknode(FZL_NODETYPE_EQUAL, op); + break; + case FZL_TOKTYPE_NEQ: + result = fzl_mknode(FZL_NODETYPE_NEQUAL, op); + break; + case FZL_TOKTYPE_LANGLE: + result = fzl_mknode(FZL_NODETYPE_LESS, op); + break; + case FZL_TOKTYPE_LEQ: + result = fzl_mknode(FZL_NODETYPE_LESS_EQUAL, op); + break; + case FZL_TOKTYPE_RANGLE: + result = fzl_mknode(FZL_NODETYPE_GREATER, op); + break; + case FZL_TOKTYPE_REQ: + result = fzl_mknode(FZL_NODETYPE_GREATER_EQUAL, op); + break; + default: + return NULL; + } + + result->binary_op.left = left; + result->binary_op.right = right; + left = result; + } + + return left; } +// Parse conditional && +fzl_node_t *parse_and(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_cond(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_DAMPERSAND)) + { + fzl_token_t * or = consume(parser); + right = parse_cond(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + or->ln, or->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_AND, or); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; +} + +// Parse conditional || +fzl_node_t *parse_or(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_and(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_DVBAR)) + { + fzl_token_t * or = consume(parser); + right = parse_and(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + or->ln, or->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_OR, or); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; +} + +// Expression parsing +fzl_node_t *parse_expression(fzl_parser_t *parser) { return parse_and(parser); } + // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { @@ -447,6 +574,88 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return left; } + if (is(parser, FZL_TOKTYPE_IF)) + { + fzl_token_t *if_tok = consume(parser); + fzl_token_t *lparen = consume(parser); + if (lparen->type != FZL_TOKTYPE_LPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected '(' before condition at %ld:%ld", lparen->ln, + lparen->cn); + return NULL; + } + + fzl_node_t *condition = parse_expression(parser); + if (!condition) + return NULL; + + fzl_token_t *rparen = consume(parser); + if (rparen->type != FZL_TOKTYPE_RPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected ')' after condition at %ld:%ld", rparen->ln, + rparen->cn); + return NULL; + } + + fzl_node_t *else_block = NULL; + fzl_node_t *true_block = parse_statement(parser); + if (!true_block) + return NULL; + + if (is(parser, FZL_TOKTYPE_ELSE)) + { + consume(parser); // else + else_block = parse_statement(parser); + if (!else_block) + return NULL; + } + + fzl_node_t *stmt_node = fzl_mknode(FZL_NODETYPE_CONDITIONAL, if_tok); + stmt_node->cond.condition = condition; + stmt_node->cond.true_block = true_block; + stmt_node->cond.else_block = else_block; + + return stmt_node; + } + + if (is(parser, FZL_TOKTYPE_LBRACE)) + { + fzl_token_t *block_tok = consume(parser); + + size_t remainder = 2; + size_t count = 0; + fzl_node_t **stmts = malloc(remainder * sizeof(fzl_node_t *)); + + while (peek(parser) && !is(parser, FZL_TOKTYPE_RBRACE)) + { + fzl_node_t *node = parse_statement(parser); + if (!node) + return NULL; + + if (remainder <= count) + { + remainder *= 2; + stmts = realloc(stmts, remainder); + } + + stmts[count++] = node; + } + fzl_token_t *rbrace = consume(parser); + if (!rbrace || rbrace->type != FZL_TOKTYPE_RBRACE) + { + snprintf(plast_err, sizeof(plast_err), + "missing '}' after block at %ld:%ld", rbrace->ln, rbrace->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_BLOCK, block_tok); + node->list.entries = stmts; + node->list.entry_count = count; + return node; + } + return parse_expression(parser); } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index c9d3a1b..91305d1 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -74,12 +74,19 @@ fzl_token_t *parse_name(fzl_reader_t *reader) size_t line = reader->line_n; size_t pos = start - reader->line_start; - if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || - strcmp(value, "uint") == 0 || strcmp(value, "int") == 0 || - strcmp(value, "i8") == 0 || strcmp(value, "i16") == 0 || - strcmp(value, "i32") == 0 || strcmp(value, "i64") == 0 || - strcmp(value, "u8") == 0 || strcmp(value, "u16") == 0 || - strcmp(value, "u32") == 0 || strcmp(value, "u64") == 0) + if (strcmp(value, "if") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_IF, line, pos, value, size); + else if (strcmp(value, "else") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_ELSE, line, pos, value, size); + else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_BOOL_LITERAL, line, pos, value, size); + else if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || + strcmp(value, "bool") == 0 || strcmp(value, "uint") == 0 || + strcmp(value, "int") == 0 || strcmp(value, "i8") == 0 || + strcmp(value, "i16") == 0 || strcmp(value, "i32") == 0 || + strcmp(value, "i64") == 0 || strcmp(value, "u8") == 0 || + strcmp(value, "u16") == 0 || strcmp(value, "u32") == 0 || + strcmp(value, "u64") == 0) tok = fzl_mktoken(FZL_TOKTYPE_TYPE, line, pos, value, size); else tok = fzl_mktoken(FZL_TOKTYPE_NAME, line, pos, value, size); @@ -175,6 +182,13 @@ fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type) return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); } +// Add double-char token +fzl_token_t *add_double(fzl_reader_t *reader, fzl_toktype_t type) +{ + size_t p = move(reader, 2); + return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); +} + int fzl_read(fzl_reader_t *reader) { if (reader->position >= reader->size) @@ -194,6 +208,30 @@ int fzl_read(fzl_reader_t *reader) if (curr == '\'' || curr == '"') token = parse_string_literal(reader); + else if (curr == '=' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_EQEQ); + else if (curr == '>' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_REQ); + else if (curr == '<' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_LEQ); + else if (curr == '!' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_NEQ); + else if (curr == '|' && reader->source[p + 1] == '|') + token = add_double(reader, FZL_TOKTYPE_DVBAR); + else if (curr == '&' && reader->source[p + 1] == '&') + token = add_double(reader, FZL_TOKTYPE_DAMPERSAND); + else if (curr == '>') + token = add_single(reader, FZL_TOKTYPE_RANGLE); + else if (curr == '<') + token = add_single(reader, FZL_TOKTYPE_LANGLE); + else if (curr == '!') + token = add_single(reader, FZL_TOKTYPE_NOT); + else if (curr == ';') + token = add_single(reader, FZL_TOKTYPE_SEMICOLON); + else if (curr == '{') + token = add_single(reader, FZL_TOKTYPE_LBRACE); + else if (curr == '}') + token = add_single(reader, FZL_TOKTYPE_RBRACE); else if (curr == '(') token = add_single(reader, FZL_TOKTYPE_LPAREN); else if (curr == ')') diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index ddcffc6..8e4054e 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -13,6 +13,9 @@ #include #include +// TODO: implement backpatching struct for jumping on if/else blocks +// TODO: implement conditional binops; conditions + char wlast_err[256]; fzl_walker_t *fzl_mkwalker(fzl_node_t *root) @@ -52,6 +55,9 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } + memset(walker->bp_table, 0, sizeof(walker->bp_table)); + walker->bp_top = 0; + return walker; } @@ -152,8 +158,139 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { switch (node->type) { - case FZL_NODETYPE_UNARY_PLUS: + case FZL_NODETYPE_NOT: + { + fzl_walk_res_t res = walk(walker, node->unary_op.operand); + if (res.error) + return res; + + push_op(walker, FZL_OPCODE_NOT); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; break; + } + + case FZL_NODETYPE_EQUAL: + case FZL_NODETYPE_NEQUAL: + case FZL_NODETYPE_LESS: + case FZL_NODETYPE_LESS_EQUAL: + case FZL_NODETYPE_GREATER: + case FZL_NODETYPE_GREATER_EQUAL: + { + fzl_walk_res_t left = walk(walker, node->binary_op.left); + if (left.error) + return left; + + fzl_walk_res_t right = walk(walker, node->binary_op.right); + if (right.error) + return right; + + if (!is_compatible(left.type, right.type)) + { + snprintf(wlast_err, sizeof(wlast_err), + "incompatible types at %ld:%ld", node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + push_op(walker, FZL_OPCODE_CMP_EQ); + break; + case FZL_NODETYPE_NEQUAL: + push_op(walker, FZL_OPCODE_CMP_NEQ); + break; + case FZL_NODETYPE_LESS: + push_op(walker, FZL_OPCODE_CMP_LT); + break; + case FZL_NODETYPE_LESS_EQUAL: + push_op(walker, FZL_OPCODE_CMP_LTEQ); + break; + case FZL_NODETYPE_GREATER: + push_op(walker, FZL_OPCODE_CMP_GT); + break; + case FZL_NODETYPE_GREATER_EQUAL: + push_op(walker, FZL_OPCODE_CMP_GTEQ); + break; + default: + break; + } + + current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + break; + } + + case FZL_NODETYPE_OR: + case FZL_NODETYPE_AND: + { + fzl_walk_res_t left = walk(walker, node->binary_op.left); + if (left.error) + return left; + + fzl_walk_res_t right = walk(walker, node->binary_op.right); + if (right.error) + return right; + + if (!is_compatible(left.type, right.type)) + { + snprintf(wlast_err, sizeof(wlast_err), + "incompatible types at %ld:%ld", node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (node->type == FZL_NODETYPE_AND) + push_op(walker, FZL_OPCODE_AND); + else + push_op(walker, FZL_OPCODE_OR); + + current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + break; + } + + case FZL_NODETYPE_CONDITIONAL: + { + fzl_walk_res_t cres = walk(walker, node->cond.condition); + if (cres.error) + return cres; + + push_op(walker, FZL_OPCODE_JZ); + + size_t jz_if = walker->op_count; + push_word(walker, (uint64_t)0); + + fzl_walk_res_t tblock_res = walk(walker, node->cond.true_block); + if (tblock_res.error) + return tblock_res; + + push_op(walker, FZL_OPCODE_JMP); + size_t jmp_else = walker->op_count; + push_word(walker, (uint64_t)0); + + walker->ops[jz_if] = walker->op_count; + + if (node->cond.else_block) + { + fzl_walk_res_t fblock_res = walk(walker, node->cond.else_block); + if (fblock_res.error) + return fblock_res; + } + + walker->ops[jmp_else] = walker->op_count; + + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; + } + + case FZL_NODETYPE_UNARY_PLUS: + { + fzl_walk_res_t res = walk(walker, node->unary_op.operand); + if (res.error) + return res; + + current_res = res; + break; + } case FZL_NODETYPE_IDENTIFIER: { @@ -248,6 +385,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_word(walker, num); current_res = (fzl_walk_res_t){.type = FZL_TYPE_INT, .error = false}; } + else if (node->token->type == FZL_TOKTYPE_BOOL_LITERAL) + { + fzl_token_t *tok = node->token; + uint64_t val = strcmp(tok->value, "true") == 0 ? 1 : 0; + + push_op(walker, FZL_OPCODE_PUSHBOOL); + push_word(walker, val); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; + } break; case FZL_NODETYPE_ADD: @@ -423,6 +569,12 @@ int fzl_walk(fzl_walker_t *walker) fzl_walk_res_t result = walk(walker, walker->root); push_op(walker, FZL_OPCODE_RM_ENVTABLE); + if (walker->bp_top > 0) + { + snprintf(wlast_err, sizeof(wlast_err), "unfinished conditional block"); + return -1; + } + if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); @@ -443,5 +595,8 @@ void fzl_freewalker(fzl_walker_t *walker) if (walker->ops) free(walker->ops); + while (walker->sym_top > 0) + fzl_freesymt(walker->syms[--walker->sym_top]); + free(walker); } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 480dd14..c762ac4 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -123,6 +123,149 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_CMP_EQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t leq = vm->dstack[--vm->dtop]; + fzl_value_t req = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.b == req.as.b}; + break; + + case FZL_OPCODE_CMP_NEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lneq = vm->dstack[--vm->dtop]; + fzl_value_t rneq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, + .as.b = lneq.as.u != rneq.as.u}; + break; + + case FZL_OPCODE_CMP_LT: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t llt = vm->dstack[--vm->dtop]; + fzl_value_t rlt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; + break; + + case FZL_OPCODE_CMP_LTEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t llteq = vm->dstack[--vm->dtop]; + fzl_value_t rlteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = llteq.as.u <= rlteq.as.u}; + break; + + case FZL_OPCODE_CMP_GT: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lgt = vm->dstack[--vm->dtop]; + fzl_value_t rgt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgt.as.u > rgt.as.u}; + break; + + case FZL_OPCODE_CMP_GTEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lgteq = vm->dstack[--vm->dtop]; + fzl_value_t rgteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = lgteq.as.u >= rgteq.as.u}; + break; + + case FZL_OPCODE_NOT: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + return -1; + } + + fzl_value_t notval = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = !notval.as.b}; + break; + + case FZL_OPCODE_JZ: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + return -1; + } + + fzl_value_t jzval = vm->dstack[--vm->dtop]; + uint64_t jzip = vm->ops[++vm->ip] - 1; + if (jzval.as.b == 0) + vm->ip = jzip; + break; + + case FZL_OPCODE_JMP: + vm->ip = vm->ops[++vm->ip] - 1; + break; + + case FZL_OPCODE_AND: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t land = vm->dstack[--vm->dtop]; + fzl_value_t rand = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, + .as.b = land.as.b && rand.as.b}; + break; + + case FZL_OPCODE_OR: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lor = vm->dstack[--vm->dtop]; + fzl_value_t ror = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lor.as.b || ror.as.b}; + break; + case FZL_OPCODE_STORE: if (vm->dtop < 1) { @@ -350,8 +493,16 @@ int fzl_vm_run(fzl_vm_t *vm) uint64_t push_n = vm->ops[++vm->ip]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_UINT, .as.u = push_n}; + break; + } + + case FZL_OPCODE_PUSHBOOL: + { + uint64_t push_b = vm->ops[++vm->ip]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = push_b}; + break; } - break; default: snprintf(last_err, sizeof(last_err), diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index e22ebd1..c53fd19 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -1,4 +1,5 @@ #include "common/fzl_opcodes.h" +#include #include #include @@ -15,11 +16,17 @@ void fzl_vm_print_ops(fzl_vm_t *vm) for (size_t i = 0; i < vm->ops_count; i++) { + printf("%04ld: ", i); + switch (vm->ops[i]) { case FZL_OPCODE_LOADCONST: - printf("LOADCONST #%lu\n", vm->ops[++i]); + { + size_t const_n = vm->ops[++i]; + fzl_const_t c = vm->consts[const_n]; + printf("LOADCONST #%lu ('%s')\n", const_n, c.as.string.chars); break; + } case FZL_OPCODE_CALL_BUILTIN: printf("CALL_BUILTIN #%lu\n", vm->ops[++i]); @@ -29,6 +36,10 @@ void fzl_vm_print_ops(fzl_vm_t *vm) printf("PUSHNUM %lu\n", vm->ops[++i]); break; + case FZL_OPCODE_PUSHBOOL: + printf("PUSHBOOL %s\n", vm->ops[++i] ? "true" : "false"); + break; + case FZL_OPCODE_MK_ENVTABLE: printf("MK_ENVTABLE\n"); break; @@ -58,12 +69,55 @@ void fzl_vm_print_ops(fzl_vm_t *vm) printf("Math opcode\n"); break; + case FZL_OPCODE_AND: + printf("AND\n"); + break; + + case FZL_OPCODE_OR: + printf("OR\n"); + break; + + case FZL_OPCODE_CMP_EQ: + printf("CMP_EQ\n"); + + case FZL_OPCODE_CMP_NEQ: + printf("CMP_NEQ\n"); + break; + + case FZL_OPCODE_CMP_LT: + printf("CMP_LT\n"); + break; + + case FZL_OPCODE_CMP_LTEQ: + printf("CMP_LTEQ\n"); + break; + + case FZL_OPCODE_CMP_GT: + printf("CMP_GT\n"); + break; + + case FZL_OPCODE_CMP_GTEQ: + printf("CMP_GTEQ\n"); + break; + + case FZL_OPCODE_NOT: + printf("CMP_NOT\n"); + break; + case FZL_OPCODE_NEG: printf("NEG\n"); break; + case FZL_OPCODE_JZ: + printf("JZ IP: %ld\n", vm->ops[++i]); + break; + + case FZL_OPCODE_JMP: + printf("JMP IP %ld\n", vm->ops[++i]); + break; + default: - printf("Unknown"); + printf("\n"); } } } -- 2.54.0 From cc3087296f0fcf711420bc83c5e150e6010f84f5 Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 20:10:37 +0300 Subject: [PATCH 26/34] ref: remove backpatch table in walker --- include/compiler/fzl_walker.h | 3 --- src/compiler/fzl_walker.c | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index d44cb4c..e51a34c 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -30,9 +30,6 @@ typedef struct fzl_symtable_t *syms[64]; size_t sym_top; - - size_t bp_table[64]; - size_t bp_top; } fzl_walker_t; // Allocate new walker diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 8e4054e..f83b1ef 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -13,9 +13,6 @@ #include #include -// TODO: implement backpatching struct for jumping on if/else blocks -// TODO: implement conditional binops; conditions - char wlast_err[256]; fzl_walker_t *fzl_mkwalker(fzl_node_t *root) @@ -55,9 +52,6 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } - memset(walker->bp_table, 0, sizeof(walker->bp_table)); - walker->bp_top = 0; - return walker; } @@ -569,12 +563,6 @@ int fzl_walk(fzl_walker_t *walker) fzl_walk_res_t result = walk(walker, walker->root); push_op(walker, FZL_OPCODE_RM_ENVTABLE); - if (walker->bp_top > 0) - { - snprintf(wlast_err, sizeof(wlast_err), "unfinished conditional block"); - return -1; - } - if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); -- 2.54.0 From 2c206822ca0e099d337bc5cede5eaf05ce0faf68 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 15 Oct 2026 02:23:43 +0300 Subject: [PATCH 27/34] feat: implement functions --- exl/fn.fzs | 24 ++++ include/common/fzl_opcodes.h | 3 + include/compiler/fzl_parser.h | 20 ++++ include/compiler/fzl_token.h | 2 + include/compiler/fzl_walker.h | 9 ++ include/compiler/utils/fzl_symtable.h | 4 +- include/vm/fzl_vm_env.h | 2 +- src/compiler/fzl_debug.c | 16 +++ src/compiler/fzl_parser.c | 102 ++++++++++++++++- src/compiler/fzl_reader.c | 4 +- src/compiler/fzl_walker.c | 154 ++++++++++++++++++++++++-- src/compiler/utils/fzl_symtable.c | 6 +- src/vm/fzl_vm.c | 56 +++++++--- src/vm/fzl_vm_debug.c | 14 ++- src/vm/fzl_vm_env.c | 11 +- 15 files changed, 391 insertions(+), 36 deletions(-) create mode 100644 exl/fn.fzs diff --git a/exl/fn.fzs b/exl/fn.fzs new file mode 100644 index 0000000..b08dec2 --- /dev/null +++ b/exl/fn.fzs @@ -0,0 +1,24 @@ +int fib(int n) +{ + if (n <= 1) + return n + + return fib(n - 1) + fib(n - 2) +} + +uint sum(uint a, uint b) +{ + return a + b +} + +printline("Test #1: fibonacci") +int result = fib(7) +if (result == 13) + printline("Recurse test passed") +else printline("Recurse test failed") + +printline("Test #2: sum") +if (sum(2, 2) == 4) + printline("Sum test passed") +else printline("Sum test failed") + diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 2ac85ae..6938b80 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -18,6 +18,9 @@ typedef enum FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero + FZL_OPCODE_CALL = 0xF30001, // Call function + FZL_OPCODE_RET = 0xF30002, // Return from function + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in dstack FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in dstack FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in dstack diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index 21b361f..363d8c0 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -1,6 +1,7 @@ #ifndef FZL_PARSER_H #define FZL_PARSER_H +#include "common/fzl_types.h" #include #include #include @@ -13,6 +14,8 @@ typedef enum FZL_NODETYPE_LITERAL, FZL_NODETYPE_IDENTIFIER, FZL_NODETYPE_CONDITIONAL, + FZL_NODETYPE_FUNCTION, + FZL_NODETYPE_RETURN, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, @@ -93,6 +96,23 @@ typedef struct fzl_node struct fzl_node *true_block; struct fzl_node *else_block; } cond; + + // Function declaration + struct + { + struct fzl_node **args; + size_t args_count; + + struct fzl_node *body; + fzl_token_t *name; + fzl_token_t *type; + } fn; + + // Return value + struct + { + struct fzl_node *value; + } ret; }; } fzl_node_t; diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index 911a76d..bcc8de1 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -8,6 +8,8 @@ // Token type typedef enum { + FZL_TOKTYPE_RETURN, // return + FZL_TOKTYPE_IF, // if FZL_TOKTYPE_ELSE, // else diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index e51a34c..7b5ee8a 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -18,6 +18,13 @@ typedef struct bool error; } fzl_walk_res_t; +// Function context descriptor +typedef struct +{ + fzl_node_t *fn; + size_t initial_env_lvl; +} fzl_walk_ctx_t; + // Walker descriptor typedef struct { @@ -30,6 +37,8 @@ typedef struct fzl_symtable_t *syms[64]; size_t sym_top; + + fzl_walk_ctx_t *ctx; } fzl_walker_t; // Allocate new walker diff --git a/include/compiler/utils/fzl_symtable.h b/include/compiler/utils/fzl_symtable.h index e81f0dc..1a591a9 100644 --- a/include/compiler/utils/fzl_symtable.h +++ b/include/compiler/utils/fzl_symtable.h @@ -4,6 +4,7 @@ #include #include #include +#include typedef enum { @@ -17,6 +18,7 @@ typedef struct { char *name; fzl_type_t type; + size_t ip; } fzl_sym_t; // Symbols table descriptor @@ -33,7 +35,7 @@ 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); + fzl_type_t type, uint64_t ip); // Get entry from specified symtable bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res); diff --git a/include/vm/fzl_vm_env.h b/include/vm/fzl_vm_env.h index a50831a..3bd1ecc 100644 --- a/include/vm/fzl_vm_env.h +++ b/include/vm/fzl_vm_env.h @@ -33,7 +33,7 @@ typedef struct fzl_vm_env_t *fzl_mkenv(); // Try get value by name -fzl_value_t fzl_env_lookup(fzl_vm_env_t *env, char *name); +bool fzl_env_lookup(fzl_vm_env_t *env, char *name, fzl_value_t *res); // Check name in env table bool fzl_env_contains(fzl_vm_env_t *env, char *name); diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index 14e5f95..c11f9c2 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -96,6 +96,22 @@ void print_node(fzl_node_t *node, int level) print_node(node->unary_op.operand, level + 1); break; + case FZL_NODETYPE_FUNCTION: + printf("%s Function named '%s' with ret type '%s':\n", levelbuf, + (char *)node->fn.name->value, (char *)node->fn.type->value); + printf("-%s Args:\n", levelbuf); + for (size_t i = 0; i < node->fn.args_count; i++) + print_node(node->fn.args[i], level + 2); + printf("-%s Body:\n", levelbuf); + print_node(node->fn.body, level + 2); + break; + + case FZL_NODETYPE_RETURN: + printf("%s Return:\n", levelbuf); + if (node->ret.value) + print_node(node->ret.value, level + 1); + break; + case FZL_NODETYPE_EQUAL: case FZL_NODETYPE_NEQUAL: case FZL_NODETYPE_LESS: diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 373d106..a581727 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -514,6 +514,79 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (type->type == FZL_TOKTYPE_TYPE) consume(parser); + if (type->type == FZL_TOKTYPE_TYPE && + next(parser)->type == FZL_TOKTYPE_LPAREN) + { + fzl_token_t *tok = consume(parser); + if (tok->type != FZL_TOKTYPE_NAME) + { + snprintf(plast_err, sizeof(plast_err), + "expected name in function definition at %ld:%ld", tok->ln, + tok->cn); + return NULL; + } + + fzl_token_t *lparen = consume(parser); + if (lparen->type != FZL_TOKTYPE_LPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected '(' after name at %ld:%ld", lparen->ln, lparen->cn); + return NULL; + } + + size_t remainder = 1; + size_t count = 0; + fzl_node_t **args = malloc(remainder * sizeof(fzl_node_t *)); + + while (peek(parser) && !is(parser, FZL_TOKTYPE_RPAREN)) + { + fzl_node_t *arg = parse_statement(parser); + if (!arg) + return NULL; + + if (remainder <= count) + { + remainder *= 2; + args = realloc(args, remainder * sizeof(fzl_node_t *)); + } + + args[count++] = arg; + + if (is(parser, FZL_TOKTYPE_COMMA)) + consume(parser); + } + + fzl_token_t *rparen = consume(parser); + if (rparen->type != FZL_TOKTYPE_RPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected ')' after argument list at %ld:%ld", rparen->ln, + rparen->cn); + return NULL; + } + + if (!is(parser, FZL_TOKTYPE_LBRACE)) + { + fzl_token_t *tok = peek(parser); + snprintf(plast_err, sizeof(plast_err), "expected '{' at %ld:%ld", + tok->ln, tok->cn); + return NULL; + } + + fzl_node_t *body = parse_statement(parser); + if (!body) + return NULL; + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FUNCTION, tok); + node->fn.args = args; + node->fn.args_count = count; + node->fn.body = body; + node->fn.name = tok; + node->fn.type = type; + + return node; + } + if (type->type == FZL_TOKTYPE_TYPE || next(parser)->type == FZL_TOKTYPE_EQ) { @@ -574,6 +647,25 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return left; } + if (is(parser, FZL_TOKTYPE_RETURN)) + { + fzl_token_t *ret = consume(parser); + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_RETURN, ret); + + if (is(parser, FZL_TOKTYPE_DOT)) + { + consume(parser); + return node; + } + + fzl_node_t *retval = parse_expression(parser); + if (!retval) + return NULL; + + node->ret.value = retval; + return node; + } + if (is(parser, FZL_TOKTYPE_IF)) { fzl_token_t *if_tok = consume(parser); @@ -637,7 +729,7 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (remainder <= count) { remainder *= 2; - stmts = realloc(stmts, remainder); + stmts = realloc(stmts, remainder * sizeof(fzl_node_t *)); } stmts[count++] = node; @@ -758,6 +850,14 @@ void fzl_freeast(fzl_node_t *root) if (root->unary_op.operand) fzl_freeast(root->unary_op.operand); } + else if (root->type == FZL_NODETYPE_FUNCTION) + { + if (root->fn.args_count) + for (size_t i = 0; i < root->fn.args_count; i++) + fzl_freenode(root->fn.args[i]); + free(root->fn.args); + fzl_freenode(root->fn.body); + } fzl_freenode(root); root = root->next; diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 91305d1..18d5426 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -74,7 +74,9 @@ fzl_token_t *parse_name(fzl_reader_t *reader) size_t line = reader->line_n; size_t pos = start - reader->line_start; - if (strcmp(value, "if") == 0) + if (strcmp(value, "return") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_RETURN, line, pos, value, size); + else if (strcmp(value, "if") == 0) tok = fzl_mktoken(FZL_TOKTYPE_IF, line, pos, value, size); else if (strcmp(value, "else") == 0) tok = fzl_mktoken(FZL_TOKTYPE_ELSE, line, pos, value, size); diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index f83b1ef..8daeadb 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -52,6 +52,8 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } + walker->ctx = NULL; + return walker; } @@ -152,6 +154,101 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { switch (node->type) { + case FZL_NODETYPE_FUNCTION: + { + push_op(walker, FZL_OPCODE_JMP); + size_t skip_ip = walker->op_count; + push_word(walker, (uint64_t)0); + + fzl_type_t fntype = tok_to_type(node->fn.type); + fzl_symtable_res_t putres = + fzl_symt_put(walker->syms[walker->sym_top - 1], + node->fn.name->value, fntype, walker->op_count); + if (putres != FZL_SYMTABLE_RESULT_OK) + { + if (putres == FZL_SYMTABLE_RESULT_EXISTS) + snprintf(wlast_err, sizeof(wlast_err), + "cannot define already declared name '%s' at %ld:%ld", + (char *)node->fn.name->value, node->token->ln, + node->token->cn); + else + snprintf(wlast_err, sizeof(wlast_err), "failed to store name '%s'", + (char *)node->fn.name->value); + + return (fzl_walk_res_t){.error = true}; + } + + // NOTE: add sym_top limit check + size_t lvl = walker->sym_top; + walker->syms[walker->sym_top++] = fzl_mksymt(); + walker->ctx = malloc(sizeof(fzl_walk_ctx_t)); + walker->ctx->fn = node; + walker->ctx->initial_env_lvl = lvl; + + push_op(walker, FZL_OPCODE_MK_ENVTABLE); + + for (ssize_t i = node->fn.args_count - 1; i >= 0; i--) + { + fzl_walk_res_t res = walk(walker, node->fn.args[i]); + if (res.error) + return res; + push_op(walker, FZL_OPCODE_WRITE); + } + + fzl_walk_res_t bodyres = walk(walker, node->fn.body); + if (bodyres.error) + return bodyres; + + // NOTE: ghost walk (w/o opcodes write) for arg check should be there in + // near future + + push_op(walker, FZL_OPCODE_RM_ENVTABLE); + walker->ops[skip_ip] = walker->op_count; + + walker->ctx = NULL; // NOTE: think about nested function defs (not now) + for (ssize_t i = walker->sym_top - 1; i >= lvl; i--) + fzl_freesymt(walker->syms[--walker->sym_top]); + + break; + } + + case FZL_NODETYPE_RETURN: + { + if (!walker->ctx) + { + snprintf(wlast_err, sizeof(wlast_err), + "return cannot be used outside of functions"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (node->ret.value) + { + fzl_walk_res_t retres = walk(walker, node->ret.value); + if (retres.error) + return retres; + + if (!is_compatible(retres.type, + tok_to_type(walker->ctx->fn->fn.type))) + { + snprintf(wlast_err, sizeof(wlast_err), + "incompatible types between return and function type at " + "%ld:%ld", + node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + current_res = (fzl_walk_res_t){.type = retres.type, .error = false}; + } + else + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + + for (size_t i = 0; i < (walker->sym_top - walker->ctx->initial_env_lvl); + i++) + push_op(walker, FZL_OPCODE_RM_ENVTABLE); + push_op(walker, FZL_OPCODE_RET); + break; + } + case FZL_NODETYPE_NOT: { fzl_walk_res_t res = walk(walker, node->unary_op.operand); @@ -294,8 +391,12 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) .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); + bool exists = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((exists = + fzl_symt_get(walker->syms[i], node->token->value, &sym))) + break; + if (!exists) { snprintf(wlast_err, sizeof(wlast_err), @@ -314,6 +415,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) } case FZL_NODETYPE_BLOCK: + walker->syms[walker->sym_top++] = fzl_mksymt(); push_op(walker, FZL_OPCODE_MK_ENVTABLE); if (node->list.entry_count > 0) for (size_t i = 0; i < node->list.entry_count; i++) @@ -322,6 +424,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (res.error) return res; } + fzl_freesymt(walker->syms[--walker->sym_top]); push_op(walker, FZL_OPCODE_RM_ENVTABLE); break; @@ -335,11 +438,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) return res; } } - // NOTE: you need to implement dynamic names call too - push_op(walker, FZL_OPCODE_CALL_BUILTIN); if (strcmp(node->token->value, "printline") == 0) { + push_op(walker, FZL_OPCODE_CALL_BUILTIN); push_word(walker, FZL_BUILTIN_PRINTLINE); if (node->call.args_count < 1) { @@ -348,14 +450,37 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; } else { - snprintf(wlast_err, sizeof(wlast_err), - "call to unknown function '%s'", (char *)node->token->value); - return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + fzl_sym_t res; + bool found = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((found = + fzl_symt_get(walker->syms[i], node->token->value, &res))) + break; + + if (!found) + { + snprintf(wlast_err, sizeof(wlast_err), + "call to unknown function '%s'", + (char *)node->token->value); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (!res.ip) + { + snprintf(wlast_err, sizeof(wlast_err), + "cannot jump to address 0x0"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + push_op(walker, FZL_OPCODE_CALL); + push_word(walker, res.ip); + + current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } - current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; break; case FZL_NODETYPE_LITERAL: @@ -475,7 +600,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { 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)); + node->var.identifier->token->value, tok_to_type(node->var.type), 0); fzl_token_t *id_tok = node->var.identifier->token; size_t const_n = add_const( @@ -509,9 +634,14 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) 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 + bool found = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((found = + fzl_symt_get(walker->syms[i], + node->binary_op.left->token->value, &sym))) + break; + + if (!found) { fzl_token_t *tok = node->binary_op.left->token->value; snprintf(wlast_err, sizeof(wlast_err), diff --git a/src/compiler/utils/fzl_symtable.c b/src/compiler/utils/fzl_symtable.c index 3d4afa8..262958e 100644 --- a/src/compiler/utils/fzl_symtable.c +++ b/src/compiler/utils/fzl_symtable.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,7 @@ fzl_symtable_t *fzl_mksymt() } fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, - fzl_type_t type) + fzl_type_t type, uint64_t ip) { if (!symt) return FZL_SYMTABLE_RESULT_ERR; @@ -35,7 +36,8 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t)); } - symt->symbols[symt->size++] = (fzl_sym_t){.name = name, .type = type}; + symt->symbols[symt->size++] = + (fzl_sym_t){.name = name, .type = type, .ip = ip}; return FZL_SYMTABLE_RESULT_OK; } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index c762ac4..662dd2f 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,27 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_CALL: + { + uint64_t ip = vm->ops[++vm->ip]; + vm->cstack[vm->ctop++] = vm->ip; + vm->ip = ip - 1; + break; + } + + case FZL_OPCODE_RET: + { + if (vm->ctop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - no place to jump"); + return -1; + } + + vm->ip = vm->cstack[--vm->ctop]; + break; + } + case FZL_OPCODE_CMP_EQ: if (vm->dtop < 2) { @@ -131,10 +153,10 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t leq = vm->dstack[--vm->dtop]; fzl_value_t req = vm->dstack[--vm->dtop]; + fzl_value_t leq = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.b == req.as.b}; + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.u == req.as.u}; break; case FZL_OPCODE_CMP_NEQ: @@ -145,8 +167,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t lneq = vm->dstack[--vm->dtop]; fzl_value_t rneq = vm->dstack[--vm->dtop]; + fzl_value_t lneq = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lneq.as.u != rneq.as.u}; break; @@ -159,8 +181,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t llt = vm->dstack[--vm->dtop]; fzl_value_t rlt = vm->dstack[--vm->dtop]; + fzl_value_t llt = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; break; @@ -173,8 +195,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t llteq = vm->dstack[--vm->dtop]; fzl_value_t rlteq = vm->dstack[--vm->dtop]; + fzl_value_t llteq = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){ .type = FZL_TYPE_BOOL, .as.b = llteq.as.u <= rlteq.as.u}; break; @@ -187,8 +209,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t lgt = vm->dstack[--vm->dtop]; fzl_value_t rgt = vm->dstack[--vm->dtop]; + fzl_value_t lgt = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgt.as.u > rgt.as.u}; break; @@ -201,8 +223,8 @@ int fzl_vm_run(fzl_vm_t *vm) return -1; } - fzl_value_t lgteq = vm->dstack[--vm->dtop]; fzl_value_t rgteq = vm->dstack[--vm->dtop]; + fzl_value_t lgteq = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = (fzl_value_t){ .type = FZL_TYPE_BOOL, .as.b = lgteq.as.u >= rgteq.as.u}; break; @@ -319,7 +341,9 @@ int fzl_vm_run(fzl_vm_t *vm) } } - vm->dstack[vm->dtop++] = value; + // vm->dstack[vm->dtop++] = value; + // NOTE: add POP opcode to prevent + // stack overflow! break; case FZL_OPCODE_MK_ENVTABLE: @@ -340,8 +364,14 @@ int fzl_vm_run(fzl_vm_t *vm) } 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); + fzl_value_t env_val; + bool result; + for (ssize_t i = vm->etop - 1; i >= 0; i--) + { + if ((result = fzl_env_lookup(vm->estack[i], (char *)val.as.s.value, + &env_val))) + break; + } vm->dstack[vm->dtop++] = env_val; break; @@ -411,10 +441,10 @@ int fzl_vm_run(fzl_vm_t *vm) "runtime error - expected 2 values in stack"); return -1; } - fzl_value_t sub1 = vm->dstack[--vm->dtop]; fzl_value_t sub2 = vm->dstack[--vm->dtop]; + fzl_value_t sub1 = vm->dstack[--vm->dtop]; vm->dstack[vm->dtop++] = - (fzl_value_t){.type = add1.type, .as.u = sub1.as.u - sub2.as.u}; + (fzl_value_t){.type = sub1.type, .as.u = sub1.as.u - sub2.as.u}; break; case FZL_OPCODE_MUL: @@ -450,8 +480,8 @@ int fzl_vm_run(fzl_vm_t *vm) "runtime error - expected 2 values in stack"); return -1; } - fzl_value_t pow1 = vm->dstack[--vm->dtop]; fzl_value_t pow2 = vm->dstack[--vm->dtop]; + fzl_value_t pow1 = vm->dstack[--vm->dtop]; uint64_t pow_n = pow1.as.u; uint64_t pow_res = pow_n; diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index c53fd19..11727a0 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -20,6 +20,17 @@ void fzl_vm_print_ops(fzl_vm_t *vm) switch (vm->ops[i]) { + case FZL_OPCODE_CALL: + { + size_t ip = vm->ops[++i]; + printf("Call IP: %lu\n", ip); + break; + } + + case FZL_OPCODE_RET: + printf("Return\n"); + break; + case FZL_OPCODE_LOADCONST: { size_t const_n = vm->ops[++i]; @@ -79,6 +90,7 @@ void fzl_vm_print_ops(fzl_vm_t *vm) case FZL_OPCODE_CMP_EQ: printf("CMP_EQ\n"); + break; case FZL_OPCODE_CMP_NEQ: printf("CMP_NEQ\n"); @@ -113,7 +125,7 @@ void fzl_vm_print_ops(fzl_vm_t *vm) break; case FZL_OPCODE_JMP: - printf("JMP IP %ld\n", vm->ops[++i]); + printf("JMP IP: %ld\n", vm->ops[++i]); break; default: diff --git a/src/vm/fzl_vm_env.c b/src/vm/fzl_vm_env.c index 974851f..2ca4ede 100644 --- a/src/vm/fzl_vm_env.c +++ b/src/vm/fzl_vm_env.c @@ -28,16 +28,19 @@ static void resize_env_if_needed(fzl_vm_env_t *env) env->table = realloc(env->table, env->remainder * sizeof(fzl_vm_env_entry_t)); } -fzl_value_t fzl_env_lookup(fzl_vm_env_t *env, char *name) +bool fzl_env_lookup(fzl_vm_env_t *env, char *name, fzl_value_t *res) { if (!env) - return null; + return false; for (size_t i = 0; i < env->size; i++) if (strcmp(env->table[i].name, name) == 0) - return env->table[i].value; + { + *res = env->table[i].value; + return true; + } - return (fzl_value_t){.type = FZL_TYPE_NULL}; + return false; } bool fzl_env_contains(fzl_vm_env_t *env, char *name) -- 2.54.0 From 36ed86b07c10add92a8277413ddba0419736b781 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 15 Oct 2026 03:07:20 +0300 Subject: [PATCH 28/34] feat: add tests --- CMakeLists.txt | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 5 +++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa89e5c..8ff03fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,3 +25,69 @@ add_executable(fz src/vm/fzl_vm_debug.c src/vm/main.c) +# +# ========== Compilation tests ========== +# + +enable_testing() + +add_test( + NAME compile_hw + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/hw.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/hw.fzs" +) +add_test( + NAME compile_print_empty + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/print_empty.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/print_empty.fzs" +) +add_test( + NAME compile_problematic_hw + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/problematic_hw.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/problematic_hw.fzs" +) +add_test( + NAME compile_base_math + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/base_math.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/base_math.fzs" +) +add_test( + NAME compile_variables + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/variables.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/variables.fzs" +) +add_test( + NAME compile_conditions + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/conditions.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/conditions.fzs" +) +add_test( + NAME compile_fn + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/fn.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/fn.fzs" +) + +set_tests_properties(compile_problematic_hw PROPERTIES WILL_FAIL TRUE) + +# +# ========== Runtime tests ========== +# + +add_test( + NAME runtime_hw + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/hw.fzb" +) +add_test( + NAME runtime_print_empty + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/print_empty.fzb" +) +add_test( + NAME runtime_base_math + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/base_math.fzb" +) +add_test( + NAME runtime_variables + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/variables.fzb" +) +add_test( + NAME runtime_conditions + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/conditions.fzb" +) +add_test( + NAME runtime_fn + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/fn.fzb" +) + diff --git a/Makefile b/Makefile index 29f7c94..304ff91 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: configure build debug-c debug-vm +.PHONY: configure build debug-c debug-vm test configure: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug @@ -12,3 +12,6 @@ debug-c: build debug-vm: build gdb --args ./build/fz ${FILE} +test: build + ctest --output-on-failure --test-dir build + -- 2.54.0 From 1035b9193c9755d9bf92de39bd44f54e1d872d2b Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 15 Oct 2026 15:52:47 +0300 Subject: [PATCH 29/34] feat: add comments --- exl/comment.fzs | 3 +++ include/compiler/fzl_token.h | 2 ++ src/compiler/fzl_reader.c | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 exl/comment.fzs diff --git a/exl/comment.fzs b/exl/comment.fzs new file mode 100644 index 0000000..883dc04 --- /dev/null +++ b/exl/comment.fzs @@ -0,0 +1,3 @@ +// I'm a comment +printline("Comment is skipped") + diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index bcc8de1..ce60c69 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -16,6 +16,8 @@ typedef enum FZL_TOKTYPE_NAME, // abc FZL_TOKTYPE_TYPE, // i16 + FZL_TOKTYPE_DDASH, // // :D + FZL_TOKTYPE_LPAREN, // ( FZL_TOKTYPE_RPAREN, // ) FZL_TOKTYPE_LBRACE, // { diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 18d5426..adbf95e 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -189,6 +189,17 @@ fzl_token_t *add_double(fzl_reader_t *reader, fzl_toktype_t type) { size_t p = move(reader, 2); return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); + +// Skip symbols until '\n' or EOF +void skip_comment(fzl_reader_t *reader) +{ + move(reader, 2); + + while (reader->position < reader->size) + { + if (reader->source[reader->position++] == '\n') + break; + } } int fzl_read(fzl_reader_t *reader) @@ -210,6 +221,11 @@ int fzl_read(fzl_reader_t *reader) if (curr == '\'' || curr == '"') token = parse_string_literal(reader); + else if (curr == '/' && reader->source[p + 1] == '/') + { + skip_comment(reader); + continue; + } else if (curr == '=' && reader->source[p + 1] == '=') token = add_double(reader, FZL_TOKTYPE_EQEQ); else if (curr == '>' && reader->source[p + 1] == '=') -- 2.54.0 From 69b2253c34917c1981d537c96413d7de85b66258 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 15 Oct 2026 17:20:43 +0300 Subject: [PATCH 30/34] feat: enhance compiler - Add comments and test for it - Add semicolon as required end of some expressions - Add new arguments parser - Add multi-file compilation - Add colors in output --- CMakeLists.txt | 10 ++ exl/base_math.fzs | 12 +- exl/comment.fzs | 2 +- exl/conditions.fzs | 10 +- exl/fn.fzs | 20 +-- exl/hw.fzs | 2 +- exl/print_empty.fzs | 2 +- exl/problematic_hw.fzs | 2 +- exl/variables.fzs | 18 +-- include/common/fzl_colors.h | 10 ++ include/compiler/fzl_reader.h | 7 +- include/compiler/fzl_token.h | 3 +- include/compiler/utils/fzl_linked.h | 49 +++++++ include/compiler/utils/fzl_options.h | 25 ++++ src/compiler/fzl_parser.c | 208 +++++++++++++++++---------- src/compiler/fzl_reader.c | 123 +++++++++++----- src/compiler/fzl_token.c | 3 +- src/compiler/fzl_walker.c | 70 ++++----- src/compiler/main.c | 198 ++++++++----------------- src/compiler/utils/fzl_linked.c | 159 ++++++++++++++++++++ src/compiler/utils/fzl_options.c | 131 +++++++++++++++++ src/vm/fzl_vm.c | 2 - 22 files changed, 739 insertions(+), 327 deletions(-) create mode 100644 include/common/fzl_colors.h create mode 100644 include/compiler/utils/fzl_linked.h create mode 100644 include/compiler/utils/fzl_options.h create mode 100644 src/compiler/utils/fzl_linked.c create mode 100644 src/compiler/utils/fzl_options.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff03fc..c6692f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ add_executable(fzc src/compiler/fzl_writer.c src/compiler/utils/fzl_const_pool.c src/compiler/utils/fzl_symtable.c + src/compiler/utils/fzl_linked.c + src/compiler/utils/fzl_options.c src/compiler/main.c) # Virtual machine @@ -59,6 +61,10 @@ add_test( NAME compile_fn COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/fn.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/fn.fzs" ) +add_test( + NAME compile_comment + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/comment.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/comment.fzs" +) set_tests_properties(compile_problematic_hw PROPERTIES WILL_FAIL TRUE) @@ -90,4 +96,8 @@ add_test( NAME runtime_fn COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/fn.fzb" ) +add_test( + NAME runtime_comment + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/comment.fzb" +) diff --git a/exl/base_math.fzs b/exl/base_math.fzs index b5e368c..b7e15c7 100644 --- a/exl/base_math.fzs +++ b/exl/base_math.fzs @@ -1,7 +1,7 @@ -printline(2 + 2 * 2) -printline(-1 + 1) -printline(2 ^ 2) -printline(1 + 1 * 1 / 1) -printline(4 % 2) -printline(10 / 2) +printline(2 + 2 * 2); +printline(-1 + 1); +printline(2 ^ 2); +printline(1 + 1 * 1 / 1); +printline(4 % 2); +printline(10 / 2); diff --git a/exl/comment.fzs b/exl/comment.fzs index 883dc04..0fd39ee 100644 --- a/exl/comment.fzs +++ b/exl/comment.fzs @@ -1,3 +1,3 @@ // I'm a comment -printline("Comment is skipped") +printline("Comment is skipped");;;;; diff --git a/exl/conditions.fzs b/exl/conditions.fzs index 4e7dbf4..b7c5a83 100644 --- a/exl/conditions.fzs +++ b/exl/conditions.fzs @@ -1,16 +1,16 @@ -bool flag = true +bool flag = true; if (!flag) - printline("buhhh, flag is not true") + printline("buhhh, flag is not true"); else if (2 + 2 >= 4) { - printline ("two plus two is four") + printline ("two plus two is four"); } else { - printline("gotcha") + printline("gotcha"); } { - printline("block after") + printline("block after"); } diff --git a/exl/fn.fzs b/exl/fn.fzs index b08dec2..e919ada 100644 --- a/exl/fn.fzs +++ b/exl/fn.fzs @@ -1,24 +1,24 @@ int fib(int n) { if (n <= 1) - return n + return n; - return fib(n - 1) + fib(n - 2) + return fib(n - 1) + fib(n - 2); } uint sum(uint a, uint b) { - return a + b + return a + b; } -printline("Test #1: fibonacci") -int result = fib(7) +printline("Test #1: fibonacci"); +int result = fib(7); if (result == 13) - printline("Recurse test passed") -else printline("Recurse test failed") + printline("Recurse test passed"); +else printline("Recurse test failed"); -printline("Test #2: sum") +printline("Test #2: sum"); if (sum(2, 2) == 4) - printline("Sum test passed") -else printline("Sum test failed") + printline("Sum test passed"); +else printline("Sum test failed"); diff --git a/exl/hw.fzs b/exl/hw.fzs index 173a105..131b32e 100644 --- a/exl/hw.fzs +++ b/exl/hw.fzs @@ -1,2 +1,2 @@ -printline('hello, world') +printline('hello, world'); diff --git a/exl/print_empty.fzs b/exl/print_empty.fzs index a9baaa0..67c1545 100644 --- a/exl/print_empty.fzs +++ b/exl/print_empty.fzs @@ -1 +1 @@ -printline("") +printline(""); diff --git a/exl/problematic_hw.fzs b/exl/problematic_hw.fzs index 88c437d..ec0ce93 100644 --- a/exl/problematic_hw.fzs +++ b/exl/problematic_hw.fzs @@ -1 +1 @@ -printline("hello) +printline("hello); diff --git a/exl/variables.fzs b/exl/variables.fzs index 18216b8..6ba588d 100644 --- a/exl/variables.fzs +++ b/exl/variables.fzs @@ -1,13 +1,13 @@ -uint work_it = 21 * 2 ^ 2 -int make_it = 10 +uint work_it = 21 * 2 ^ 2; +int make_it = 10; -u64 do_it -do_it = 15 +u64 do_it; +do_it = 15; -printline(work_it) -printline(make_it + do_it) +printline(work_it); +printline(make_it + do_it); -char make_us -str floyd = "hello, Pink Floyd!" -printline(floyd) +char make_us; +str floyd = "hello, Pink Floyd!"; +printline(floyd); diff --git a/include/common/fzl_colors.h b/include/common/fzl_colors.h new file mode 100644 index 0000000..528537d --- /dev/null +++ b/include/common/fzl_colors.h @@ -0,0 +1,10 @@ +#ifndef FZL_COLORS_H +#define FZL_COLORS_H + +#define FZL_CERROR "\033[0;31m" +#define FZL_CWARNING "\033[0;33m" +#define FZL_COK "\033[0;32m" +#define FZL_CDEBUG "\033[0;34m" +#define FZL_CRST "\033[0m" + +#endif // FZL_COLORS_H diff --git a/include/compiler/fzl_reader.h b/include/compiler/fzl_reader.h index 2a85dac..140b0f3 100644 --- a/include/compiler/fzl_reader.h +++ b/include/compiler/fzl_reader.h @@ -2,6 +2,7 @@ #define FZL_READER_H #include +#include #include #include #include @@ -19,16 +20,16 @@ typedef struct fzl_reader_ll_t *first; fzl_reader_ll_t *last; + fzl_linked_t *linked; + char *source; size_t size; size_t position; - size_t line_start; - size_t line_n; } fzl_reader_t; // Allocate new reader -fzl_reader_t *fzl_mkreader(char *source); +fzl_reader_t *fzl_mkreader(fzl_linked_t *linked); // Read source file int fzl_read(fzl_reader_t *reader); diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index ce60c69..bb9a5de 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -61,11 +61,12 @@ typedef struct bool store_value; void *value; uint16_t value_sz; + char *filename; } fzl_token_t; // Allocate new token fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col, - void *value, size_t value_sz); + void *value, size_t value_sz, char *filename); // Free token and its contents void fzl_freetoken(fzl_token_t *token); diff --git a/include/compiler/utils/fzl_linked.h b/include/compiler/utils/fzl_linked.h new file mode 100644 index 0000000..0c7c360 --- /dev/null +++ b/include/compiler/utils/fzl_linked.h @@ -0,0 +1,49 @@ +#ifndef FZL_LINKED_H +#define FZL_LINKED_H + +#include +#include + +// Linked file descriptor +typedef struct +{ + char *name; + size_t start; + size_t end; +} fzl_linked_entry_t; + +// Linked descriptor +typedef struct +{ + fzl_linked_entry_t *entries; + size_t entries_count; + + char *source; + size_t total_sz; +} fzl_linked_t; + +// Position in file +typedef struct +{ + size_t line; + size_t col; + + char *filename; +} fzl_file_loc; + +// Allocated new linked +fzl_linked_t *fzl_mklinked(); + +// Read specified files +bool fzl_linked_add_files(fzl_linked_t *l, char **files, size_t files_count); + +// Get last error from linked +char *fzl_linked_geterror(); + +// Get filename and position in it by reader cursor +bool fzl_linked_getpos(fzl_linked_t *l, size_t pos, fzl_file_loc *res); + +// Free linked obj +void fzl_freelinked(fzl_linked_t *linked); + +#endif // FZL_LINKED_H diff --git a/include/compiler/utils/fzl_options.h b/include/compiler/utils/fzl_options.h new file mode 100644 index 0000000..896693c --- /dev/null +++ b/include/compiler/utils/fzl_options.h @@ -0,0 +1,25 @@ +#ifndef FZL_OPTIONS_H +#define FZL_OPTIONS_H + +#include +#include + +// Compiler options +typedef struct +{ + char **files; + size_t files_count; + + char *outname; + + bool should_continue; + bool verbose; +} fzl_options_t; + +// Parse command line arguments +fzl_options_t *fzl_mkoptions(int argc, char **argv); + +// Free options +void fzl_freeoptions(fzl_options_t *options); + +#endif // FZL_OPTIONS_H diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index a581727..0bb7dae 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -7,6 +7,7 @@ #include char plast_err[256]; +fzl_token_t *last_known_tok = NULL; fzl_parser_t *fzl_mkparser(fzl_reader_ll_t *first) { @@ -50,6 +51,8 @@ static fzl_token_t *peek(fzl_parser_t *parser) { if (!parser || !parser->cursor) return NULL; + + last_known_tok = parser->cursor->token; return parser->cursor->token; } @@ -157,8 +160,8 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) fzl_node_t *node = parse_expression(parser); if (!is(parser, FZL_TOKTYPE_RPAREN)) { - snprintf(plast_err, sizeof(plast_err), "expected ')' at %ld:%ld", - parser->cursor->token->ln, parser->cursor->token->cn); + snprintf(plast_err, sizeof(plast_err), "'%s': expected ')' at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } consume(parser); @@ -169,9 +172,28 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) fzl_token_t *name = consume(parser); return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); } + else if (is(parser, FZL_TOKTYPE_TYPE)) + { + fzl_token_t *type = consume(parser); + fzl_token_t *name = consume(parser); + if (name->type != FZL_TOKTYPE_NAME) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected name after type at %ld:%ld", type->filename, + type->ln, type->cn); + return NULL; + } - snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %ld:%ld", - peek(parser)->ln, peek(parser)->cn); + fzl_node_t *decl = fzl_mknode(FZL_NODETYPE_DECLARATION, type); + decl->var.type = type; + decl->var.identifier = fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); + return decl; + } + + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': failed to parse primary at %ld:%ld", peek(parser)->filename, + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -186,8 +208,10 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) if (!left) { - snprintf(plast_err, sizeof(plast_err), "failed to parse postfix at %ld:%ld", - peek(parser)->ln, peek(parser)->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': failed to parse postfix at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -226,8 +250,8 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) { free(args); snprintf(plast_err, sizeof(plast_err), - "expected ')' after argument list at %ld:%ld", peek(parser)->ln, - peek(parser)->cn); + "'%s': expected ')' after argument list at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -274,8 +298,10 @@ fzl_node_t *parse_power(fzl_parser_t *parser) if (!left) { - snprintf(plast_err, sizeof(plast_err), "failed to parse power at %ld:%ld", - peek(parser)->ln, peek(parser)->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': failed to parse power at %ld:%ld", peek(parser)->filename, + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -286,9 +312,10 @@ fzl_node_t *parse_power(fzl_parser_t *parser) right = parse_unary(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), - "failed to parse right side of power at %ld:%ld", - peek(parser)->ln, peek(parser)->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': failed to parse right side of power at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -314,8 +341,9 @@ fzl_node_t *parse_factor(fzl_parser_t *parser) if (!left) { if (!plast_err[0]) - snprintf(plast_err, sizeof(plast_err), "invalid expression at %ld:%ld", - parser->cursor->token->ln, parser->cursor->token->cn); + snprintf(plast_err, sizeof(plast_err), + "'%s': invalid expression at %ld:%ld", peek(parser)->filename, + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -327,9 +355,10 @@ fzl_node_t *parse_factor(fzl_parser_t *parser) right = parse_factor(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), - "unfinished factor (mul/div) expression at %ld:%ld", - left->token->ln, left->token->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': unfinished factor (mul/div) expression at %ld:%ld", + left->token->filename, left->token->ln, left->token->cn); return NULL; } @@ -353,8 +382,10 @@ fzl_node_t *parse_term(fzl_parser_t *parser) if (!left) { - snprintf(plast_err, sizeof(plast_err), "invalid expression at %ld:%ld", - parser->cursor->token->ln, parser->cursor->token->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': invalid expression at %ld:%ld", peek(parser)->filename, + peek(parser)->ln, peek(parser)->cn); return NULL; } @@ -365,9 +396,10 @@ fzl_node_t *parse_term(fzl_parser_t *parser) right = parse_factor(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), - "unfinished term (add/sub) expression at %ld:%ld", - left->token->ln, left->token->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': unfinished term (add/sub) expression at %ld:%ld", + left->token->filename, left->token->ln, left->token->cn); return NULL; } @@ -400,8 +432,10 @@ fzl_node_t *parse_cond(fzl_parser_t *parser) right = parse_term(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", - op->ln, op->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': unfinished expression at %ld:%ld", op->filename, op->ln, + op->cn); return NULL; } @@ -456,8 +490,10 @@ fzl_node_t *parse_and(fzl_parser_t *parser) right = parse_cond(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", - or->ln, or->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': unfinished expression at %ld:%ld", or->filename, or->ln, + or->cn); return NULL; } @@ -488,8 +524,10 @@ fzl_node_t *parse_or(fzl_parser_t *parser) right = parse_and(parser); if (!right) { - snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", - or->ln, or->cn); + if (!plast_err[0]) + snprintf(plast_err, sizeof(plast_err), + "'%s': unfinished expression at %ld:%ld", or->filename, or->ln, + or->cn); return NULL; } @@ -508,6 +546,8 @@ fzl_node_t *parse_expression(fzl_parser_t *parser) { return parse_and(parser); } // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { + fzl_node_t *res = NULL; + if (is(parser, FZL_TOKTYPE_TYPE)) { fzl_token_t *type = peek(parser); @@ -521,8 +561,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (tok->type != FZL_TOKTYPE_NAME) { snprintf(plast_err, sizeof(plast_err), - "expected name in function definition at %ld:%ld", tok->ln, - tok->cn); + "'%s': expected name in function definition at %ld:%ld", + tok->filename, tok->ln, tok->cn); return NULL; } @@ -530,7 +570,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (lparen->type != FZL_TOKTYPE_LPAREN) { snprintf(plast_err, sizeof(plast_err), - "expected '(' after name at %ld:%ld", lparen->ln, lparen->cn); + "'%s': expected '(' after name at %ld:%ld", lparen->filename, + lparen->ln, lparen->cn); return NULL; } @@ -540,7 +581,7 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) while (peek(parser) && !is(parser, FZL_TOKTYPE_RPAREN)) { - fzl_node_t *arg = parse_statement(parser); + fzl_node_t *arg = parse_expression(parser); if (!arg) return NULL; @@ -560,16 +601,16 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (rparen->type != FZL_TOKTYPE_RPAREN) { snprintf(plast_err, sizeof(plast_err), - "expected ')' after argument list at %ld:%ld", rparen->ln, - rparen->cn); + "'%s': expected ')' after argument list at %ld:%ld", + rparen->filename, rparen->ln, rparen->cn); return NULL; } if (!is(parser, FZL_TOKTYPE_LBRACE)) { fzl_token_t *tok = peek(parser); - snprintf(plast_err, sizeof(plast_err), "expected '{' at %ld:%ld", - tok->ln, tok->cn); + snprintf(plast_err, sizeof(plast_err), "'%s': expected '{' at %ld:%ld", + tok->filename, tok->ln, tok->cn); return NULL; } @@ -586,14 +627,15 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return node; } - - if (type->type == FZL_TOKTYPE_TYPE || next(parser)->type == FZL_TOKTYPE_EQ) + else if (type->type == FZL_TOKTYPE_TYPE || + next(parser)->type == FZL_TOKTYPE_EQ) { fzl_node_t *identifier = parse_primary(parser); if (!identifier || identifier->type != FZL_NODETYPE_IDENTIFIER) { - snprintf(plast_err, sizeof(plast_err), "expected identifier at %ld:%ld", + snprintf(plast_err, sizeof(plast_err), + "'%s': expected identifier at %ld:%ld", type->filename, type->ln, type->cn); return NULL; } @@ -602,29 +644,28 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) left->var.type = type; left->var.identifier = identifier; - if (!is(parser, FZL_TOKTYPE_EQ)) - return left; - - fzl_node_t *right; - while (is(parser, FZL_TOKTYPE_EQ)) + if (is(parser, FZL_TOKTYPE_EQ)) { - fzl_token_t *asn = consume(parser); - right = parse_expression(parser); - if (!right) - return NULL; + fzl_node_t *right; + while (is(parser, FZL_TOKTYPE_EQ)) + { + fzl_token_t *asn = consume(parser); + right = parse_expression(parser); + if (!right) + return NULL; - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_ASSIGN, asn); - node->binary_op.left = left; - node->binary_op.right = right; + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_ASSIGN, asn); + node->binary_op.left = left; + node->binary_op.right = right; - left = node; + left = node; + } } - return left; + res = left; } } - - if (is(parser, FZL_TOKTYPE_NAME) && next(parser)->type == FZL_TOKTYPE_EQ) + else if (is(parser, FZL_TOKTYPE_NAME) && next(parser)->type == FZL_TOKTYPE_EQ) { fzl_token_t *name = consume(parser); fzl_node_t *left = fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); @@ -644,37 +685,33 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) left = assn; } - return left; + res = left; } - - if (is(parser, FZL_TOKTYPE_RETURN)) + else if (is(parser, FZL_TOKTYPE_RETURN)) { fzl_token_t *ret = consume(parser); fzl_node_t *node = fzl_mknode(FZL_NODETYPE_RETURN, ret); - if (is(parser, FZL_TOKTYPE_DOT)) + if (!is(parser, FZL_TOKTYPE_SEMICOLON)) { - consume(parser); - return node; + fzl_node_t *retval = parse_expression(parser); + if (!retval) + return NULL; + + node->ret.value = retval; } - fzl_node_t *retval = parse_expression(parser); - if (!retval) - return NULL; - - node->ret.value = retval; - return node; + res = node; } - - if (is(parser, FZL_TOKTYPE_IF)) + else if (is(parser, FZL_TOKTYPE_IF)) { fzl_token_t *if_tok = consume(parser); fzl_token_t *lparen = consume(parser); if (lparen->type != FZL_TOKTYPE_LPAREN) { snprintf(plast_err, sizeof(plast_err), - "expected '(' before condition at %ld:%ld", lparen->ln, - lparen->cn); + "'%s': expected '(' before condition at %ld:%ld", + lparen->filename, lparen->ln, lparen->cn); return NULL; } @@ -686,8 +723,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) if (rparen->type != FZL_TOKTYPE_RPAREN) { snprintf(plast_err, sizeof(plast_err), - "expected ')' after condition at %ld:%ld", rparen->ln, - rparen->cn); + "'%s': expected ')' after condition at %ld:%ld", + rparen->filename, rparen->ln, rparen->cn); return NULL; } @@ -711,8 +748,7 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return stmt_node; } - - if (is(parser, FZL_TOKTYPE_LBRACE)) + else if (is(parser, FZL_TOKTYPE_LBRACE)) { fzl_token_t *block_tok = consume(parser); @@ -735,10 +771,11 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) stmts[count++] = node; } fzl_token_t *rbrace = consume(parser); - if (!rbrace || rbrace->type != FZL_TOKTYPE_RBRACE) + if (rbrace && rbrace->type != FZL_TOKTYPE_RBRACE) { snprintf(plast_err, sizeof(plast_err), - "missing '}' after block at %ld:%ld", rbrace->ln, rbrace->cn); + "'%s': missing '}' after block at %ld:%ld", rbrace->filename, + rbrace->ln, rbrace->cn); return NULL; } @@ -748,7 +785,20 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return node; } - return parse_expression(parser); + if (!res) + res = parse_expression(parser); + + if (!is(parser, FZL_TOKTYPE_SEMICOLON)) + { + printf("Current token are %d\n", last_known_tok->type); + snprintf(plast_err, sizeof(plast_err), "'%s': missing ';' at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + while (is(parser, FZL_TOKTYPE_SEMICOLON)) + consume(parser); + + return res; } // Internal parsing entry point diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index adbf95e..15752a1 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -1,4 +1,5 @@ #include "compiler/fzl_token.h" +#include "compiler/utils/fzl_linked.h" #include #include #include @@ -9,7 +10,7 @@ // Last error from reader static char last_err[256] = {0}; -fzl_reader_t *fzl_mkreader(char *source) +fzl_reader_t *fzl_mkreader(fzl_linked_t *linked) { fzl_reader_ll_t *first_node = malloc(sizeof(fzl_reader_ll_t)); first_node->token = NULL; @@ -18,11 +19,10 @@ fzl_reader_t *fzl_mkreader(char *source) fzl_reader_t *reader = malloc(sizeof(fzl_reader_t)); reader->first = first_node; reader->last = first_node; - reader->source = source; - reader->size = strlen(source); + reader->linked = linked; + reader->source = linked->source; + reader->size = linked->total_sz; reader->position = 0; - reader->line_n = 1; - reader->line_start = 0; return reader; } @@ -30,21 +30,12 @@ fzl_reader_t *fzl_mkreader(char *source) // --- Read zone --- // Move cursor forward +// @deprecated static size_t move(fzl_reader_t *reader, size_t len) { if (!reader) return 0; - char *src = reader->source; - for (size_t i = 0; i < len; i++) - { - if (src[reader->position + i] == '\n') - { - reader->line_n++; - reader->line_start = reader->position + i + 1; - } - } - return reader->position += len; } @@ -71,17 +62,24 @@ fzl_token_t *parse_name(fzl_reader_t *reader) move(reader, size); fzl_token_t *tok; - size_t line = reader->line_n; - size_t pos = start - reader->line_start; + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, start, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return NULL; + } if (strcmp(value, "return") == 0) - tok = fzl_mktoken(FZL_TOKTYPE_RETURN, line, pos, value, size); + tok = + fzl_mktoken(FZL_TOKTYPE_RETURN, l.line, l.col, value, size, l.filename); else if (strcmp(value, "if") == 0) - tok = fzl_mktoken(FZL_TOKTYPE_IF, line, pos, value, size); + tok = fzl_mktoken(FZL_TOKTYPE_IF, l.line, l.col, value, size, l.filename); else if (strcmp(value, "else") == 0) - tok = fzl_mktoken(FZL_TOKTYPE_ELSE, line, pos, value, size); + tok = fzl_mktoken(FZL_TOKTYPE_ELSE, l.line, l.col, value, size, l.filename); else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0) - tok = fzl_mktoken(FZL_TOKTYPE_BOOL_LITERAL, line, pos, value, size); + tok = fzl_mktoken(FZL_TOKTYPE_BOOL_LITERAL, l.line, l.col, value, size, + l.filename); else if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || strcmp(value, "bool") == 0 || strcmp(value, "uint") == 0 || strcmp(value, "int") == 0 || strcmp(value, "i8") == 0 || @@ -89,9 +87,9 @@ fzl_token_t *parse_name(fzl_reader_t *reader) strcmp(value, "i64") == 0 || strcmp(value, "u8") == 0 || strcmp(value, "u16") == 0 || strcmp(value, "u32") == 0 || strcmp(value, "u64") == 0) - tok = fzl_mktoken(FZL_TOKTYPE_TYPE, line, pos, value, size); + tok = fzl_mktoken(FZL_TOKTYPE_TYPE, l.line, l.col, value, size, l.filename); else - tok = fzl_mktoken(FZL_TOKTYPE_NAME, line, pos, value, size); + tok = fzl_mktoken(FZL_TOKTYPE_NAME, l.line, l.col, value, size, l.filename); free(value); return tok; @@ -120,26 +118,33 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader) } while (reader->source[lpos] != open_symbol && lpos < reader->size); size++; + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, reader->position, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return NULL; + } + if (close_symbol != open_symbol) { - snprintf(last_err, sizeof(last_err), "unterminated string at %ld:%ld", - reader->line_n, reader->position - reader->line_start); + snprintf(last_err, sizeof(last_err), "'%s': unterminated string at %ld:%ld", + l.filename, l.line, l.col); return NULL; } if (size == 2) { fzl_token_t *tok = - fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->line_n, - reader->position - reader->line_start, "", 0); + fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, l.line, l.col, "", 0, l.filename); move(reader, 2); return tok; } char *value = malloc(size - 2); memcpy(value, reader->source + reader->position + 1, size - 2); - fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, reader->line_n, - lpos - reader->line_start, value, size - 2); + fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, l.line, l.col, value, + size - 2, l.filename); free(value); move(reader, size); @@ -166,8 +171,16 @@ fzl_token_t *parse_number(fzl_reader_t *reader) memcpy(value, reader->source + reader->position, size); value[size] = '\0'; - fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, reader->line_n, - lpos - reader->line_start, value, size); + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, reader->position, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return NULL; + } + + fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, l.line, l.col, value, + size, l.filename); free(value); if (!tok) @@ -181,14 +194,31 @@ fzl_token_t *parse_number(fzl_reader_t *reader) fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type) { size_t p = move(reader, 1); - return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, p, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return NULL; + } + + return fzl_mktoken(type, l.line, l.col, NULL, 0, l.filename); } // Add double-char token fzl_token_t *add_double(fzl_reader_t *reader, fzl_toktype_t type) { size_t p = move(reader, 2); - return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, p, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return NULL; + } + + return fzl_mktoken(type, l.line, l.col, NULL, 0, l.filename); +} // Skip symbols until '\n' or EOF void skip_comment(fzl_reader_t *reader) @@ -277,16 +307,35 @@ int fzl_read(fzl_reader_t *reader) token = parse_number(reader); else { - sprintf(last_err, "undefined token '%c' at %ld:%ld", curr, reader->line_n, - p - reader->line_start); + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, p, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return -1; + } + + sprintf(last_err, "'%s': undefined token '%c' at %ld:%ld", l.filename, + curr, l.line, l.col); return -1; } if (!token) { if (!last_err[0]) - sprintf(last_err, "cannot parse token at %ld:%ld", reader->line_n, - p - reader->line_start); + { + fzl_file_loc l; + bool ok = fzl_linked_getpos(reader->linked, p, &l); + if (!ok) + { + snprintf(last_err, sizeof(last_err), "failed to locate line:col"); + return -1; + } + + sprintf(last_err, "'%s': cannot parse token at %ld:%ld", l.filename, + l.line, l.col); + } + return -1; } diff --git a/src/compiler/fzl_token.c b/src/compiler/fzl_token.c index 96e761e..8ba49a7 100644 --- a/src/compiler/fzl_token.c +++ b/src/compiler/fzl_token.c @@ -4,7 +4,7 @@ #include fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col, - void *value, size_t value_len) + void *value, size_t value_len, char *filename) { char *valholder = malloc(value_len + 1); memcpy(valholder, value, value_len); @@ -17,6 +17,7 @@ fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col, token->store_value = value_len > 0; token->value = valholder; token->value_sz = value_len; + token->filename = filename; return token; } diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 8daeadb..eb46f75 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -167,12 +167,14 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (putres != FZL_SYMTABLE_RESULT_OK) { if (putres == FZL_SYMTABLE_RESULT_EXISTS) - snprintf(wlast_err, sizeof(wlast_err), - "cannot define already declared name '%s' at %ld:%ld", - (char *)node->fn.name->value, node->token->ln, - node->token->cn); + snprintf( + wlast_err, sizeof(wlast_err), + "'%s': cannot define already declared name '%s' at %ld:%ld", + node->token->filename, (char *)node->fn.name->value, + node->token->ln, node->token->cn); else - snprintf(wlast_err, sizeof(wlast_err), "failed to store name '%s'", + snprintf(wlast_err, sizeof(wlast_err), + "'%s': failed to store name '%s'", node->fn.name->filename, (char *)node->fn.name->value); return (fzl_walk_res_t){.error = true}; @@ -216,8 +218,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { if (!walker->ctx) { - snprintf(wlast_err, sizeof(wlast_err), - "return cannot be used outside of functions"); + snprintf( + wlast_err, sizeof(wlast_err), + "'%s': return cannot be used outside of functions (at %ld:%ld)", + node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -230,10 +234,11 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (!is_compatible(retres.type, tok_to_type(walker->ctx->fn->fn.type))) { - snprintf(wlast_err, sizeof(wlast_err), - "incompatible types between return and function type at " - "%ld:%ld", - node->token->ln, node->token->cn); + snprintf( + wlast_err, sizeof(wlast_err), + "'%s': incompatible types between return and function type at " + "%ld:%ld", + node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -278,8 +283,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (!is_compatible(left.type, right.type)) { snprintf(wlast_err, sizeof(wlast_err), - "incompatible types at %ld:%ld", node->token->ln, - node->token->cn); + "'%s': incompatible types at %ld:%ld", node->token->filename, + node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -325,8 +330,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (!is_compatible(left.type, right.type)) { snprintf(wlast_err, sizeof(wlast_err), - "incompatible types at %ld:%ld", node->token->ln, - node->token->cn); + "'%s': incompatible types at %ld:%ld", node->token->filename, + node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -400,9 +405,9 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) 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); + "'%s': cannot find symbol '%s' at %ld:%ld", + node->token->filename, (char *)node->token->value, + node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -446,8 +451,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (node->call.args_count < 1) { snprintf(wlast_err, sizeof(wlast_err), - "'printline' call with zero arguments at %ld:%ld", - node->token->ln, node->token->cn); + "'%s': 'printline' call with zero arguments at %ld:%ld", + node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; @@ -534,18 +539,19 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { fzl_token_t *ltok = node->binary_op.left->token; snprintf(wlast_err, sizeof(wlast_err), - "left operand at %ld:%ld is not compatible to integer types", - ltok->ln, ltok->cn); + "'%s': left operand at %ld:%ld is not compatible to integer " + "types", + ltok->filename, ltok->ln, ltok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (rres.type < FZL_TYPE_INT8) { fzl_token_t *rtok = node->binary_op.right->token; - snprintf( - wlast_err, sizeof(wlast_err), - "right operand at %ld:%ld is not compatible to integer types", - rtok->ln, rtok->cn); + snprintf(wlast_err, sizeof(wlast_err), + "'%s': right operand at %ld:%ld is not compatible to " + "integer types", + rtok->filename, rtok->ln, rtok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -586,8 +592,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { fzl_token_t *tok = node->unary_op.operand->token; snprintf(wlast_err, sizeof(wlast_err), - "incompatible with unary operator type at %ld:%ld", tok->ln, - tok->cn); + "'%s': incompatible with unary operator type at %ld:%ld", + tok->filename, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -645,8 +651,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { 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); + "'%s': name '%s' is not exists at %ld:%ld", tok->filename, + (char *)tok->value, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } @@ -654,8 +660,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { 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); + "'%s': R-Value is not compatible with L-Value at %ld:%ld", + tok->filename, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } diff --git a/src/compiler/main.c b/src/compiler/main.c index f8b52e3..305a83c 100644 --- a/src/compiler/main.c +++ b/src/compiler/main.c @@ -1,147 +1,69 @@ +#include #include #include #include #include #include +#include +#include #include #include #include #include -// Build arguments setup descriptor -typedef struct -{ - char *filepath; - char *outpath; - bool verbose; - bool help; -} build_args_t; - -// Print command usage hint -void print_usage() -{ - printf("Usage: fzc [flags] \n"); - printf("Flags:\n"); - printf("\t-v\t--verbose\tdisplay debug information\n"); - printf("\t-h\t--help\t\tprint usage hint (which you're looking at)\n"); -} - -// Read the arguments and build `build_args_t` struct -build_args_t *getargs(int argc, char **argv) -{ - build_args_t *args = malloc(sizeof(build_args_t)); - if (!args) - return NULL; - - args->filepath = NULL; - args->outpath = NULL; - args->verbose = false; - args->help = false; - - size_t args_total = 0; - if (argc > 2) - { - for (int i = 1; i < argc; i++) - { - if (argv[i][0] != '-') - continue; - - args_total++; - - if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) - { - args->help = true; - } - else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) - { - args->verbose = true; - } - else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) - { - if (!argv[i + 1] || argv[i + 1][0] == '-') - { - printf("Error: file path is not specified.\n"); - return NULL; - } - args->outpath = argv[++i]; - } - else - { - printf("Error: unknown argument '%s'\n", argv[i]); - return NULL; - } - } - } - - if (argc - 1 - args_total < 1) - { - printf("Error: source code file is not specified.\n"); - print_usage(); - return NULL; - } - - if (argv[argc - 1][0] == '-') - { - printf("Error: last argument MUST be a path to file.\n"); - printf("Write 'fzc --help' to read help\n"); - return NULL; - } - - args->filepath = argv[argc - 1]; - return args; -} - int main(int argc, char **argv) { - build_args_t *args = getargs(argc, argv); - if (args == NULL) + fzl_options_t *opts = fzl_mkoptions(argc, argv); + if (!opts) return -1; - else if (args->help) + else if (!opts->should_continue) return 0; - if (args->verbose) - printf("> analyze\n-> reading file..."); + if (opts->verbose) + printf("%s> analyze\n-> reading file...%s", FZL_CDEBUG, FZL_CRST); - char *source; - FILE *fd = fopen(args->filepath, "r"); - if (!fd) + fzl_linked_t *linked = fzl_mklinked(); + if (!linked) { - printf("Error: failed to open file '%s'\n", args->filepath); + char *lasterr = fzl_linked_geterror(); + if (lasterr[0]) + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, lasterr); return -1; } - fseek(fd, 0L, SEEK_END); - size_t fsz = ftell(fd); - rewind(fd); - - source = malloc(fsz + 1); - fread(source, fsz, 1, fd); - fclose(fd); - source[fsz] = '\0'; - - if (args->verbose) + bool read_result = + fzl_linked_add_files(linked, opts->files, opts->files_count); + if (!read_result) { - printf("ok!\n"); - printf("--> Red %ld bytes\n", fsz); - printf("-> tokenizing..."); + char *lasterr = fzl_linked_geterror(); + if (lasterr[0]) + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, lasterr); + return -1; } - fzl_reader_t *rd = fzl_mkreader(source); - int result = fzl_read(rd); + if (opts->verbose) + { + printf("%sok%s!\n", FZL_COK, FZL_CRST); + printf("%s--> Red %ld bytes%s\n", FZL_CDEBUG, linked->total_sz, FZL_CRST); + printf("%s-> tokenizing...%s", FZL_CDEBUG, FZL_CRST); + } + + fzl_reader_t *rd = fzl_mkreader(linked); + int result = fzl_read(rd); if (result != 0) { - printf("error: %s\n", fzl_reader_geterror()); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_reader_geterror()); return -1; } - if (args->verbose) - printf("ok!\n"); + if (opts->verbose) + printf("%sok%s!\n", FZL_COK, FZL_CRST); fzl_reader_ll_t *entry = rd->first; - if (args->verbose) + if (opts->verbose) { - printf("--> structure: "); + printf("%s--> structure: %s", FZL_CDEBUG, FZL_CRST); while (entry) { if (entry->token) @@ -157,42 +79,42 @@ int main(int argc, char **argv) } printf("\n"); - printf("-> parsing..."); + printf("%s-> parsing...%s", FZL_CDEBUG, FZL_CRST); } fzl_parser_t *parser = fzl_mkparser(rd->first); - result = fzl_parse(parser); + result = fzl_parse(parser); if (result != 0) { - printf("Error: %s\n", fzl_parser_geterror()); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_parser_geterror()); return -1; } - if (args->verbose) + if (opts->verbose) { - printf("ok!\n"); + printf("%sok%s!\n", FZL_COK, FZL_CRST); fzl_debug_print_ast(parser->root); - printf("> building\n"); - printf("-> walking AST...\n"); + printf("%s> building%s\n", FZL_CDEBUG, FZL_CRST); + printf("%s-> walking AST...%s\n", FZL_CDEBUG, FZL_CRST); } fzl_walker_t *walker = fzl_mkwalker(parser->root); - result = fzl_walk(walker); + result = fzl_walk(walker); if (result != 0) { - printf("Error: %s\n", fzl_walker_geterror()); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_walker_geterror()); return -1; } - if (args->verbose) + if (opts->verbose) { - printf("ok!\n"); + printf("%sok%s!\n", FZL_COK, FZL_CRST); - printf("--> Constants table:\n"); + printf("%s--> Constants table%s:\n", FZL_CDEBUG, FZL_CRST); for (size_t i = 0; i < walker->consts->size; i++) printf("---> %s\n", walker->consts->array[i].as.string.chars); - printf("--> Ops:\n"); + printf("%s--> Ops%s:\n", FZL_CDEBUG, FZL_CRST); printf("---> "); for (size_t i = 0; i < walker->op_count; i++) { @@ -202,40 +124,40 @@ int main(int argc, char **argv) } printf("\n"); - printf("-> preparing final write\n"); + printf("%s-> preparing final write%s\n", FZL_CDEBUG, FZL_CRST); } char outpath[256]; - if (args->outpath) - strcpy(outpath, args->outpath); + if (opts->outname) + strcpy(outpath, opts->outname); else strcpy(outpath, "a.fzb"); - if (args->verbose) + if (opts->verbose) { - printf("-> output file path is: %s\n", outpath); - printf("-> writing file..."); + printf("%s-> output file path is%s: %s\n", FZL_CDEBUG, FZL_CRST, outpath); + printf("%s-> writing file...%s", FZL_CDEBUG, FZL_CRST); } fzl_writer_t *writer = fzl_mkwriter(outpath); - result = fzl_write(writer, walker); + result = fzl_write(writer, walker); if (result != 0) { - printf("Error: %s\n", fzl_writer_geterror()); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_writer_geterror()); return -1; } - if (args->verbose) - printf("ok!\n"); + if (opts->verbose) + printf("%sok%s!\n", FZL_COK, FZL_CRST); - free(source); - free(args); + fzl_freeoptions(opts); + fzl_freelinked(linked); fzl_freereader(rd); fzl_freeast(parser->root); fzl_freeparser(parser); fzl_freewalker(walker); fzl_freewriter(writer); - printf("done.\n"); + printf("%sCompiled successfully%s.\n", FZL_COK, FZL_CRST); return 0; } diff --git a/src/compiler/utils/fzl_linked.c b/src/compiler/utils/fzl_linked.c new file mode 100644 index 0000000..f7414eb --- /dev/null +++ b/src/compiler/utils/fzl_linked.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include + +char llast_err[256]; + +fzl_linked_t *fzl_mklinked() +{ + fzl_linked_t *l = malloc(sizeof(fzl_linked_t)); + if (!l) + { + snprintf(llast_err, sizeof(llast_err), "potato PC..."); + return NULL; + } + + l->entries = NULL; + l->entries_count = 0; + l->source = NULL; + l->total_sz = 0; + + return l; +} + +bool fzl_linked_add_files(fzl_linked_t *l, char **files, size_t files_count) +{ + if (!l) + return false; + + size_t eremainder = 2; + size_t ecount = 0; + fzl_linked_entry_t *entries = malloc(eremainder * sizeof(fzl_linked_entry_t)); + + size_t current_start = 0; + for (size_t i = 0; i < files_count; i++) + { + char *source; + FILE *fd = fopen(files[i], "r"); + if (!fd) + { + snprintf(llast_err, sizeof(llast_err), "failed to open file '%s'\n", + files[i]); + free(entries); + return false; + } + + fseek(fd, 0L, SEEK_END); + size_t file_sz = ftell(fd); + rewind(fd); + + source = malloc(file_sz + 1); + fread(source, file_sz, 1, fd); + fclose(fd); + source[file_sz] = 0; + + if (eremainder <= ecount) + { + eremainder *= 2; + entries = realloc(entries, eremainder * sizeof(fzl_linked_entry_t)); + if (!entries) + { + snprintf(llast_err, sizeof(llast_err), "potato pc..."); + free(source); + return NULL; + } + } + entries[ecount++] = + (fzl_linked_entry_t){.name = files[i], + .start = current_start, + .end = current_start + file_sz - 1}; + + if (!l->source) + l->source = malloc(file_sz); + else + l->source = realloc(l->source, current_start + file_sz); + + if (!l->source) + { + snprintf(llast_err, sizeof(llast_err), + "failed to allocate space for source code holder"); + free(entries); + free(source); + return NULL; + } + + strcpy(l->source + current_start, source); + current_start += file_sz; + } + + l->source = realloc(l->source, current_start + 1); + if (!l->source) + { + snprintf(llast_err, sizeof(llast_err), + "not enough space for entire source\n"); + free(entries); + return NULL; + } + + l->source[current_start] = 0; + + l->entries = entries; + l->entries_count = ecount; + l->total_sz = current_start; + return true; +} + +char *fzl_linked_geterror() { return llast_err; } + +bool fzl_linked_getpos(fzl_linked_t *l, size_t pos, fzl_file_loc *res) +{ + if (!l) + return false; + + bool found = false; + fzl_linked_entry_t e; + for (size_t i = 0; i < l->entries_count; i++) + { + e = l->entries[i]; + if (pos >= e.start && pos <= e.end) + { + found = true; + break; + } + } + + if (!found) + return NULL; + + size_t line = 1; + size_t line_start = e.start; + for (size_t p = e.start; p < pos; p++) + { + if (l->source[p] == '\n') + { + line++; + line_start = p + 1; + } + } + + *res = + (fzl_file_loc){.filename = e.name, .line = line, .col = pos - line_start}; + return true; +} + +void fzl_freelinked(fzl_linked_t *linked) +{ + if (!linked) + return; + + if (linked->source) + free(linked->source); + + if (linked->entries) + free(linked->entries); + + free(linked); +} diff --git a/src/compiler/utils/fzl_options.c b/src/compiler/utils/fzl_options.c new file mode 100644 index 0000000..4060189 --- /dev/null +++ b/src/compiler/utils/fzl_options.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +const char version[] = "FZL Compiler 1.0"; + +void print_help() +{ + printf("Usage: fzc [flags] \n"); + printf("Available flags:\n"); + printf("\t-h\t--help\t\tPrint help\n"); + printf("\t-v\t--verbose\tDisplay compilation info (tokens, AST, consts " + "table, etc)\n"); + printf("\t-V\t--version\tPrint compiler version\n"); + printf("\t-o\t--out\t\tSet compiled file name\n"); +} + +fzl_options_t *fzl_mkoptions(int argc, char **argv) +{ + fzl_options_t *opts = malloc(sizeof(fzl_options_t)); + if (!opts) + { + printf("%sBruh... Potato PC%s\n", FZL_CERROR, FZL_CRST); + return NULL; + } + + memset(opts, 0, sizeof(fzl_options_t)); + + if (argc < 2) + { + print_help(); + opts->should_continue = false; + return opts; + } + + size_t i; + for (i = 1; i < argc; i++) + { + if (argv[i][0] != '-') + break; + + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) + { + print_help(); + opts->should_continue = false; + break; + } + else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) + { + printf("%s\n", version); + opts->should_continue = false; + break; + } + else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) + { + opts->verbose = true; + } + else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) + { + if (i == argc - 1) + { + printf("%sError%s: missing out file name\n", FZL_CERROR, FZL_CRST); + free(opts); + return NULL; + } + + char *filename = argv[++i]; + opts->outname = filename; + } + else + { + printf("%sError%s: undefined flag '%s'\n", FZL_CERROR, FZL_CRST, argv[i]); + free(opts); + return NULL; + } + } + + size_t fremainder = 2; + size_t fcount = 0; + char **files = malloc(fremainder * sizeof(char *)); + + for (; i < argc; i++) + { + if (argv[i][0] == '-') + { + printf( + "%sWarning%s: unhandled flag '%s' (should be defined before sources " + "list)\n", + FZL_CWARNING, FZL_CRST, argv[i]); + continue; + } + + if (fremainder <= fcount) + { + fremainder *= 2; + files = realloc(files, fremainder * sizeof(char *)); + } + + files[fcount++] = argv[i]; + } + + opts->files = files; + opts->files_count = fcount; + + if (i < argc - 1) + { + printf("%sError%s: flags should go before source files list.\n", FZL_CERROR, + FZL_CRST); + printf("Remain %zu arguments are not handled.\n", argc - 1 - i); + + free(files); + free(opts); + return NULL; + } + + opts->should_continue = true; + return opts; +} + +void fzl_freeoptions(fzl_options_t *options) +{ + if (!options) + return; + + if (options->files) + free(options->files); + + free(options); +} diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 662dd2f..899c82b 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -11,8 +11,6 @@ #include #include -// NOTE: need to add optimized arithmetics in near future - char last_err[256]; static char *expect_hdr = "FZL\0\1"; -- 2.54.0 From 683dd08181b3a00376e59b73134efe7b0ac97e59 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 15 Oct 2026 21:41:45 +0300 Subject: [PATCH 31/34] wip: working on classes --- CMakeLists.txt | 8 + exl/class.fzs | 32 ++ include/compiler/fzl_parser.h | 22 +- include/compiler/fzl_token.h | 6 +- src/compiler/fzl_parser.c | 726 +++++++++++++++++++++------------- src/compiler/fzl_reader.c | 14 +- 6 files changed, 532 insertions(+), 276 deletions(-) create mode 100644 exl/class.fzs diff --git a/CMakeLists.txt b/CMakeLists.txt index c6692f4..9bee88a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,10 @@ add_test( NAME compile_comment COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/comment.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/comment.fzs" ) +add_test( + NAME compile_class + COMMAND fzc -o "${CMAKE_CURRENT_BINARY_DIR}/class.fzb" "${CMAKE_CURRENT_SOURCE_DIR}/exl/class.fzs" +) set_tests_properties(compile_problematic_hw PROPERTIES WILL_FAIL TRUE) @@ -100,4 +104,8 @@ add_test( NAME runtime_comment COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/comment.fzb" ) +add_test( + NAME runtime_class + COMMAND fz "${CMAKE_CURRENT_BINARY_DIR}/class.fzb" +) diff --git a/exl/class.fzs b/exl/class.fzs new file mode 100644 index 0000000..84c7787 --- /dev/null +++ b/exl/class.fzs @@ -0,0 +1,32 @@ +class Point +{ + int x; + int y; + + // Move point by [dx,dy] + fn move(int dx, int dy) { + this.x = this.x + dx; + this.y = this.y + dy; + } +} + +// NOTE: WHY THE FUCK THIS FUCKING COMPILER REQUIRES ';' AFTER '='?! +// | +// v +Point obj = new Point; +obj.x = 1; +obj.y = 2; + +printline("Current X,Y:"); +printline(obj.x); +printline(obj.y); + +obj.move(9, 8); +printline("X,Y after move:"); +printline(obj.x); +printline(obj.y); + +if (obj.x == 10 && obj.y == 10) + printline("Test passed"); +else printline("Test failed"); + diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index 363d8c0..3be82e5 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -1,7 +1,6 @@ #ifndef FZL_PARSER_H #define FZL_PARSER_H -#include "common/fzl_types.h" #include #include #include @@ -16,6 +15,8 @@ typedef enum FZL_NODETYPE_CONDITIONAL, FZL_NODETYPE_FUNCTION, FZL_NODETYPE_RETURN, + FZL_NODETYPE_CLASS, + FZL_NODETYPE_FIELD, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, @@ -113,6 +114,25 @@ typedef struct fzl_node { struct fzl_node *value; } ret; + + // Class declaration + struct + { + fzl_token_t *name; + + struct fzl_node **fields; + size_t fields_count; + + struct fzl_node **methods; + size_t methods_count; + } class; + + // Member access + struct + { + struct fzl_node *parent; + struct fzl_node *field; + } memb; }; } fzl_node_t; diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index bb9a5de..30b5f81 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -8,7 +8,11 @@ // Token type typedef enum { - FZL_TOKTYPE_RETURN, // return + FZL_TOKTYPE_CLASS, // class + FZL_TOKTYPE_NEW, // new + + FZL_TOKTYPE_FUNCTION, // fn + FZL_TOKTYPE_RETURN, // return FZL_TOKTYPE_IF, // if FZL_TOKTYPE_ELSE, // else diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 0bb7dae..ccf99f9 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -170,7 +170,14 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) else if (is(parser, FZL_TOKTYPE_NAME)) { fzl_token_t *name = consume(parser); - return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); + if (!is(parser, FZL_TOKTYPE_NAME)) + return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); + + fzl_token_t *realname = consume(parser); // :D + fzl_node_t *decl = fzl_mknode(FZL_NODETYPE_DECLARATION, name); + decl->var.type = name; + decl->var.identifier = fzl_mknode(FZL_NODETYPE_IDENTIFIER, realname); + return decl; } else if (is(parser, FZL_TOKTYPE_TYPE)) { @@ -215,52 +222,72 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) return NULL; } - while (is(parser, FZL_TOKTYPE_LPAREN)) + if (is(parser, FZL_TOKTYPE_LPAREN)) { - consume(parser); - - size_t args_capacity = 2; - size_t args_count = 0; - fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); - - while (!is(parser, FZL_TOKTYPE_RPAREN)) + while (is(parser, FZL_TOKTYPE_LPAREN)) { - fzl_node_t *arg = parse_expression(parser); - if (!arg) + consume(parser); + + size_t args_capacity = 2; + size_t args_count = 0; + fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); + + while (!is(parser, FZL_TOKTYPE_RPAREN)) + { + fzl_node_t *arg = parse_expression(parser); + if (!arg) + { + free(args); + return NULL; + } + + if (args_capacity <= args_count) + { + args_capacity *= 2; + args = realloc(args, args_capacity * sizeof(fzl_node_t)); + } + + args[args_count] = arg; + args_count++; + + if (is(parser, FZL_TOKTYPE_COMMA)) + consume(parser); + } + + fzl_token_t *rparen = consume(parser); + if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN) { free(args); + snprintf(plast_err, sizeof(plast_err), + "'%s': expected ')' after argument list at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } - if (args_capacity <= args_count) - { - args_capacity *= 2; - args = realloc(args, args_capacity * sizeof(fzl_node_t)); - } + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); + node->call.callee = left; + node->call.args = args; + node->call.args_count = args_count; - args[args_count] = arg; - args_count++; - - if (is(parser, FZL_TOKTYPE_COMMA)) - consume(parser); + left = node; } - - fzl_token_t *rparen = consume(parser); - if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN) + } + else if (is(parser, FZL_TOKTYPE_DOT)) + { + while (is(parser, FZL_TOKTYPE_DOT)) { - free(args); - snprintf(plast_err, sizeof(plast_err), - "'%s': expected ')' after argument list at %ld:%ld", - peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); - return NULL; + consume(parser); + + fzl_node_t *right = parse_primary(parser); + if (!right) + return NULL; + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FIELD, right->token); + node->memb.parent = left; + node->memb.field = right; + + left = node; } - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); - node->call.callee = left; - node->call.args = args; - node->call.args_count = args_count; - - left = node; } return left; @@ -540,257 +567,395 @@ fzl_node_t *parse_or(fzl_parser_t *parser) return left; } +// Assignments parsing +fzl_node_t *parse_assignment(fzl_parser_t *parser) +{ + fzl_node_t *left = parse_or(parser); + if (!left) + return NULL; + + if (is(parser, FZL_TOKTYPE_EQ)) + { + fzl_token_t *asn = consume(parser); + if (left->type != FZL_NODETYPE_IDENTIFIER && + left->type != FZL_NODETYPE_DECLARATION && + left->type != FZL_NODETYPE_FIELD) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': cannot assign to '%s' at %ld:%ld", left->token->filename, + (char *)left->token->value, asn->ln, asn->cn); + return NULL; + } + + fzl_node_t *right = parse_assignment(parser); + if (!right) + return NULL; + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_ASSIGN, asn); + node->binary_op.left = left; + node->binary_op.right = right; + + left = node; + } + + return left; +} + // Expression parsing -fzl_node_t *parse_expression(fzl_parser_t *parser) { return parse_and(parser); } +fzl_node_t *parse_expression(fzl_parser_t *parser) +{ + return parse_assignment(parser); +} + +fzl_node_t *parse_statement(fzl_parser_t *parser); + +// Parse class definition +fzl_node_t *parse_class(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_token_t *class = consume(parser); + fzl_token_t *name = consume(parser); + if (name->type != FZL_TOKTYPE_NAME) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected class name at %ld:%ld", name->filename, name->ln, + name->cn); + return NULL; + } + + fzl_token_t *lbrace = consume(parser); + if (lbrace->type != FZL_TOKTYPE_LBRACE) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected '{' after class name at %ld:%ld", lbrace->filename, + lbrace->ln, lbrace->cn); + return NULL; + } + + size_t fremainder = 2; + size_t fcount = 0; + fzl_node_t **fields = malloc(fremainder * sizeof(fzl_node_t *)); + if (!fields) + { + snprintf(plast_err, sizeof(plast_err), + "failed to allocate space for class fields"); + return NULL; + } + + size_t mremainder = 2; + size_t mcount = 0; + fzl_node_t **methods = malloc(mremainder * sizeof(fzl_node_t *)); + if (!methods) + { + snprintf(plast_err, sizeof(plast_err), + "failed to allocate space for class fields"); + free(fields); + return NULL; + } + + while (!is(parser, FZL_TOKTYPE_RBRACE)) + { + fzl_node_t *node = parse_statement(parser); + if (!node) + return NULL; + + if (node->type == FZL_NODETYPE_DECLARATION) + { + if (fremainder <= fcount) + { + fremainder *= 2; + fields = realloc(fields, fremainder * sizeof(fzl_node_t *)); + if (!fields) + { + snprintf(plast_err, sizeof(plast_err), + "failed to resize fields array"); + free(methods); + return NULL; + } + } + + fields[fcount++] = node; + } + else if (node->type == FZL_NODETYPE_FUNCTION) + { + if (mremainder <= mcount) + { + mremainder *= 2; + methods = realloc(methods, mremainder * sizeof(fzl_node_t *)); + if (!methods) + { + snprintf(plast_err, sizeof(plast_err), + "failed to resize methods array"); + free(fields); + return NULL; + } + } + + fzl_node_t **new_args = realloc(node->fn.args, ++node->fn.args_count); + memcpy(&new_args[1], new_args, + (node->fn.args_count - 1) * sizeof(fzl_node_t *)); + + fzl_token_t *this_tok = + fzl_mktoken(FZL_TOKTYPE_NAME, node->fn.name->ln, node->fn.name->cn, + "this", 4, node->fn.name->filename); + fzl_node_t *this_id = fzl_mknode(FZL_NODETYPE_IDENTIFIER, this_tok); + fzl_node_t *this_dec = fzl_mknode(FZL_NODETYPE_DECLARATION, name); + this_dec->var.type = name; + this_dec->var.identifier = this_id; + + new_args[0] = this_dec; + methods[mcount++] = node; + } + else + { + snprintf(plast_err, sizeof(plast_err), + "'%s': unknown type of class member at %ld:%ld", + node->token->filename, node->token->ln, node->token->cn); + free(fields); + free(methods); + return NULL; + } + } + fzl_token_t *rbra = consume(parser); + if (rbra->type != FZL_TOKTYPE_RBRACE) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected '}' after class definition at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + free(fields); + free(methods); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CLASS, class); + node->class.name = name; + node->class.fields = fields; + node->class.fields_count = fcount; + node->class.methods = methods; + node->class.methods_count = mcount; + + return node; +} + +// Functions parsing +fzl_node_t *parse_function(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_token_t *fn = consume(parser); + fzl_token_t *name = consume(parser); + if (!name || name->type != FZL_TOKTYPE_NAME) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected function name at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + + fzl_token_t *lpar = consume(parser); + if (!lpar || lpar->type != FZL_TOKTYPE_LPAREN) + { + snprintf(plast_err, sizeof(plast_err), "'%s': expected '(' at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + + size_t remainder = 1; + size_t count = 0; + fzl_node_t **args = malloc(remainder * sizeof(fzl_node_t *)); + + while (peek(parser) && !is(parser, FZL_TOKTYPE_RPAREN)) + { + fzl_node_t *arg = parse_expression(parser); + if (!arg) + return NULL; + + if (remainder <= count) + { + remainder *= 2; + args = realloc(args, remainder * sizeof(fzl_node_t *)); + } + + args[count++] = arg; + + if (is(parser, FZL_TOKTYPE_COMMA)) + consume(parser); + } + + fzl_token_t *rpar = consume(parser); + if (!rpar || rpar->type != FZL_TOKTYPE_RPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected ')' after argument list at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + + fzl_token_t *type; + if (is(parser, FZL_TOKTYPE_TYPE) || is(parser, FZL_TOKTYPE_NAME)) + type = consume(parser); + + if (!type) + type = fzl_mktoken(FZL_TOKTYPE_TYPE, last_known_tok->ln, last_known_tok->cn, + "null", 4, last_known_tok->filename); + + if (!is(parser, FZL_TOKTYPE_LBRACE)) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected '{' after function declaration at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + + fzl_node_t *body = parse_statement(parser); + if (!body) + return NULL; + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FUNCTION, name); + node->fn.args = args; + node->fn.args_count = count; + node->fn.body = body; + node->fn.name = name; + node->fn.type = type; + + return node; +} + +// Return parsing +fzl_node_t *parse_return(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_token_t *ret = consume(parser); + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_RETURN, ret); + + if (!is(parser, FZL_TOKTYPE_SEMICOLON)) + { + fzl_node_t *retval = parse_expression(parser); + if (!retval) + return NULL; + + node->ret.value = retval; + } + + return node; +} + +fzl_node_t *parse_if(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_token_t *if_tok = consume(parser); + fzl_token_t *lparen = consume(parser); + if (lparen->type != FZL_TOKTYPE_LPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected '(' before condition at %ld:%ld", lparen->filename, + lparen->ln, lparen->cn); + return NULL; + } + + fzl_node_t *condition = parse_expression(parser); + if (!condition) + return NULL; + + fzl_token_t *rparen = consume(parser); + if (rparen->type != FZL_TOKTYPE_RPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expected ')' after condition at %ld:%ld", rparen->filename, + rparen->ln, rparen->cn); + return NULL; + } + + fzl_node_t *else_block = NULL; + fzl_node_t *true_block = parse_statement(parser); + if (!true_block) + return NULL; + + if (is(parser, FZL_TOKTYPE_ELSE)) + { + consume(parser); // else + else_block = parse_statement(parser); + if (!else_block) + return NULL; + } + + fzl_node_t *stmt_node = fzl_mknode(FZL_NODETYPE_CONDITIONAL, if_tok); + stmt_node->cond.condition = condition; + stmt_node->cond.true_block = true_block; + stmt_node->cond.else_block = else_block; + + return stmt_node; +} + +// Block statement parsing +fzl_node_t *parse_block(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_token_t *block_tok = consume(parser); + + size_t remainder = 2; + size_t count = 0; + fzl_node_t **stmts = malloc(remainder * sizeof(fzl_node_t *)); + + while (peek(parser) && !is(parser, FZL_TOKTYPE_RBRACE)) + { + fzl_node_t *node = parse_statement(parser); + if (!node) + return NULL; + + if (remainder <= count) + { + remainder *= 2; + stmts = realloc(stmts, remainder * sizeof(fzl_node_t *)); + } + + stmts[count++] = node; + } + fzl_token_t *rbrace = consume(parser); + if (rbrace && rbrace->type != FZL_TOKTYPE_RBRACE) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': missing '}' after block at %ld:%ld", rbrace->filename, + rbrace->ln, rbrace->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_BLOCK, block_tok); + node->list.entries = stmts; + node->list.entry_count = count; + return node; +} // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { fzl_node_t *res = NULL; - if (is(parser, FZL_TOKTYPE_TYPE)) - { - fzl_token_t *type = peek(parser); - if (type->type == FZL_TOKTYPE_TYPE) - consume(parser); - - if (type->type == FZL_TOKTYPE_TYPE && - next(parser)->type == FZL_TOKTYPE_LPAREN) - { - fzl_token_t *tok = consume(parser); - if (tok->type != FZL_TOKTYPE_NAME) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected name in function definition at %ld:%ld", - tok->filename, tok->ln, tok->cn); - return NULL; - } - - fzl_token_t *lparen = consume(parser); - if (lparen->type != FZL_TOKTYPE_LPAREN) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected '(' after name at %ld:%ld", lparen->filename, - lparen->ln, lparen->cn); - return NULL; - } - - size_t remainder = 1; - size_t count = 0; - fzl_node_t **args = malloc(remainder * sizeof(fzl_node_t *)); - - while (peek(parser) && !is(parser, FZL_TOKTYPE_RPAREN)) - { - fzl_node_t *arg = parse_expression(parser); - if (!arg) - return NULL; - - if (remainder <= count) - { - remainder *= 2; - args = realloc(args, remainder * sizeof(fzl_node_t *)); - } - - args[count++] = arg; - - if (is(parser, FZL_TOKTYPE_COMMA)) - consume(parser); - } - - fzl_token_t *rparen = consume(parser); - if (rparen->type != FZL_TOKTYPE_RPAREN) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected ')' after argument list at %ld:%ld", - rparen->filename, rparen->ln, rparen->cn); - return NULL; - } - - if (!is(parser, FZL_TOKTYPE_LBRACE)) - { - fzl_token_t *tok = peek(parser); - snprintf(plast_err, sizeof(plast_err), "'%s': expected '{' at %ld:%ld", - tok->filename, tok->ln, tok->cn); - return NULL; - } - - fzl_node_t *body = parse_statement(parser); - if (!body) - return NULL; - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FUNCTION, tok); - node->fn.args = args; - node->fn.args_count = count; - node->fn.body = body; - node->fn.name = tok; - node->fn.type = type; - - return node; - } - else if (type->type == FZL_TOKTYPE_TYPE || - next(parser)->type == FZL_TOKTYPE_EQ) - { - - fzl_node_t *identifier = parse_primary(parser); - if (!identifier || identifier->type != FZL_NODETYPE_IDENTIFIER) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected identifier at %ld:%ld", type->filename, - type->ln, type->cn); - return NULL; - } - - fzl_node_t *left = fzl_mknode(FZL_NODETYPE_DECLARATION, type); - left->var.type = type; - left->var.identifier = identifier; - - if (is(parser, FZL_TOKTYPE_EQ)) - { - fzl_node_t *right; - while (is(parser, FZL_TOKTYPE_EQ)) - { - fzl_token_t *asn = consume(parser); - right = parse_expression(parser); - if (!right) - return NULL; - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_ASSIGN, asn); - node->binary_op.left = left; - node->binary_op.right = right; - - left = node; - } - } - - res = left; - } - } - else if (is(parser, FZL_TOKTYPE_NAME) && next(parser)->type == FZL_TOKTYPE_EQ) - { - fzl_token_t *name = consume(parser); - fzl_node_t *left = fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); - - while (is(parser, FZL_TOKTYPE_EQ)) - { - fzl_token_t *eq = consume(parser); - - fzl_node_t *right = parse_expression(parser); - if (!right) - return NULL; - - fzl_node_t *assn = fzl_mknode(FZL_NODETYPE_ASSIGN, eq); - assn->binary_op.left = left; - assn->binary_op.right = right; - - left = assn; - } - - res = left; - } + if (is(parser, FZL_TOKTYPE_CLASS)) + return parse_class(parser); + else if (is(parser, FZL_TOKTYPE_FUNCTION)) + return parse_function(parser); else if (is(parser, FZL_TOKTYPE_RETURN)) - { - fzl_token_t *ret = consume(parser); - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_RETURN, ret); - - if (!is(parser, FZL_TOKTYPE_SEMICOLON)) - { - fzl_node_t *retval = parse_expression(parser); - if (!retval) - return NULL; - - node->ret.value = retval; - } - - res = node; - } + res = parse_return(parser); else if (is(parser, FZL_TOKTYPE_IF)) - { - fzl_token_t *if_tok = consume(parser); - fzl_token_t *lparen = consume(parser); - if (lparen->type != FZL_TOKTYPE_LPAREN) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected '(' before condition at %ld:%ld", - lparen->filename, lparen->ln, lparen->cn); - return NULL; - } - - fzl_node_t *condition = parse_expression(parser); - if (!condition) - return NULL; - - fzl_token_t *rparen = consume(parser); - if (rparen->type != FZL_TOKTYPE_RPAREN) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': expected ')' after condition at %ld:%ld", - rparen->filename, rparen->ln, rparen->cn); - return NULL; - } - - fzl_node_t *else_block = NULL; - fzl_node_t *true_block = parse_statement(parser); - if (!true_block) - return NULL; - - if (is(parser, FZL_TOKTYPE_ELSE)) - { - consume(parser); // else - else_block = parse_statement(parser); - if (!else_block) - return NULL; - } - - fzl_node_t *stmt_node = fzl_mknode(FZL_NODETYPE_CONDITIONAL, if_tok); - stmt_node->cond.condition = condition; - stmt_node->cond.true_block = true_block; - stmt_node->cond.else_block = else_block; - - return stmt_node; - } + return parse_if(parser); else if (is(parser, FZL_TOKTYPE_LBRACE)) - { - fzl_token_t *block_tok = consume(parser); - - size_t remainder = 2; - size_t count = 0; - fzl_node_t **stmts = malloc(remainder * sizeof(fzl_node_t *)); - - while (peek(parser) && !is(parser, FZL_TOKTYPE_RBRACE)) - { - fzl_node_t *node = parse_statement(parser); - if (!node) - return NULL; - - if (remainder <= count) - { - remainder *= 2; - stmts = realloc(stmts, remainder * sizeof(fzl_node_t *)); - } - - stmts[count++] = node; - } - fzl_token_t *rbrace = consume(parser); - if (rbrace && rbrace->type != FZL_TOKTYPE_RBRACE) - { - snprintf(plast_err, sizeof(plast_err), - "'%s': missing '}' after block at %ld:%ld", rbrace->filename, - rbrace->ln, rbrace->cn); - return NULL; - } - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_BLOCK, block_tok); - node->list.entries = stmts; - node->list.entry_count = count; - return node; - } + return parse_block(parser); if (!res) res = parse_expression(parser); if (!is(parser, FZL_TOKTYPE_SEMICOLON)) { - printf("Current token are %d\n", last_known_tok->type); snprintf(plast_err, sizeof(plast_err), "'%s': missing ';' at %ld:%ld", last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); return NULL; @@ -908,9 +1073,26 @@ void fzl_freeast(fzl_node_t *root) free(root->fn.args); fzl_freenode(root->fn.body); } + else if (root->type == FZL_NODETYPE_RETURN) + { + if (root->ret.value) + fzl_freenode(root->ret.value); + } + else if (root->type == FZL_NODETYPE_CLASS) + { + if (root->class.fields_count) + for (size_t i = 0; i < root->class.fields_count; i++) + fzl_freenode(root->class.fields[i]); + free(root->class.fields); + + if (root->class.methods_count) + for (size_t i = 0; i < root->class.methods_count; i++) + fzl_freenode(root->class.methods[i]); + free(root->class.methods); + } - fzl_freenode(root); root = root->next; + free(root); } } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index 15752a1..bc4e3e5 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -70,7 +70,15 @@ fzl_token_t *parse_name(fzl_reader_t *reader) return NULL; } - if (strcmp(value, "return") == 0) + if (strcmp(value, "class") == 0) + tok = + fzl_mktoken(FZL_TOKTYPE_CLASS, l.line, l.col, value, size, l.filename); + else if (strcmp(value, "new") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_NEW, l.line, l.col, value, size, l.filename); + else if (strcmp(value, "fn") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_FUNCTION, l.line, l.col, value, size, + l.filename); + else if (strcmp(value, "return") == 0) tok = fzl_mktoken(FZL_TOKTYPE_RETURN, l.line, l.col, value, size, l.filename); else if (strcmp(value, "if") == 0) @@ -86,7 +94,7 @@ fzl_token_t *parse_name(fzl_reader_t *reader) strcmp(value, "i16") == 0 || strcmp(value, "i32") == 0 || strcmp(value, "i64") == 0 || strcmp(value, "u8") == 0 || strcmp(value, "u16") == 0 || strcmp(value, "u32") == 0 || - strcmp(value, "u64") == 0) + strcmp(value, "u64") == 0 || strcmp(value, "null") == 0) tok = fzl_mktoken(FZL_TOKTYPE_TYPE, l.line, l.col, value, size, l.filename); else tok = fzl_mktoken(FZL_TOKTYPE_NAME, l.line, l.col, value, size, l.filename); @@ -286,6 +294,8 @@ int fzl_read(fzl_reader_t *reader) token = add_single(reader, FZL_TOKTYPE_RPAREN); else if (curr == ',') token = add_single(reader, FZL_TOKTYPE_COMMA); + else if (curr == '.') + token = add_single(reader, FZL_TOKTYPE_DOT); else if (curr == '*') token = add_single(reader, FZL_TOKTYPE_STAR); else if (curr == '/') -- 2.54.0 From bc5d887ec4925b729d110ab7c85d73266583b338 Mon Sep 17 00:00:00 2001 From: samsonium Date: Fri, 16 Oct 2026 18:11:28 +0300 Subject: [PATCH 32/34] feat: implement objects and classes --- .gdbinit | 13 + CMakeLists.txt | 1 + README.md | 2 + README.ru.md | 4 +- exl/class.fzs | 6 +- exl/fn.fzs | 4 +- include/common/fzl_opcodes.h | 4 + include/common/fzl_types.h | 1 + include/common/fzl_value.h | 2 + include/compiler/fzl_parser.h | 7 + include/compiler/fzl_walker.h | 20 +- include/compiler/utils/fzl_ctable.h | 30 ++ include/compiler/utils/fzl_symtable.h | 9 +- include/vm/fzl_vm.h | 4 + include/vm/fzl_vm_object.h | 14 + src/compiler/fzl_debug.c | 23 ++ src/compiler/fzl_parser.c | 143 +++++--- src/compiler/fzl_walker.c | 505 +++++++++++++++++++++++--- src/compiler/utils/fzl_ctable.c | 29 ++ src/compiler/utils/fzl_symtable.c | 24 +- src/vm/fzl_vm.c | 99 ++++- src/vm/fzl_vm_debug.c | 20 +- 22 files changed, 848 insertions(+), 116 deletions(-) create mode 100644 include/compiler/utils/fzl_ctable.h create mode 100644 include/vm/fzl_vm_object.h create mode 100644 src/compiler/utils/fzl_ctable.c diff --git a/.gdbinit b/.gdbinit index b48dd6a..56124a5 100644 --- a/.gdbinit +++ b/.gdbinit @@ -2,4 +2,17 @@ skip peek skip consume skip next skip is +skip fzl_mknode +skip fzl_mktoken +skip fzl_symt_put +skip fzl_symt_get +skip add_const +skip push_op +skip push_word +skip fzl_mkenv +skip fzl_freeenv +skip fzl_env_lookup +skip fzl_env_contains +skip fzl_env_storevalue +skip fzl_env_writevalue diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bee88a..925fd71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(fzc src/compiler/utils/fzl_symtable.c src/compiler/utils/fzl_linked.c src/compiler/utils/fzl_options.c + src/compiler/utils/fzl_ctable.c src/compiler/main.c) # Virtual machine diff --git a/README.md b/README.md index e965f7c..da6c7ab 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ At the moment, the language has the following capabilities: - `str` - strings - `bool` - boolean - Assignments +- Functions +- Classes and objects Working examples of code can be found in `exl/` directory. diff --git a/README.ru.md b/README.ru.md index 28d32c5..0280ea0 100644 --- a/README.ru.md +++ b/README.ru.md @@ -9,7 +9,7 @@ На данный момент язык имеет: - Встроенную функцию `printline` для вывода аргумента в stdout -- Строковый и числовой литералы +- Строковый, булев и числовой литералы - Арифметические операции: сложение, вычитание, умножение, деление, модуло, возведение в степень - Декларация переменных со следующими типами: - `uint`, `int` - целые числа размером с машинное слово @@ -18,6 +18,8 @@ - `str` - строки - `bool` - булевы - Присваивание +- Функции +- Классы и объекты Примеры кода лежат в директории `exl/`. diff --git a/exl/class.fzs b/exl/class.fzs index 84c7787..f73a12d 100644 --- a/exl/class.fzs +++ b/exl/class.fzs @@ -10,9 +10,6 @@ class Point } } -// NOTE: WHY THE FUCK THIS FUCKING COMPILER REQUIRES ';' AFTER '='?! -// | -// v Point obj = new Point; obj.x = 1; obj.y = 2; @@ -21,6 +18,9 @@ printline("Current X,Y:"); printline(obj.x); printline(obj.y); +// NOTE: WHY THE FUCK THIS FUCKING COMPILER REQUIRES ';' AFTER '='?! +// | +// v obj.move(9, 8); printline("X,Y after move:"); printline(obj.x); diff --git a/exl/fn.fzs b/exl/fn.fzs index e919ada..dde5dad 100644 --- a/exl/fn.fzs +++ b/exl/fn.fzs @@ -1,4 +1,4 @@ -int fib(int n) +fn fib(int n) int { if (n <= 1) return n; @@ -6,7 +6,7 @@ int fib(int n) return fib(n - 1) + fib(n - 2); } -uint sum(uint a, uint b) +fn sum(uint a, uint b) uint { return a + b; } diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 6938b80..a0d8ac3 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -21,6 +21,10 @@ typedef enum FZL_OPCODE_CALL = 0xF30001, // Call function FZL_OPCODE_RET = 0xF30002, // Return from function + FZL_OPCODE_ALLOC_OBJECT = 0xFA0001, // Allocate N slots + FZL_OPCODE_GET_FIELD = 0xFA0002, // Get field N of an object at dstack + FZL_OPCODE_SET_FIELD = 0xFA0003, // Set field N of an object at dstack + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in dstack FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in dstack FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in dstack diff --git a/include/common/fzl_types.h b/include/common/fzl_types.h index 6e85456..346cb96 100644 --- a/include/common/fzl_types.h +++ b/include/common/fzl_types.h @@ -4,6 +4,7 @@ typedef enum { FZL_TYPE_NULL, + FZL_TYPE_OBJECT, FZL_TYPE_STRING, FZL_TYPE_CHAR, diff --git a/include/common/fzl_value.h b/include/common/fzl_value.h index df8d68c..c6e2db4 100644 --- a/include/common/fzl_value.h +++ b/include/common/fzl_value.h @@ -13,6 +13,8 @@ typedef struct union { + void *obj; + struct { const char *value; diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index 3be82e5..f96c307 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -17,6 +17,7 @@ typedef enum FZL_NODETYPE_RETURN, FZL_NODETYPE_CLASS, FZL_NODETYPE_FIELD, + FZL_NODETYPE_CONSTRUCT, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, @@ -133,6 +134,12 @@ typedef struct fzl_node struct fzl_node *parent; struct fzl_node *field; } memb; + + // Object construct + struct + { + struct fzl_node *name; + } construct; }; } fzl_node_t; diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index 7b5ee8a..a984e68 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -5,10 +5,20 @@ #include #include #include +#include #include #include #include +// Type of context +typedef enum +{ + FZL_WALK_CTX_TYPE_GLOBAL, + FZL_WALK_CTX_TYPE_FN, + FZL_WALK_CTX_TYPE_CLASS, + FZL_WALK_CTX_TYPE_ASSIGN +} fzl_walk_ctx_type_t; + // Internal walk function result typedef struct { @@ -16,11 +26,16 @@ typedef struct char *error_msg; bool error; + + char *classname; } fzl_walk_res_t; // Function context descriptor -typedef struct +typedef struct fzl_walk_ctx { + fzl_walk_ctx_type_t type; + struct fzl_walk_ctx *parent; + fzl_node_t *fn; size_t initial_env_lvl; } fzl_walk_ctx_t; @@ -39,6 +54,9 @@ typedef struct size_t sym_top; fzl_walk_ctx_t *ctx; + + fzl_ctable_t *classes[64]; + size_t classes_count; } fzl_walker_t; // Allocate new walker diff --git a/include/compiler/utils/fzl_ctable.h b/include/compiler/utils/fzl_ctable.h new file mode 100644 index 0000000..edfa495 --- /dev/null +++ b/include/compiler/utils/fzl_ctable.h @@ -0,0 +1,30 @@ +#ifndef FZL_CTABLE_H +#define FZL_CTABLE_H + +#include +#include + +// Class field descriptor +typedef struct +{ + char *name; + size_t slot_index; + fzl_type_t type; +} fzl_ctable_field_t; + +// Class table descriptor +typedef struct +{ + char *name; + fzl_ctable_field_t *fields; + size_t fields_count; + size_t fields_remainder; +} fzl_ctable_t; + +// Allocate class table +fzl_ctable_t *fzl_mkctable(char *name); + +// Free class table +void fzl_freectable(fzl_ctable_t *ct); + +#endif // FZL_CTABLE_H diff --git a/include/compiler/utils/fzl_symtable.h b/include/compiler/utils/fzl_symtable.h index 1a591a9..ebaacdc 100644 --- a/include/compiler/utils/fzl_symtable.h +++ b/include/compiler/utils/fzl_symtable.h @@ -10,7 +10,8 @@ typedef enum { FZL_SYMTABLE_RESULT_OK, FZL_SYMTABLE_RESULT_ERR, - FZL_SYMTABLE_RESULT_EXISTS + FZL_SYMTABLE_RESULT_EXISTS, + FZL_SYMTABLE_RESULT_NOT_OBJECT } fzl_symtable_res_t; // Symbols table entry descriptor @@ -19,6 +20,8 @@ typedef struct char *name; fzl_type_t type; size_t ip; + + char *classname; } fzl_sym_t; // Symbols table descriptor @@ -37,6 +40,10 @@ fzl_symtable_t *fzl_mksymt(); fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, fzl_type_t type, uint64_t ip); +// Assign class name to object type +fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name, + char *classname); + // Get entry from specified symtable bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res); diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index e772fdd..9d0075d 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -6,6 +6,7 @@ #include #include #include +#include // VM descriptor typedef struct @@ -29,6 +30,9 @@ typedef struct // Environment stack fzl_vm_env_t *estack[256]; size_t etop; + + fzl_vm_object_t *objects[256]; + size_t objects_count; } fzl_vm_t; // Allocate FZL VM diff --git a/include/vm/fzl_vm_object.h b/include/vm/fzl_vm_object.h new file mode 100644 index 0000000..67cbff2 --- /dev/null +++ b/include/vm/fzl_vm_object.h @@ -0,0 +1,14 @@ +#ifndef FZL_VM_OBJECT_H +#define FZL_VM_OBJECT_H + +#include +#include + +// Object descriptor +typedef struct +{ + fzl_value_t *values; + size_t values_count; +} fzl_vm_object_t; + +#endif // FZL_VM_OBJECT_H diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index c11f9c2..2a78659 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -32,6 +32,27 @@ void print_node(fzl_node_t *node, int level) switch (node->type) { + case FZL_NODETYPE_CONSTRUCT: + printf("%s Construct class '%s':\n", levelbuf, + (char *)node->construct.name->token->value); + break; + + case FZL_NODETYPE_CLASS: + printf("%s Class '%s':\n", levelbuf, (char *)node->token->value); + printf("-%s Fields:\n", levelbuf); + for (size_t i = 0; i < node->class.fields_count; i++) + print_node(node->class.fields[i], level + 2); + printf("-%s Methods:\n", levelbuf); + for (size_t i = 0; i < node->class.methods_count; i++) + print_node(node->class.methods[i], level + 2); + break; + + case FZL_NODETYPE_FIELD: + printf("%s Field '%s' access:\n", levelbuf, + (char *)node->memb.field->token->value); + print_node(node->memb.parent, level + 1); + break; + case FZL_NODETYPE_DECLARATION: printf("%s Declare variable with type %d:\n", levelbuf, node->var.type->type); @@ -112,6 +133,8 @@ void print_node(fzl_node_t *node, int level) print_node(node->ret.value, level + 1); break; + case FZL_NODETYPE_AND: + case FZL_NODETYPE_OR: case FZL_NODETYPE_EQUAL: case FZL_NODETYPE_NEQUAL: case FZL_NODETYPE_LESS: diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index ccf99f9..8c6f8ba 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -72,8 +72,9 @@ static fzl_token_t *next(fzl_parser_t *parser) // Consume token at cursor and move to the next static fzl_token_t *consume(fzl_parser_t *parser) { - if (!parser || !parser->cursor) + if (!parser || !parser->cursor || !parser->cursor->token) return NULL; + fzl_token_t *tok = parser->cursor->token; parser->cursor = parser->cursor->next; return tok; @@ -222,71 +223,75 @@ fzl_node_t *parse_postfix(fzl_parser_t *parser) return NULL; } - if (is(parser, FZL_TOKTYPE_LPAREN)) + if (is(parser, FZL_TOKTYPE_LPAREN) || is(parser, FZL_TOKTYPE_DOT)) { - while (is(parser, FZL_TOKTYPE_LPAREN)) + while (is(parser, FZL_TOKTYPE_LPAREN) || is(parser, FZL_TOKTYPE_DOT)) { - consume(parser); + fzl_token_t *post = consume(parser); - size_t args_capacity = 2; - size_t args_count = 0; - fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); - - while (!is(parser, FZL_TOKTYPE_RPAREN)) + if (post->type == FZL_TOKTYPE_LPAREN) { - fzl_node_t *arg = parse_expression(parser); - if (!arg) + size_t args_capacity = 2; + size_t args_count = 0; + fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *)); + + while (!is(parser, FZL_TOKTYPE_RPAREN)) + { + fzl_node_t *arg = parse_expression(parser); + if (!arg) + { + free(args); + return NULL; + } + + if (args_capacity <= args_count) + { + args_capacity *= 2; + args = realloc(args, args_capacity * sizeof(fzl_node_t)); + } + + args[args_count] = arg; + args_count++; + + if (is(parser, FZL_TOKTYPE_COMMA)) + consume(parser); + } + + fzl_token_t *rparen = consume(parser); + if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN) { free(args); + snprintf(plast_err, sizeof(plast_err), + "'%s': expected ')' after argument list at %ld:%ld", + peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); return NULL; } - if (args_capacity <= args_count) + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); + node->call.callee = left; + node->call.args = args; + node->call.args_count = args_count; + + left = node; + } + else if (post->type == FZL_TOKTYPE_DOT) + { + if (!is(parser, FZL_TOKTYPE_NAME)) { - args_capacity *= 2; - args = realloc(args, args_capacity * sizeof(fzl_node_t)); + snprintf(plast_err, sizeof(plast_err), + "'%s': expected name after '.' at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, + last_known_tok->cn); + return NULL; } - args[args_count] = arg; - args_count++; + fzl_token_t *name = consume(parser); + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FIELD, name); + node->memb.parent = left; + node->memb.field = fzl_mknode(FZL_NODETYPE_IDENTIFIER, name); - if (is(parser, FZL_TOKTYPE_COMMA)) - consume(parser); + left = node; } - - fzl_token_t *rparen = consume(parser); - if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN) - { - free(args); - snprintf(plast_err, sizeof(plast_err), - "'%s': expected ')' after argument list at %ld:%ld", - peek(parser)->filename, peek(parser)->ln, peek(parser)->cn); - return NULL; - } - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token); - node->call.callee = left; - node->call.args = args; - node->call.args_count = args_count; - - left = node; - } - } - else if (is(parser, FZL_TOKTYPE_DOT)) - { - while (is(parser, FZL_TOKTYPE_DOT)) - { - consume(parser); - - fzl_node_t *right = parse_primary(parser); - if (!right) - return NULL; - - fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FIELD, right->token); - node->memb.parent = left; - node->memb.field = right; - - left = node; } } @@ -567,10 +572,33 @@ fzl_node_t *parse_or(fzl_parser_t *parser) return left; } +// Object construct parsing +fzl_node_t *parse_new(fzl_parser_t *parser) +{ + if (!is(parser, FZL_TOKTYPE_NEW)) + return parse_or(parser); + + fzl_token_t *new = consume(parser); + fzl_node_t *objname = parse_or(parser); + if (objname->type != FZL_NODETYPE_IDENTIFIER && + objname->type != FZL_NODETYPE_FIELD) + { + snprintf(plast_err, sizeof(plast_err), + "'%s': expect class name after 'new' keyword at %ld:%ld", + last_known_tok->filename, last_known_tok->ln, last_known_tok->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CONSTRUCT, new); + node->construct.name = objname; + + return node; +} + // Assignments parsing fzl_node_t *parse_assignment(fzl_parser_t *parser) { - fzl_node_t *left = parse_or(parser); + fzl_node_t *left = parse_new(parser); if (!left) return NULL; @@ -1090,6 +1118,15 @@ void fzl_freeast(fzl_node_t *root) fzl_freenode(root->class.methods[i]); free(root->class.methods); } + else if (root->type == FZL_NODETYPE_CONSTRUCT) + { + fzl_freenode(root->construct.name); + } + else if (root->type == FZL_NODETYPE_FIELD) + { + fzl_freenode(root->memb.field); + fzl_freenode(root->memb.parent); + } root = root->next; free(root); diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index eb46f75..05206b8 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,9 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } - walker->ctx = NULL; + walker->ctx = NULL; + walker->classes_count = 0; + memset(walker->classes, 0, sizeof(walker->classes)); return walker; } @@ -103,6 +106,36 @@ static bool is_compatible(fzl_type_t t1, fzl_type_t t2) return t1 >= FZL_TYPE_INT8 && t2 >= FZL_TYPE_INT8; } +// Add one layer of ctx +fzl_walk_ctx_t *nest_ctx(fzl_walker_t *walker, fzl_walk_ctx_type_t type) +{ + if (!walker) + return NULL; + + fzl_walk_ctx_t *newctx = malloc(sizeof(fzl_walk_ctx_t)); + if (!newctx) + return NULL; + + newctx->type = type; + newctx->parent = walker->ctx; + walker->ctx = newctx; + + return walker->ctx; +} + +// Go outside current ctx and then clean it +fzl_walk_ctx_t *fin_ctx(fzl_walker_t *walker) +{ + if (!walker) + return NULL; + + fzl_walk_ctx_t *curr = walker->ctx; + walker->ctx = walker->ctx->parent; + + free(curr); + return walker->ctx; +} + // Add const to consts table size_t add_const(fzl_walker_t *walker, fzl_const_t c) { @@ -154,6 +187,178 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { switch (node->type) { + case FZL_NODETYPE_CLASS: + { + fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_CLASS); + if (!newctx) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + newctx->fn = node; + newctx->initial_env_lvl = walker->sym_top; + + walker->classes[walker->classes_count] = + fzl_mkctable(node->class.name->value); + fzl_ctable_t *class = walker->classes[walker->classes_count++]; + class->name = node->class.name->value; + + for (size_t i = 0; i < node->class.fields_count; i++) + { + if (class->fields_remainder <= class->fields_count) + { + class->fields_remainder *= 2; + class->fields = + realloc(class->fields, + class->fields_remainder * sizeof(fzl_ctable_field_t)); + } + + class->fields[class->fields_count] = (fzl_ctable_field_t){ + .name = node->class.fields[i]->var.identifier->token->value, + .type = tok_to_type(node->class.fields[i]->var.type), + .slot_index = class->fields_count++}; + } + + for (size_t i = 0; i < node->class.methods_count; i++) + { + fzl_node_t *fn = node->class.methods[i]; + + size_t classname_len = strlen(class->name); + size_t fnname_len = strlen(fn->fn.name->value); + char *method_name = malloc(classname_len + fnname_len + 2); + + strcpy(method_name, class->name); + method_name[classname_len] = '_'; + strcpy((method_name + classname_len + 1), fn->fn.name->value); + method_name[classname_len + fnname_len + 1] = 0; + + fn->fn.name->value = method_name; + fzl_walk_res_t fnres = walk(walker, fn); + if (fnres.error) + return fnres; + } + + if (!fin_ctx(walker)) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; + } + + case FZL_NODETYPE_CONSTRUCT: + { + fzl_node_t *classname = node->construct.name; + fzl_ctable_t *ct = NULL; + for (size_t i = 0; i < walker->classes_count; i++) + { + if (strcmp((char *)classname->token->value, + walker->classes[i]->name) == 0) + { + ct = walker->classes[i]; + break; + } + } + if (!ct) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': class '%s' not found (at %ld:%ld)", + classname->token->filename, (char *)classname->token->value, + classname->token->ln, classname->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + push_op(walker, FZL_OPCODE_ALLOC_OBJECT); + push_word(walker, ct->fields_count); + + current_res = (fzl_walk_res_t){ + .type = FZL_TYPE_OBJECT, .error = false, .classname = ct->name}; + break; + } + + case FZL_NODETYPE_FIELD: + { + fzl_sym_t sym; + bool exists = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((exists = fzl_symt_get(walker->syms[i], + node->memb.parent->token->value, &sym))) + break; + + if (!exists) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': cannot find symbol '%s' at %ld:%ld", + node->token->filename, (char *)node->token->value, + node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (!sym.classname) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': '%s' is not an object (at %ld:%ld)", + node->token->filename, sym.name, node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + fzl_ctable_t *ct = NULL; + for (size_t i = 0; i < walker->classes_count; i++) + { + if (strcmp(sym.classname, walker->classes[i]->name) == 0) + { + ct = walker->classes[i]; + break; + } + } + + fzl_token_t *tok = node->memb.parent->token; + if (!ct) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': access to unknown class '%s' at %ld:%ld", + tok->filename, (char *)tok->value, tok->ln, tok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + fzl_token_t *ftok = node->memb.field->token; + fzl_ctable_field_t ctf; + bool field_exists = false; + for (size_t i = 0; i < ct->fields_count; i++) + { + if (strcmp(ftok->value, ct->fields[i].name) == 0) + { + field_exists = true; + ctf = ct->fields[i]; + break; + } + } + + if (!field_exists) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': cannot find field '%s' in class '%s' at %ld:%ld", + ftok->filename, (char *)ftok->value, (char *)tok->value, + ftok->ln, ftok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + fzl_walk_res_t parent_res = walk(walker, node->memb.parent); + if (parent_res.error) + return parent_res; + + push_op(walker, FZL_OPCODE_GET_FIELD); + push_word(walker, ctf.slot_index); + + current_res = (fzl_walk_res_t){ + .type = ctf.type, .error = false, .classname = sym.classname}; + break; + } + case FZL_NODETYPE_FUNCTION: { push_op(walker, FZL_OPCODE_JMP); @@ -183,9 +388,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) // NOTE: add sym_top limit check size_t lvl = walker->sym_top; walker->syms[walker->sym_top++] = fzl_mksymt(); - walker->ctx = malloc(sizeof(fzl_walk_ctx_t)); - walker->ctx->fn = node; - walker->ctx->initial_env_lvl = lvl; + + fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_FN); + if (!newctx) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + newctx->type = FZL_WALK_CTX_TYPE_FN; + newctx->fn = node; + newctx->initial_env_lvl = lvl; push_op(walker, FZL_OPCODE_MK_ENVTABLE); @@ -205,18 +418,25 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) // near future push_op(walker, FZL_OPCODE_RM_ENVTABLE); + push_op(walker, FZL_OPCODE_RET); walker->ops[skip_ip] = walker->op_count; - walker->ctx = NULL; // NOTE: think about nested function defs (not now) + if (!fin_ctx(walker)) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + for (ssize_t i = walker->sym_top - 1; i >= lvl; i--) fzl_freesymt(walker->syms[--walker->sym_top]); + current_res = (fzl_walk_res_t){.type = fntype, .error = false}; break; } case FZL_NODETYPE_RETURN: { - if (!walker->ctx) + if (!walker->ctx || walker->ctx->type != FZL_WALK_CTX_TYPE_FN) { snprintf( wlast_err, sizeof(wlast_err), @@ -415,7 +635,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_word(walker, n); push_op(walker, FZL_OPCODE_LOOKUP_ENV); - current_res = (fzl_walk_res_t){.type = sym.type, .error = false}; + current_res = (fzl_walk_res_t){ + .type = sym.type, .error = false, .classname = sym.classname}; break; } @@ -434,18 +655,18 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) break; case FZL_NODETYPE_CALL: - if (node->call.args_count > 0) - { - for (size_t i = 0; i < node->call.args_count; i++) - { - fzl_walk_res_t res = walk(walker, node->call.args[i]); - if (res.error) - return res; - } - } - if (strcmp(node->token->value, "printline") == 0) { + if (node->call.args_count > 0) + { + for (size_t i = 0; i < node->call.args_count; i++) + { + fzl_walk_res_t res = walk(walker, node->call.args[i]); + if (res.error) + return res; + } + } + push_op(walker, FZL_OPCODE_CALL_BUILTIN); push_word(walker, FZL_BUILTIN_PRINTLINE); if (node->call.args_count < 1) @@ -459,32 +680,138 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) } else { - fzl_sym_t res; - bool found = false; - for (ssize_t i = walker->sym_top - 1; i >= 0; i--) - if ((found = - fzl_symt_get(walker->syms[i], node->token->value, &res))) - break; - - if (!found) + if (node->call.callee->type == FZL_NODETYPE_IDENTIFIER) { - snprintf(wlast_err, sizeof(wlast_err), - "call to unknown function '%s'", - (char *)node->token->value); - return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; - } + if (node->call.args_count > 0) + { + for (size_t i = 0; i < node->call.args_count; i++) + { + fzl_walk_res_t res = walk(walker, node->call.args[i]); + if (res.error) + return res; + } + } - if (!res.ip) + fzl_sym_t res; + bool found = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((found = + fzl_symt_get(walker->syms[i], node->token->value, &res))) + break; + + if (!found) + { + snprintf(wlast_err, sizeof(wlast_err), + "call to unknown function '%s'", + (char *)node->token->value); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (!res.ip) + { + snprintf(wlast_err, sizeof(wlast_err), + "cannot jump to address 0x0"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + push_op(walker, FZL_OPCODE_CALL); + push_word(walker, res.ip); + + current_res = (fzl_walk_res_t){.type = res.type, .error = false}; + } + else if (node->call.callee->type == FZL_NODETYPE_FIELD) { - snprintf(wlast_err, sizeof(wlast_err), - "cannot jump to address 0x0"); - return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + fzl_node_t *parent_node = node->call.callee->memb.parent; + + fzl_sym_t res; + bool found = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((found = fzl_symt_get(walker->syms[i], + parent_node->token->value, &res))) + break; + + if (!found) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': access to unknown object '%s' at %ld:%ld", + node->token->filename, (char *)parent_node->token->value, + node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (res.type != FZL_TYPE_OBJECT || !res.classname) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': name '%s' is not an object (at %ld:%ld)", + parent_node->token->filename, + (char *)parent_node->token->value, + parent_node->token->ln, parent_node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + found = false; + for (size_t i = 0; i < walker->classes_count; i++) + { + if (strcmp(walker->classes[i]->name, res.classname) == 0) + { + found = true; + break; + } + } + + if (!found) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': class '%s' not found at %ld:%ld", + node->token->filename, res.classname, node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + char *classname = res.classname; + size_t classlen = strlen(res.classname); + size_t fnlen = strlen(node->call.callee->token->value); + char *methodname = malloc(classlen + fnlen + 2); + strcpy(methodname, res.classname); + strcpy((methodname + classlen + 1), + node->call.callee->token->value); + methodname[classlen] = '_'; + methodname[classlen + fnlen + 1] = 0; + + found = false; + for (ssize_t i = walker->sym_top - 1; i >= 0; i--) + if ((found = fzl_symt_get(walker->syms[i], methodname, &res))) + break; + + if (!found) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': class '%s' has no method '%s' at %ld:%ld", + node->token->filename, classname, + (char *)node->call.callee->token->value, node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + fzl_walk_res_t parent = walk(walker, parent_node); + if (parent.error) + return parent; + + if (node->call.args_count > 0) + { + for (size_t i = 0; i < node->call.args_count; i++) + { + fzl_walk_res_t res = walk(walker, node->call.args[i]); + if (res.error) + return res; + } + } + + push_op(walker, FZL_OPCODE_CALL); + push_word(walker, res.ip); + + current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } - - push_op(walker, FZL_OPCODE_CALL); - push_word(walker, res.ip); - - current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } break; @@ -604,9 +931,33 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) case FZL_NODETYPE_DECLARATION: { + fzl_ctable_t *ct = NULL; + for (size_t i = 0; i < walker->classes_count; i++) + { + if (strcmp(node->var.type->value, walker->classes[i]->name) == 0) + { + ct = walker->classes[i]; + break; + } + } + 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), 0); + node->var.identifier->token->value, + ct != NULL ? FZL_TYPE_OBJECT : tok_to_type(node->var.type), 0); + + if (ct) + fzl_symt_setobjclass(walker->syms[walker->sym_top - 1], + node->var.identifier->token->value, ct->name); + + if (res != FZL_SYMTABLE_RESULT_OK) + { + fzl_token_t *tok = node->var.identifier->token; + snprintf(wlast_err, sizeof(wlast_err), + "'%s': cannot store '%s' at %ld:%ld", tok->filename, + (char *)tok->value, tok->ln, tok->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } fzl_token_t *id_tok = node->var.identifier->token; size_t const_n = add_const( @@ -631,11 +982,20 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) if (rres.error) return rres; + fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_ASSIGN); + if (!newctx) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + 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; + + push_op(walker, FZL_OPCODE_WRITE); } else if (node->binary_op.left->type == FZL_NODETYPE_IDENTIFIER) { @@ -672,9 +1032,65 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) node->binary_op.left->token->value_sz}); push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, const_n); + push_op(walker, FZL_OPCODE_WRITE); + } + else if (node->binary_op.left->type == FZL_NODETYPE_FIELD) + { + fzl_node_t *field_node = node->binary_op.left; + fzl_walk_res_t obj_res = walk(walker, field_node->memb.parent); + if (obj_res.error) + return obj_res; + + fzl_ctable_t *ct; + for (size_t i = 0; i < walker->classes_count; i++) + { + if (strcmp(obj_res.classname, walker->classes[i]->name) == 0) + { + ct = walker->classes[i]; + break; + } + } + + if (!ct) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': cannot find class '%s' at %ld:%ld", + field_node->token->filename, obj_res.classname, + field_node->token->ln, field_node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + fzl_ctable_field_t *ctf; + for (size_t i = 0; i < ct->fields_count; i++) + { + if (strcmp(field_node->memb.field->token->value, + ct->fields[i].name) == 0) + { + ctf = &ct->fields[i]; + break; + } + } + + if (!ctf) + { + fzl_node_t *fn = field_node->memb.field; + snprintf(wlast_err, sizeof(wlast_err), + "'%s': class '%s' has no field '%s' at %ld:%ld", + fn->token->filename, obj_res.classname, + (char *)fn->token->value, fn->token->ln, fn->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + push_op(walker, FZL_OPCODE_SET_FIELD); + push_word(walker, ctf->slot_index); + } + + if (!fin_ctx(walker)) + { + snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } - push_op(walker, FZL_OPCODE_WRITE); current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; break; } @@ -695,10 +1111,14 @@ int fzl_walk(fzl_walker_t *walker) return -1; } + nest_ctx(walker, FZL_WALK_CTX_TYPE_GLOBAL); + push_op(walker, FZL_OPCODE_MK_ENVTABLE); fzl_walk_res_t result = walk(walker, walker->root); push_op(walker, FZL_OPCODE_RM_ENVTABLE); + fin_ctx(walker); + if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); @@ -722,5 +1142,8 @@ void fzl_freewalker(fzl_walker_t *walker) while (walker->sym_top > 0) fzl_freesymt(walker->syms[--walker->sym_top]); + for (size_t i = 0; i < walker->classes_count; i++) + fzl_freectable(walker->classes[i]); + free(walker); } diff --git a/src/compiler/utils/fzl_ctable.c b/src/compiler/utils/fzl_ctable.c new file mode 100644 index 0000000..1b7edab --- /dev/null +++ b/src/compiler/utils/fzl_ctable.c @@ -0,0 +1,29 @@ +#include +#include +#include + +fzl_ctable_t *fzl_mkctable(char *name) +{ + fzl_ctable_t *ctable = malloc(sizeof(fzl_ctable_t)); + if (!ctable) + return NULL; + + memset(ctable, 0, sizeof(fzl_ctable_t)); + ctable->name = name; + ctable->fields_remainder = 2; + ctable->fields = + malloc(ctable->fields_remainder * sizeof(fzl_ctable_field_t)); + + return ctable; +} + +void fzl_freectable(fzl_ctable_t *ct) +{ + if (!ct) + return; + + if (ct->fields) + free(ct->fields); + + free(ct); +} diff --git a/src/compiler/utils/fzl_symtable.c b/src/compiler/utils/fzl_symtable.c index 262958e..a1c5f5d 100644 --- a/src/compiler/utils/fzl_symtable.c +++ b/src/compiler/utils/fzl_symtable.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -37,10 +38,31 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name, } symt->symbols[symt->size++] = - (fzl_sym_t){.name = name, .type = type, .ip = ip}; + (fzl_sym_t){.name = name, .type = type, .ip = ip, .classname = NULL}; return FZL_SYMTABLE_RESULT_OK; } +fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name, + char *classname) +{ + if (!symt) + return FZL_SYMTABLE_RESULT_ERR; + + for (size_t i = 0; i < symt->size; i++) + { + if (strcmp(symt->symbols[i].name, name) == 0) + { + if (symt->symbols[i].type != FZL_TYPE_OBJECT) + return FZL_SYMTABLE_RESULT_NOT_OBJECT; + + symt->symbols[i].classname = classname; + return FZL_SYMTABLE_RESULT_OK; + } + } + + return FZL_SYMTABLE_RESULT_ERR; +} + bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res) { if (!symt) diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 899c82b..7573e24 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -1,4 +1,3 @@ -#include "vm/fzl_vm_env.h" #include #include #include @@ -10,6 +9,8 @@ #include #include #include +#include +#include char last_err[256]; @@ -24,18 +25,20 @@ fzl_vm_t *fzl_mkvm(const char *filepath) return NULL; } - vm->consts = NULL; - vm->consts_count = 0; - vm->ops = NULL; - vm->ops_count = 0; - vm->ip = 0; - vm->dtop = 0; - vm->ctop = 0; - vm->etop = 0; + vm->consts = NULL; + vm->consts_count = 0; + vm->ops = NULL; + vm->ops_count = 0; + vm->ip = 0; + vm->dtop = 0; + vm->ctop = 0; + vm->etop = 0; + vm->objects_count = 0; memset(vm->dstack, 0, sizeof(vm->dstack)); memset(vm->cstack, 0, sizeof(vm->cstack)); memset(vm->estack, 0, sizeof(vm->estack)); + memset(vm->objects, 0, sizeof(vm->objects)); FILE *fd = fopen(filepath, "rb"); if (!fd) @@ -122,6 +125,84 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_ALLOC_OBJECT: + { + uint64_t values_count = vm->ops[++vm->ip]; + fzl_vm_object_t *obj = malloc(sizeof(fzl_vm_object_t)); + obj->values = malloc(values_count * sizeof(fzl_value_t)); + obj->values_count = values_count; + + vm->objects[vm->objects_count++] = obj; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = obj}; + break; + } + + case FZL_OPCODE_GET_FIELD: + { + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + return -1; + } + + uint64_t field_slot = vm->ops[++vm->ip]; + fzl_value_t val = vm->dstack[--vm->dtop]; + if (val.type != FZL_TYPE_OBJECT) + { + snprintf( + last_err, sizeof(last_err), + "runtime error - cannot access field #%lu of non-object type", + field_slot); + return -1; + } + + fzl_vm_object_t *obj = val.as.obj; + if (obj->values_count <= field_slot) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu", field_slot); + return -1; + } + + vm->dstack[vm->dtop++] = obj->values[field_slot]; + break; + } + + case FZL_OPCODE_SET_FIELD: + { + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + uint64_t field_slot = vm->ops[++vm->ip]; + fzl_value_t objval = vm->dstack[--vm->dtop]; + fzl_value_t wrval = vm->dstack[--vm->dtop]; + if (objval.type != FZL_TYPE_OBJECT) + { + snprintf( + last_err, sizeof(last_err), + "runtime error - cannot access field #%lu of non-object type", + field_slot); + return -1; + } + + fzl_vm_object_t *obj = objval.as.obj; + if (obj->values_count <= field_slot) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu", field_slot); + return -1; + } + + obj->values[field_slot] = wrval; + break; + } + case FZL_OPCODE_CALL: { uint64_t ip = vm->ops[++vm->ip]; diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index 11727a0..9f3f3ef 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -20,15 +20,27 @@ void fzl_vm_print_ops(fzl_vm_t *vm) switch (vm->ops[i]) { + case FZL_OPCODE_ALLOC_OBJECT: + printf("ALLOC_OBJECT: %lu fields\n", vm->ops[++i]); + break; + + case FZL_OPCODE_GET_FIELD: + printf("GET_FIELD #%lu\n", vm->ops[++i]); + break; + + case FZL_OPCODE_SET_FIELD: + printf("SET_FIELD #%lu\n", vm->ops[++i]); + break; + case FZL_OPCODE_CALL: { size_t ip = vm->ops[++i]; - printf("Call IP: %lu\n", ip); + printf("CALL IP: %lu\n", ip); break; } case FZL_OPCODE_RET: - printf("Return\n"); + printf("RET\n"); break; case FZL_OPCODE_LOADCONST: @@ -77,7 +89,7 @@ void fzl_vm_print_ops(fzl_vm_t *vm) case FZL_OPCODE_DIV: case FZL_OPCODE_POW: case FZL_OPCODE_MOD: - printf("Math opcode\n"); + printf("MATH\n"); break; case FZL_OPCODE_AND: @@ -129,7 +141,7 @@ void fzl_vm_print_ops(fzl_vm_t *vm) break; default: - printf("\n"); + printf("\n"); } } } -- 2.54.0 From 4e7d09cfa9ec5ed039406d3c8053703b84cb84dc Mon Sep 17 00:00:00 2001 From: samsonium Date: Fri, 16 Oct 2026 18:31:10 +0300 Subject: [PATCH 33/34] fix: add objects cleanup after execution --- src/vm/fzl_vm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 7573e24..7c22d8f 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -638,6 +638,12 @@ void fzl_freevm(fzl_vm_t *vm) free((void *)vm->consts[i].as.string.chars); } + for (size_t i = 0; i < vm->objects_count; i++) + { + free(vm->objects[i]->values); + free(vm->objects[i]); + } + free(vm->consts); free(vm->ops); free(vm); -- 2.54.0 From c643f200a13b8c71fe746e7c5a94899deeba10e9 Mon Sep 17 00:00:00 2001 From: samsonium Date: Fri, 16 Oct 2026 18:31:19 +0300 Subject: [PATCH 34/34] upd: update READMEs --- README.md | 3 ++- README.ru.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da6c7ab..80001d9 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,9 @@ This file provides set up codestyle rules. There is two targets in Makefile for debugging purposes: `build-c` for the compiler and `build-vm` for the VM. Each target can be called with `FILE` variable. For example: - ```shell $ make debug-c FILE=exl/hw.fzs ``` +To test new features, use `test` make target and the tests, defined in `CMakeLists.txt`. + diff --git a/README.ru.md b/README.ru.md index 0280ea0..875cdb5 100644 --- a/README.ru.md +++ b/README.ru.md @@ -57,3 +57,5 @@ $ make debug-c FILE=exl/hw.fzs ``` +Для проверки новых фич, нужно использовать make-таргет `test`, а тесты писать в `CMakeLists.txt`. + -- 2.54.0