18 Commits
Author SHA1 Message Date
samsonium a885746fdd feat: add object escaping on assing to variable in outer env 2026-10-29 17:46:56 +03:00
samsonium 2cf96d06c6 fix: fmtstring printing 2026-10-29 17:46:20 +03:00
samsonium bbdf91ee02 fix: cutted space in string literal begin 2026-10-29 17:46:08 +03:00
samsonium 27374a96eb fix: AST levels print 2026-10-29 17:45:39 +03:00
samsonium dcab2a8875 feat: add runtime speed comparison between fz, c and cpython 2026-10-29 05:21:31 +03:00
samsonium 7b804b8809 feat: optimize runtime (2x+ faster) 2026-10-29 05:21:00 +03:00
samsonium eeca5e653d feat: add benchmar Make target 2026-10-29 03:25:48 +03:00
samsonium f4c97795fa feat: optimize VM using dispatch table 2026-10-29 02:59:38 +03:00
samsonium e2de23ed57 fix: suppress libffi cmake warnings 2026-10-29 00:26:33 +03:00
samsonium 8c90d54782 ref: split parser into separate files 2026-10-29 00:08:44 +03:00
samsonium 62b0030e4a ref: move analyzer fn defs into compiler/iternals 2026-10-28 23:24:17 +03:00
samsonium bbdaa4a9b9 ref: split reader into separate files 2026-10-28 23:12:46 +03:00
samsonium 7c04d8b7ec ref: remove unused loop checker 2026-10-28 23:12:06 +03:00
samsonium 567bf49d27 wip: working on dynamic linking
- Feat: add example for wrapping dynamically linked library
- Feat: add new tokens (import, extern, type, handle, struct, ref, out)
2026-10-28 21:43:56 +03:00
samsonium d93c315359 fix: clear first AST node if got error inside parse_program 2026-10-28 19:37:17 +03:00
samsonium e11089c90f feat: implement format strings 2026-10-28 17:57:18 +03:00
samsonium ea28c470d2 wip: begin dynamic library loading dev 2026-10-28 15:20:58 +03:00
samsonium 70bf63e2ea feat: add libffi cmake dependency 2026-10-28 00:31:09 +03:00
102 changed files with 3726 additions and 2632 deletions
+1
View File
@@ -5,6 +5,7 @@ skip is
skip -rfunction fzl_mk* skip -rfunction fzl_mk*
skip -rfunction fzl_free* skip -rfunction fzl_free*
skip -rfunction fzl_symt* skip -rfunction fzl_symt*
skip -rfunction fzl_linked*
skip add_const skip add_const
skip push_op skip push_op
skip push_word skip push_word
+2 -1
View File
@@ -3,7 +3,9 @@
# Dirs # Dirs
!/docs !/docs
!/exl !/exl
!/benchmarks
!/include !/include
!/scripts
!/src !/src
!/tests !/tests
@@ -15,4 +17,3 @@
!/Makefile !/Makefile
!/README.md !/README.md
!/README.ru.md !/README.ru.md
+61 -1
View File
@@ -11,6 +11,22 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif() endif()
include_directories(include) include_directories(include)
include(FetchContent)
# --- libffi ---
set(PREV_SUPPRESS_DEV ${CMAKE_SUPPRESS_DEVELOPER_WARNINGS})
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS TRUE CACHE INTERNAL "")
FetchContent_Declare(ffi
GIT_REPOSITORY "https://git.samsonium.dev/samsonium/libffi.git"
GIT_TAG "main"
)
FetchContent_MakeAvailable(ffi)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ${PREV_SUPPRESS_DEV} CACHE INTERNAL "")
# --------------
# Compiler # Compiler
add_executable(fzc add_executable(fzc
@@ -19,7 +35,6 @@ add_executable(fzc
src/compiler/utils/fzl_linked.c src/compiler/utils/fzl_linked.c
src/compiler/utils/fzl_options.c src/compiler/utils/fzl_options.c
src/compiler/utils/fzl_ctable.c src/compiler/utils/fzl_ctable.c
src/compiler/checkers/fzl_loop_check.c
src/compiler/analyzer/utility.c src/compiler/analyzer/utility.c
src/compiler/analyzer/context.c src/compiler/analyzer/context.c
src/compiler/analyzer/an_assign.c src/compiler/analyzer/an_assign.c
@@ -38,8 +53,38 @@ add_executable(fzc
src/compiler/analyzer/an_identifier.c src/compiler/analyzer/an_identifier.c
src/compiler/analyzer/an_loop.c src/compiler/analyzer/an_loop.c
src/compiler/analyzer/an_return.c src/compiler/analyzer/an_return.c
src/compiler/analyzer/an_strfmt.c
src/compiler/analyzer/an_unarycond.c src/compiler/analyzer/an_unarycond.c
src/compiler/analyzer/an_unarymath.c src/compiler/analyzer/an_unarymath.c
src/compiler/parser/pa_and.c
src/compiler/parser/pa_assignment.c
src/compiler/parser/pa_block.c
src/compiler/parser/pa_break.c
src/compiler/parser/pa_class.c
src/compiler/parser/pa_cond.c
src/compiler/parser/pa_expression.c
src/compiler/parser/pa_factor.c
src/compiler/parser/pa_for.c
src/compiler/parser/pa_function.c
src/compiler/parser/pa_if.c
src/compiler/parser/pa_loop.c
src/compiler/parser/pa_new.c
src/compiler/parser/pa_or.c
src/compiler/parser/pa_postfix.c
src/compiler/parser/pa_power.c
src/compiler/parser/pa_primary.c
src/compiler/parser/pa_program.c
src/compiler/parser/pa_return.c
src/compiler/parser/pa_statement.c
src/compiler/parser/pa_term.c
src/compiler/parser/pa_unary.c
src/compiler/parser/pa_while.c
src/compiler/parser/utility.c
src/compiler/reader/rd_fmtstr.c
src/compiler/reader/rd_name.c
src/compiler/reader/rd_number.c
src/compiler/reader/rd_string_literal.c
src/compiler/reader/utility.c
src/compiler/fzl_analyzer.c src/compiler/fzl_analyzer.c
src/compiler/fzl_debug.c src/compiler/fzl_debug.c
src/compiler/fzl_parser.c src/compiler/fzl_parser.c
@@ -51,11 +96,26 @@ add_executable(fzc
# Virtual machine # Virtual machine
add_executable(fz add_executable(fz
src/vm/handlers/group_and_or.c
src/vm/handlers/group_cmp.c
src/vm/handlers/group_ctx.c
src/vm/handlers/group_flow.c
src/vm/handlers/group_fn.c
src/vm/handlers/group_math.c
src/vm/handlers/group_not.c
src/vm/handlers/group_obj.c
src/vm/handlers/group_objcmp.c
src/vm/handlers/group_stack.c
src/vm/handlers/group_str.c
src/vm/handlers/group_strcmp.c
src/vm/handlers/master.c
src/vm/fzl_vm.c src/vm/fzl_vm.c
src/vm/fzl_vm_env.c src/vm/fzl_vm_env.c
src/vm/fzl_vm_debug.c src/vm/fzl_vm_debug.c
src/vm/main.c) src/vm/main.c)
target_link_libraries(fz PRIVATE ffi_static dl)
# #
# ========== Tests ========== # ========== Tests ==========
# #
+40 -4
View File
@@ -1,15 +1,22 @@
.PHONY: build debug-c debug-vm test .PHONY: build debug-c debug-vm test bench
BENCH_FILE ?= benchmarks/loop.fzs
RUNS ?= 7
WARMUP ?= 2
MAX_ITER ?= 2000000
current_time = python3 -c "import time; print(time.time())"
build: build:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build cmake --build build
build-release: build-release:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake -S . -B build -DCMAKE_BUILD_TYPE=Release $(COMPARING)
cmake --build build cmake --build build $(COMPARING)
debug-c: build debug-c: build
gdb --args ./build/fzc ${FILE} gdb --args ./build/fzc -v ${FILE}
debug-vm: build debug-vm: build
gdb --args ./build/fz ${FILE} gdb --args ./build/fz ${FILE}
@@ -17,3 +24,32 @@ debug-vm: build
test: build test: build
ctest --output-on-failure --test-dir build ctest --output-on-failure --test-dir build
test-release: build-release
ctest --output-on-failure --test-dir build
bench: COMPARING=&>/dev/null
bench: build-release
@./build/fzc -o build/benchmark.fzb $(BENCH_FILE)
@python3 scripts/benchmark.py --runs $(RUNS) --warmup $(WARMUP) \
--max-iter $(MAX_ITER) --vm ./build/fz --program build/benchmark.fzb
compare: COMPARING=&>/dev/null
compare: build-release
@echo "=== Million sums benchmark ==="
@./build/fzc -o build/bench.fzb $(BENCH_FILE) 1>/dev/null 2>/dev/null
@ST=$$($(current_time)); \
./build/fz --allow-infinite build/bench.fzb 1>/dev/null 2>/dev/null; \
ET=$$($(current_time)); \
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'FZLang : {delta:.5f} ms')"
@ST=$$($(current_time)); \
./benchmarks/loop-c 1>/dev/null 2>/dev/null; \
ET=$$($(current_time)); \
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'C : {delta:.5f} ms')"
@ST=$$($(current_time)); \
python3 ./benchmarks/loop.py 1>/dev/null 2>/dev/null; \
ET=$$($(current_time)); \
python3 -c "delta = ($$ET - $$ST) * 1000; print(f'CPython: {delta:.5f} ms')"
+17
View File
@@ -51,3 +51,20 @@ $ make debug-c FILE=exl/hw.fzs
Для проверки новых фич, нужно использовать make-таргет `test`, а тесты писать в `CMakeLists.txt`. Для проверки новых фич, нужно использовать make-таргет `test`, а тесты писать в `CMakeLists.txt`.
### Бенчмаркинг
`make bench` собирает Release-версию, компилирует `benchmarks/loop.fzs` один раз и
измеряет время запуска программы в виртуальной машине. По умолчанию выполняются
2 прогрева и 7 замеров; выводятся медиана, среднее, минимум и максимум. Компиляция
исходника в измерения не входит.
Параметры можно переопределить:
```shell
make bench RUNS=15 WARMUP=3
make bench BENCH_FILE=path/to/program.fzs MAX_ITER=2000000
```
Измеряется полное время запуска процесса `fz` (включая загрузку программы), а не
только цикл интерпретации. Для сопоставимых результатов запускайте бенчмарк на
одной машине и не меняйте нагрузку между прогонами.
BIN
View File
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
// Integer loop workload for measuring VM execution without I/O.
uint i = 0;
uint sum = 0;
for (i = 0; i < 1000000; i = i + 1)
sum = sum + i;
printline(sum);
+4
View File
@@ -0,0 +1,4 @@
s = 0
for i in range(1_000_000):
s += i
print(s)
+38
View File
@@ -0,0 +1,38 @@
extern "libglfw" {
type GLFWwindow = handle;
type GLFWmonitor = handle;
struct GLFWvidmode {
int width;
int height;
int redBits;
int greenBits;
int blueBits;
int refreshRate;
}
/// GLFWvidmode*
type GLFWvidmodeRef = ref GLFWvidmode;
/// Create window with specified [width], [height] and [title]
fn glfw_mkWindow(
int width,
int height,
str title,
GLFWmonitor mon,
GLFWwindow shr
) GLFWwindow = "glfwCreateWindow";
/// Get videomode from monitor [mon]
fn glfw_getVideoMode(
GLFWmonitor mon
) GLFWvidmodeRef = "glfwGetVideoMode";
/// Get framebuffer [width] and [height]
fn glfw_getFrameBufferSize(
GLFWwindow win,
out int width,
out int height
) = "glfwGetFramebufferSize";
}
+4
View File
@@ -14,6 +14,8 @@ typedef enum
FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table
FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table
FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable
FZL_OPCODE_LOAD_LOCAL = 0xF10006, // Load local variable
FZL_OPCODE_STORE_LOCAL = 0xF10007, // Store local variable
FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address
FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero
@@ -35,6 +37,8 @@ typedef enum
FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in dstack FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in dstack
FZL_OPCODE_NEG = 0xA00007, // Negotiate number in dstack FZL_OPCODE_NEG = 0xA00007, // Negotiate number in dstack
FZL_OPCODE_BUILD_STR = 0xA10001, // Build a string from N values in dstack
FZL_OPCODE_CMP_EQ = 0xB00001, // Equal FZL_OPCODE_CMP_EQ = 0xB00001, // Equal
FZL_OPCODE_CMP_NEQ = 0xB00002, // Not equal FZL_OPCODE_CMP_NEQ = 0xB00002, // Not equal
FZL_OPCODE_CMP_LT = 0xB00003, // Less than FZL_OPCODE_CMP_LT = 0xB00003, // Less than
+2
View File
@@ -6,6 +6,8 @@ typedef enum
FZL_TYPE_NULL, FZL_TYPE_NULL,
FZL_TYPE_OBJECT, FZL_TYPE_OBJECT,
FZL_TYPE_EXTERN_FN,
FZL_TYPE_STRING, FZL_TYPE_STRING,
FZL_TYPE_CHAR, FZL_TYPE_CHAR,
FZL_TYPE_BOOL, FZL_TYPE_BOOL,
-28
View File
@@ -76,32 +76,4 @@ void fzl_analyze(fzl_analyzer_t *analyzer);
// Free analyzer // Free analyzer
void fzl_freeanalyzer(fzl_analyzer_t *analyzer); void fzl_freeanalyzer(fzl_analyzer_t *analyzer);
extern char an_problem[256];
bool an_is_compatible(fzl_type_t a, fzl_type_t b, char *ac, char *bc);
char *an_typetostr(fzl_type_t type, char *classname);
fzl_type_t an_toktotype(fzl_token_t *tok);
fzl_an_res_t analyze_common(fzl_analyzer_t *analyzer, fzl_node_t *node);
fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node);
fzl_an_res_t analyze_identifier(fzl_analyzer_t *analyzer, fzl_node_t *id);
fzl_an_res_t analyze_conditional(fzl_analyzer_t *analyzer, fzl_node_t *cond);
fzl_an_res_t analyze_function(fzl_analyzer_t *analyzer, fzl_node_t *fn_node);
fzl_an_res_t analyze_block(fzl_analyzer_t *analyzer, fzl_node_t *block_node);
fzl_an_res_t analyze_return(fzl_analyzer_t *analyzer, fzl_node_t *ret_node);
fzl_an_res_t analyze_class(fzl_analyzer_t *analyzer, fzl_node_t *class_node);
fzl_an_res_t analyze_field(fzl_analyzer_t *analyzer, fzl_node_t *field);
fzl_an_res_t analyze_declaration(fzl_analyzer_t *analyzer, fzl_node_t *decl);
fzl_an_res_t analyze_construct(fzl_analyzer_t *analyzer, fzl_node_t *construct);
fzl_an_res_t analyze_loop(fzl_analyzer_t *analyzer, fzl_node_t *loop_node);
fzl_an_res_t analyze_break(fzl_analyzer_t *analyzer, fzl_node_t *break_node);
fzl_an_res_t analyze_binmath(fzl_analyzer_t *analyzer, fzl_node_t *bin_node);
fzl_an_res_t analyze_unarymath(fzl_analyzer_t *analyzer, fzl_node_t *unary);
fzl_an_res_t analyze_condition(fzl_analyzer_t *analyzer, fzl_node_t *cond_node);
fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary);
fzl_an_res_t analyze_assign(fzl_analyzer_t *analyzer, fzl_node_t *assign_node);
fzl_an_ctx_t *an_nest_ctx(fzl_analyzer_t *analyzer, fzl_an_ctx_type_t type);
fzl_an_ctx_t *an_fin_ctx(fzl_analyzer_t *analyzer);
#endif // FZL_ANALYZER_H #endif // FZL_ANALYZER_H
+1
View File
@@ -11,6 +11,7 @@ typedef enum
FZL_NODETYPE_BLOCK, FZL_NODETYPE_BLOCK,
FZL_NODETYPE_CALL, FZL_NODETYPE_CALL,
FZL_NODETYPE_LITERAL, FZL_NODETYPE_LITERAL,
FZL_NODETYPE_FMT_STRING,
FZL_NODETYPE_IDENTIFIER, FZL_NODETYPE_IDENTIFIER,
FZL_NODETYPE_CONDITIONAL, FZL_NODETYPE_CONDITIONAL,
FZL_NODETYPE_FUNCTION, FZL_NODETYPE_FUNCTION,
+44 -33
View File
@@ -8,57 +8,68 @@
// Token type // Token type
typedef enum typedef enum
{ {
FZL_TOKTYPE_CLASS, // class FZL_TOKTYPE_IMPORT, // `import`
FZL_TOKTYPE_NEW, // new FZL_TOKTYPE_EXTERN, // `extern`
FZL_TOKTYPE_TYPEDEF, // `type`
FZL_TOKTYPE_HANDLE, // `handle`
FZL_TOKTYPE_STRUCT, // `struct`
FZL_TOKTYPE_REFERENCE, // `ref`
FZL_TOKTYPE_OUT, // `out`
FZL_TOKTYPE_FUNCTION, // fn FZL_TOKTYPE_CLASS, // `class`
FZL_TOKTYPE_RETURN, // return FZL_TOKTYPE_NEW, // `new`
FZL_TOKTYPE_FOR, // for FZL_TOKTYPE_FUNCTION, // `fn`
FZL_TOKTYPE_WHILE, // while FZL_TOKTYPE_RETURN, // `return`
FZL_TOKTYPE_BREAK, // break
FZL_TOKTYPE_IF, // if FZL_TOKTYPE_FOR, // `for`
FZL_TOKTYPE_ELSE, // else FZL_TOKTYPE_WHILE, // `while`
FZL_TOKTYPE_BREAK, // `break`
FZL_TOKTYPE_IF, // `if`
FZL_TOKTYPE_ELSE, // `else`
FZL_TOKTYPE_NAME, // abc FZL_TOKTYPE_NAME, // abc
FZL_TOKTYPE_TYPE, // i16 FZL_TOKTYPE_TYPE, // i16
FZL_TOKTYPE_DDASH, // // :D FZL_TOKTYPE_DDASH, // `//` :D
FZL_TOKTYPE_LPAREN, // ( FZL_TOKTYPE_LPAREN, // `(`
FZL_TOKTYPE_RPAREN, // ) FZL_TOKTYPE_RPAREN, // `)`
FZL_TOKTYPE_LBRACE, // { FZL_TOKTYPE_LBRACE, // `{`
FZL_TOKTYPE_RBRACE, // } FZL_TOKTYPE_RBRACE, // `}`
FZL_TOKTYPE_COMMA, // , FZL_TOKTYPE_COMMA, // `,`
FZL_TOKTYPE_DOT, // . FZL_TOKTYPE_DOT, // `.`
FZL_TOKTYPE_SEMICOLON, // ; FZL_TOKTYPE_SEMICOLON, // `;`
FZL_TOKTYPE_STR_LITERAL, // "" FZL_TOKTYPE_STR_LITERAL, // ""
FZL_TOKTYPE_NUM_LITERAL, // 123 FZL_TOKTYPE_NUM_LITERAL, // 123
FZL_TOKTYPE_BOOL_LITERAL, // true FZL_TOKTYPE_BOOL_LITERAL, // true
FZL_TOKTYPE_NULL_LITERAL, // null FZL_TOKTYPE_NULL_LITERAL, // null
FZL_TOKTYPE_DAMPERSAND, // && FZL_TOKTYPE_STR_FORMAT_START, // `$` before `"`
FZL_TOKTYPE_DVBAR, // || FZL_TOKTYPE_STR_FORMAT_END, // end of string formatting sequence
FZL_TOKTYPE_EQEQ, // == FZL_TOKTYPE_DAMPERSAND, // `&&`
FZL_TOKTYPE_REQ, // >= FZL_TOKTYPE_DVBAR, // `||`
FZL_TOKTYPE_LEQ, // <=
FZL_TOKTYPE_NEQ, // !=
FZL_TOKTYPE_LANGLE, // <
FZL_TOKTYPE_RANGLE, // >
FZL_TOKTYPE_NOT, // !
FZL_TOKTYPE_STAR, // * FZL_TOKTYPE_EQEQ, // `==`
FZL_TOKTYPE_SLASH, // / FZL_TOKTYPE_REQ, // `>=`
FZL_TOKTYPE_PLUS, // + FZL_TOKTYPE_LEQ, // `<=`
FZL_TOKTYPE_MINUS, // - FZL_TOKTYPE_NEQ, // `!=`
FZL_TOKTYPE_CARET, // ^ FZL_TOKTYPE_LANGLE, // `<`
FZL_TOKTYPE_PERCENT, // % FZL_TOKTYPE_RANGLE, // `>`
FZL_TOKTYPE_NOT, // `!`
FZL_TOKTYPE_EQ // = FZL_TOKTYPE_STAR, // `*`
FZL_TOKTYPE_SLASH, // `/`
FZL_TOKTYPE_PLUS, // `+`
FZL_TOKTYPE_MINUS, // `-`
FZL_TOKTYPE_CARET, // `^`
FZL_TOKTYPE_PERCENT, // `%`
FZL_TOKTYPE_EQ // `=`
} fzl_toktype_t; } fzl_toktype_t;
// Token descriptor // Token descriptor
+38
View File
@@ -0,0 +1,38 @@
#ifndef INTERNALS_ANALYZER_H
#define INTERNALS_ANALYZER_H
#include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h>
#include <compiler/fzl_token.h>
#include <stdbool.h>
extern char an_problem[256];
bool an_is_compatible(fzl_type_t a, fzl_type_t b, char *ac, char *bc);
char *an_typetostr(fzl_type_t type, char *classname);
fzl_type_t an_toktotype(fzl_token_t *tok);
fzl_an_res_t analyze_common(fzl_analyzer_t *analyzer, fzl_node_t *node);
fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node);
fzl_an_res_t analyze_identifier(fzl_analyzer_t *analyzer, fzl_node_t *id);
fzl_an_res_t analyze_conditional(fzl_analyzer_t *analyzer, fzl_node_t *cond);
fzl_an_res_t analyze_function(fzl_analyzer_t *analyzer, fzl_node_t *fn_node);
fzl_an_res_t analyze_block(fzl_analyzer_t *analyzer, fzl_node_t *block_node);
fzl_an_res_t analyze_return(fzl_analyzer_t *analyzer, fzl_node_t *ret_node);
fzl_an_res_t analyze_class(fzl_analyzer_t *analyzer, fzl_node_t *class_node);
fzl_an_res_t analyze_field(fzl_analyzer_t *analyzer, fzl_node_t *field);
fzl_an_res_t analyze_declaration(fzl_analyzer_t *analyzer, fzl_node_t *decl);
fzl_an_res_t analyze_construct(fzl_analyzer_t *analyzer, fzl_node_t *construct);
fzl_an_res_t analyze_loop(fzl_analyzer_t *analyzer, fzl_node_t *loop_node);
fzl_an_res_t analyze_break(fzl_analyzer_t *analyzer, fzl_node_t *break_node);
fzl_an_res_t analyze_binmath(fzl_analyzer_t *analyzer, fzl_node_t *bin_node);
fzl_an_res_t analyze_unarymath(fzl_analyzer_t *analyzer, fzl_node_t *unary);
fzl_an_res_t analyze_condition(fzl_analyzer_t *analyzer, fzl_node_t *cond_node);
fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary);
fzl_an_res_t analyze_assign(fzl_analyzer_t *analyzer, fzl_node_t *assign_node);
fzl_an_res_t analyze_strfmt(fzl_analyzer_t *analyzer, fzl_node_t *strfmt_node);
fzl_an_ctx_t *an_nest_ctx(fzl_analyzer_t *analyzer, fzl_an_ctx_type_t type);
fzl_an_ctx_t *an_fin_ctx(fzl_analyzer_t *analyzer);
#endif // INTERNALS_ANALYZER_H
+45
View File
@@ -0,0 +1,45 @@
#ifndef INTERNALS_PARSER_H
#define INTERNALS_PARSER_H
#include "compiler/fzl_parser.h"
#include "compiler/fzl_token.h"
extern char plast_err[256];
extern fzl_token_t *last_known_tok;
extern fzl_token_t null_tok;
extern fzl_token_t this_tok;
fzl_token_t *peek(fzl_parser_t *parser);
fzl_token_t *consume(fzl_parser_t *parser);
bool is(fzl_parser_t *parser, fzl_toktype_t type);
fzl_nodetype_t math_op_to_type(fzl_toktype_t type);
fzl_nodetype_t unary_op_to_type(fzl_toktype_t type);
fzl_node_t *pa_primary(fzl_parser_t *parser);
fzl_node_t *pa_postfix(fzl_parser_t *parser);
fzl_node_t *pa_unary(fzl_parser_t *parser);
fzl_node_t *pa_power(fzl_parser_t *parser);
fzl_node_t *pa_factor(fzl_parser_t *parser);
fzl_node_t *pa_term(fzl_parser_t *parser);
fzl_node_t *pa_cond(fzl_parser_t *parser);
fzl_node_t *pa_and(fzl_parser_t *parser);
fzl_node_t *pa_or(fzl_parser_t *parser);
fzl_node_t *pa_new(fzl_parser_t *parser);
fzl_node_t *pa_assignment(fzl_parser_t *parser);
fzl_node_t *pa_expression(fzl_parser_t *parser);
fzl_node_t *pa_class(fzl_parser_t *parser);
fzl_node_t *pa_function(fzl_parser_t *parser);
fzl_node_t *pa_return(fzl_parser_t *parser);
fzl_node_t *pa_if(fzl_parser_t *parser);
fzl_node_t *pa_block(fzl_parser_t *parser);
fzl_node_t *pa_for(fzl_parser_t *parser);
fzl_node_t *pa_while(fzl_parser_t *parser);
fzl_node_t *pa_loop(fzl_parser_t *parser);
fzl_node_t *pa_break(fzl_parser_t *parser);
fzl_node_t *pa_statement(fzl_parser_t *parser);
fzl_node_t *pa_program(fzl_parser_t *parser);
#endif // INTERNALS_PARSER_H
+19
View File
@@ -0,0 +1,19 @@
#ifndef INTERNALS_READER_H
#define INTERNALS_READER_H
#include <compiler/fzl_reader.h>
#include <stddef.h>
extern char last_err[256];
size_t move(fzl_reader_t *reader, size_t len);
void rd_comment(fzl_reader_t *reader);
fzl_token_t *rd_name(fzl_reader_t *reader);
fzl_token_t *rd_string_literal(fzl_reader_t *reader);
fzl_token_t *rd_number(fzl_reader_t *reader);
fzl_token_t *rd_single(fzl_reader_t *reader, fzl_toktype_t type);
fzl_token_t *rd_double(fzl_reader_t *reader, fzl_toktype_t type);
fzl_token_t *rd_fmtstr(fzl_reader_t *reader);
#endif // INTERNALS_READER_H
+8
View File
@@ -23,6 +23,9 @@ typedef struct
fzl_node_t *node; fzl_node_t *node;
size_t ip; size_t ip;
bool slot_set;
size_t slot;
char *classname; char *classname;
} fzl_sym_t; } fzl_sym_t;
@@ -32,6 +35,8 @@ typedef struct
size_t size; size_t size;
size_t remainder; size_t remainder;
size_t free_slot;
fzl_sym_t *symbols; fzl_sym_t *symbols;
} fzl_symtable_t; } fzl_symtable_t;
@@ -46,6 +51,9 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name, fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
char *classname); char *classname);
// Assign a local slot to a variable
int fzl_symt_setslot(fzl_symtable_t *symt, char *name);
// Get entry from specified symtable // Get entry from specified symtable
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res); bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res);
+9 -1
View File
@@ -3,9 +3,11 @@
#include <common/fzl_value.h> #include <common/fzl_value.h>
#include <compiler/utils/fzl_const_pool.h> // NOTE: move outside compiler scope to common #include <compiler/utils/fzl_const_pool.h> // NOTE: move outside compiler scope to common
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <vm/fzl_vm_env.h> #include <vm/fzl_vm_env.h>
#include <vm/fzl_vm_extern.h>
#include <vm/fzl_vm_object.h> #include <vm/fzl_vm_object.h>
// VM descriptor // VM descriptor
@@ -14,11 +16,17 @@ typedef struct
fzl_const_t *consts; fzl_const_t *consts;
size_t consts_count; size_t consts_count;
fzl_vm_extern_t *externs;
size_t externs_count;
uint64_t *ops; uint64_t *ops;
size_t ops_count; size_t ops_count;
size_t ip; size_t ip;
bool allow_infinite;
size_t max_iter;
// Data stack // Data stack
fzl_value_t dstack[256]; fzl_value_t dstack[256];
size_t dtop; size_t dtop;
@@ -36,7 +44,7 @@ typedef struct
fzl_vm_t *fzl_mkvm(const char *filepath); fzl_vm_t *fzl_mkvm(const char *filepath);
// Load and run code // Load and run code
int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter); int fzl_vm_run(fzl_vm_t *vm);
// Get last VM error message // Get last VM error message
char *fzl_vm_geterror(); char *fzl_vm_geterror();
+14
View File
@@ -0,0 +1,14 @@
#ifndef FZL_VM_EXTERN_H
#define FZL_VM_EXTERN_H
#include <ffi.h>
// Loaded extern function definition
typedef struct
{
void *func_ptr;
ffi_cif cif;
ffi_type **arg_types;
} fzl_vm_extern_t;
#endif // FZL_VM_EXTERN_H
+112
View File
@@ -0,0 +1,112 @@
#ifndef INTERNALS_OPCODE_H
#define INTERNALS_OPCODE_H
#include "vm/fzl_vm.h"
#include <stddef.h>
// Opcode handler descriptor
typedef int (*opcode_handler)(fzl_vm_t *);
extern char last_err[256];
extern size_t last_jmp_ip;
extern size_t total_jmp_on_same;
void print_object(fzl_vm_object_t *obj);
void print_err_data(fzl_vm_t *vm);
void escape_recursive(fzl_vm_env_t *current, fzl_vm_env_t *parent,
fzl_vm_object_t *obj);
// --- stack ---
extern const opcode_handler group_stack[];
int op_loadconst(fzl_vm_t *vm);
int op_call_builtin(fzl_vm_t *vm);
int op_pushnum(fzl_vm_t *vm);
int op_pushbool(fzl_vm_t *vm);
// --- context ---
extern const opcode_handler group_ctx[];
int op_mk_envtable(fzl_vm_t *vm);
int op_rm_envtable(fzl_vm_t *vm);
int op_store(fzl_vm_t *vm);
int op_lookup_env(fzl_vm_t *vm);
int op_write(fzl_vm_t *vm);
int op_load_local(fzl_vm_t *vm);
int op_store_local(fzl_vm_t *vm);
// --- flow ---
extern const opcode_handler group_flow[];
int op_jmp(fzl_vm_t *vm);
int op_jz(fzl_vm_t *vm);
// --- functions ---
extern const opcode_handler group_fn[];
int op_call(fzl_vm_t *vm);
int op_ret(fzl_vm_t *vm);
int op_call_extern(fzl_vm_t *vm);
// --- objects ---
extern const opcode_handler group_obj[];
int op_alloc_object(fzl_vm_t *vm);
int op_get_field(fzl_vm_t *vm);
int op_set_field(fzl_vm_t *vm);
int op_escape_object(fzl_vm_t *vm);
int op_dereference(fzl_vm_t *vm);
// --- math ---
extern const opcode_handler group_math[];
int op_add(fzl_vm_t *vm);
int op_sub(fzl_vm_t *vm);
int op_mul(fzl_vm_t *vm);
int op_div(fzl_vm_t *vm);
int op_pow(fzl_vm_t *vm);
int op_mod(fzl_vm_t *vm);
int op_neg(fzl_vm_t *vm);
// --- strings ---
extern const opcode_handler group_str[];
int op_build_str(fzl_vm_t *vm);
// --- conditions ---
extern const opcode_handler group_cmp[];
int op_cmp_eq(fzl_vm_t *vm);
int op_cmp_neq(fzl_vm_t *vm);
int op_cmp_lt(fzl_vm_t *vm);
int op_cmp_lteq(fzl_vm_t *vm);
int op_cmp_gt(fzl_vm_t *vm);
int op_cmp_gteq(fzl_vm_t *vm);
extern const opcode_handler group_strcmp[];
int op_cmp_eq_str(fzl_vm_t *vm);
int op_cmp_neq_str(fzl_vm_t *vm);
extern const opcode_handler group_objcmp[];
int op_cmp_eq_obj(fzl_vm_t *vm);
int op_cmp_neq_obj(fzl_vm_t *vm);
extern const opcode_handler group_and_or[];
int op_and(fzl_vm_t *vm);
int op_or(fzl_vm_t *vm);
extern const opcode_handler group_not[];
int op_not(fzl_vm_t *vm);
// ------------------------
extern const opcode_handler *const master_tbl[];
#endif // INTERNALS_OPCODE_H
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Run an FZL VM benchmark and report wall-clock timing statistics."""
import argparse
import statistics
import subprocess
import sys
import time
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--runs", type=int, default=7, help="measured runs")
parser.add_argument("--warmup", type=int, default=2, help="warm-up runs")
parser.add_argument("--max-iter", type=int, default=2_000_000)
parser.add_argument("--vm", required=True, help="path to the fz executable")
parser.add_argument("--program", required=True, help="compiled .fzb program")
args = parser.parse_args()
if args.runs < 1 or args.warmup < 0 or args.max_iter < 1:
parser.error("runs/max-iter must be positive and warmup must be non-negative")
command = [args.vm, "--max-iter", str(args.max_iter), args.program]
for _ in range(args.warmup):
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
sys.stderr.write(result.stdout)
sys.stderr.write(result.stderr)
raise SystemExit(result.returncode)
measurements = []
for _ in range(args.runs):
start = time.perf_counter()
result = subprocess.run(
command, capture_output=True, text=True
)
elapsed = time.perf_counter() - start
if result.returncode != 0:
sys.stderr.write(result.stdout)
sys.stderr.write(result.stderr)
raise SystemExit(result.returncode)
measurements.append(elapsed)
print(f"Runs: {args.runs} (warm-up: {args.warmup})")
print(f"Median: {statistics.median(measurements):.6f} s")
print(f"Mean: {statistics.mean(measurements):.6f} s")
print(f"Min: {min(measurements):.6f} s")
print(f"Max: {max(measurements):.6f} s")
if __name__ == "__main__":
main()
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <stdio.h> #include <stdio.h>
// Analyze assigns // Analyze assigns
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <stdio.h> #include <stdio.h>
// Analyze binary arithmetics // Analyze binary arithmetics
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
// Analyze break node // Analyze break node
fzl_an_res_t analyze_break(fzl_analyzer_t *analyzer, fzl_node_t *break_node) fzl_an_res_t analyze_break(fzl_analyzer_t *analyzer, fzl_node_t *break_node)
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdio.h> #include <stdio.h>
+5
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
@@ -14,6 +15,10 @@ fzl_an_res_t analyze_common(fzl_analyzer_t *analyzer, fzl_node_t *node)
fzl_an_res_t res = {.error = false, .type = FZL_TYPE_NULL, .classname = NULL}; fzl_an_res_t res = {.error = false, .type = FZL_TYPE_NULL, .classname = NULL};
switch (node->type) switch (node->type)
{ {
case FZL_NODETYPE_FMT_STRING:
res = analyze_strfmt(analyzer, node);
break;
case FZL_NODETYPE_ASSIGN: case FZL_NODETYPE_ASSIGN:
res = analyze_assign(analyzer, node); res = analyze_assign(analyzer, node);
break; break;
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <stdio.h> #include <stdio.h>
// Analyze binary conditional nodes // Analyze binary conditional nodes
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <stdio.h> #include <stdio.h>
// Analyze conditional statement // Analyze conditional statement
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -1,5 +1,6 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
+1
View File
@@ -2,6 +2,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <stdio.h> #include <stdio.h>
// Analyze return statement // Analyze return statement
+39
View File
@@ -0,0 +1,39 @@
#include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <stddef.h>
// Analyze format string
fzl_an_res_t analyze_strfmt(fzl_analyzer_t *analyzer, fzl_node_t *strfmt_node)
{
if (!analyzer)
return (fzl_an_res_t){.error = true};
if (!strfmt_node || strfmt_node->type != FZL_NODETYPE_FMT_STRING)
{
an_push_error(analyzer, NULL, "node is not format string");
return (fzl_an_res_t){.error = true};
}
for (size_t i = 0; i < strfmt_node->list.entry_count; i++)
{
fzl_node_t *node = strfmt_node->list.entries[i];
if (node->type != FZL_NODETYPE_LITERAL &&
node->type != FZL_NODETYPE_IDENTIFIER)
{
an_push_error(
analyzer, node->token,
"format string can only contain string literals and identifiers");
return (fzl_an_res_t){.error = true};
}
fzl_an_res_t node_res = analyze_common(analyzer, node);
if (node_res.error)
return (fzl_an_res_t){.error = true};
}
return (fzl_an_res_t){
.error = false, .type = FZL_TYPE_STRING, .classname = NULL};
}
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
// Analyze unary condition // Analyze unary condition
fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary) fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary)
+1
View File
@@ -1,6 +1,7 @@
#include <common/fzl_types.h> #include <common/fzl_types.h>
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/internals/analyzer.h>
#include <stdbool.h> #include <stdbool.h>
// Analyze unary arithmetic // Analyze unary arithmetic
+1
View File
@@ -1,4 +1,5 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
-229
View File
@@ -1,229 +0,0 @@
#include <common/fzl_opcodes.h>
#include <common/fzl_types.h>
#include <compiler/fzl_parser.h>
#include <compiler/fzl_walker.h>
#include <compiler/utils/fzl_const_pool.h>
#include <compiler/utils/fzl_symtable.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
ssize_t check_top;
fzl_nodetype_t check_tree[256];
// Ghost walk AST tree (w/o adding opcodes) to check if
// variable used in condition is changed
void ghost_walk_changed(fzl_node_t *node, bool *changed, fzl_sym_t *sym)
{
if (!node || !changed || *changed)
return;
check_tree[check_top++] = node->type;
switch (node->type)
{
case FZL_NODETYPE_BREAK:
case FZL_NODETYPE_DECLARATION:
case FZL_NODETYPE_CONSTRUCT:
case FZL_NODETYPE_CLASS:
case FZL_NODETYPE_FUNCTION:
case FZL_NODETYPE_LITERAL:
break;
case FZL_NODETYPE_UNARY_PLUS:
case FZL_NODETYPE_UNARY_MINUS:
case FZL_NODETYPE_NOT:
ghost_walk_changed(node->unary_op.operand, changed, sym);
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:
case FZL_NODETYPE_OR:
case FZL_NODETYPE_AND:
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_ASSIGN:
ghost_walk_changed(node->binary_op.left, changed, sym);
ghost_walk_changed(node->binary_op.right, changed, sym);
break;
case FZL_NODETYPE_FIELD:
ghost_walk_changed(node->memb.parent, changed, sym);
ghost_walk_changed(node->memb.field, changed, sym);
break;
case FZL_NODETYPE_RETURN:
ghost_walk_changed(node->ret.value, changed, sym);
break;
case FZL_NODETYPE_CONDITIONAL:
ghost_walk_changed(node->cond.condition, changed, sym);
ghost_walk_changed(node->cond.true_block, changed, sym);
ghost_walk_changed(node->cond.else_block, changed, sym);
break;
case FZL_NODETYPE_LOOP:
ghost_walk_changed(node->loop.cond, changed, sym);
ghost_walk_changed(node->loop.step, changed, sym);
ghost_walk_changed(node->loop.body, changed, sym);
break;
case FZL_NODETYPE_BLOCK:
for (size_t i = 0; i < node->list.entry_count; i++)
ghost_walk_changed(node->list.entries[i], changed, sym);
break;
case FZL_NODETYPE_CALL:
ghost_walk_changed(node->call.callee, changed, sym);
for (size_t i = 0; i < node->call.args_count; i++)
ghost_walk_changed(node->call.args[i], changed, sym);
break;
case FZL_NODETYPE_IDENTIFIER:
if (strcmp(node->token->value, sym->name) == 0)
{
*changed = true;
}
if (*changed)
{
*changed = false;
for (ssize_t i = check_top - 1; i >= 0; i--)
{
fzl_nodetype_t cur_t = check_tree[i];
if (cur_t == FZL_NODETYPE_ADD || cur_t == FZL_NODETYPE_SUB ||
cur_t == FZL_NODETYPE_MUL || cur_t == FZL_NODETYPE_DIV ||
cur_t == FZL_NODETYPE_POW || cur_t == FZL_NODETYPE_MOD ||
cur_t == FZL_NODETYPE_UNARY_MINUS)
{
*changed = true;
break;
}
}
}
break;
}
check_tree[--check_top] = 0;
}
// Whether value changed in loop body
bool value_is_changed(fzl_node_t *node, fzl_sym_t *sym)
{
if (!node || !sym)
return false;
bool changed = false;
check_top = 0;
memset(check_tree, 0, sizeof(check_tree));
ghost_walk_changed(node, &changed, sym);
return changed;
}
// Try to find symbol in loop condition node
bool retrieve_cond_sym(fzl_walker_t *walker, fzl_node_t *node, fzl_sym_t *sym)
{
if (!walker || !node)
return false;
switch (node->type)
{
case FZL_NODETYPE_BREAK:
case FZL_NODETYPE_BLOCK:
case FZL_NODETYPE_CALL:
case FZL_NODETYPE_LITERAL:
case FZL_NODETYPE_CONDITIONAL:
case FZL_NODETYPE_FUNCTION:
case FZL_NODETYPE_RETURN:
case FZL_NODETYPE_CLASS:
case FZL_NODETYPE_FIELD:
case FZL_NODETYPE_CONSTRUCT:
case FZL_NODETYPE_LOOP:
case FZL_NODETYPE_ASSIGN:
case FZL_NODETYPE_DECLARATION:
return false;
case FZL_NODETYPE_UNARY_MINUS:
case FZL_NODETYPE_UNARY_PLUS:
case FZL_NODETYPE_NOT:
return retrieve_cond_sym(walker, node->unary_op.operand, sym);
case FZL_NODETYPE_ADD:
case FZL_NODETYPE_SUB:
case FZL_NODETYPE_MUL:
case FZL_NODETYPE_DIV:
case FZL_NODETYPE_POW:
case FZL_NODETYPE_MOD:
case FZL_NODETYPE_AND:
case FZL_NODETYPE_OR:
case FZL_NODETYPE_LESS:
case FZL_NODETYPE_LESS_EQUAL:
case FZL_NODETYPE_GREATER:
case FZL_NODETYPE_GREATER_EQUAL:
case FZL_NODETYPE_EQUAL:
case FZL_NODETYPE_NEQUAL:
if (retrieve_cond_sym(walker, node->binary_op.left, sym))
return true;
if (retrieve_cond_sym(walker, node->binary_op.right, sym))
return true;
return false;
case FZL_NODETYPE_IDENTIFIER:
{
bool exists;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
{
if ((exists = fzl_symt_get(walker->syms[i], (char *)node->token->value,
sym)))
break;
}
return exists;
}
}
return false;
}
// Check for infinite loop
bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t start_ip,
size_t end_ip)
{
if (!walker)
return false;
end_ip = (end_ip >= walker->op_count) ? (walker->op_count - 1) : end_ip;
fzl_sym_t sym;
bool sym_exists = retrieve_cond_sym(walker, node->loop.cond, &sym);
if (sym_exists)
{
if (sym.type >= FZL_TYPE_INT8)
{
bool value_changed = value_is_changed(node, &sym);
if (value_changed)
return false;
}
}
// At least has jump outside
for (size_t i = start_ip; i <= end_ip; i++)
{
if (walker->ops[i] != FZL_OPCODE_JMP)
continue;
size_t jump_ip = walker->ops[++i];
if (jump_ip >= end_ip)
return false;
}
return true;
}
+1
View File
@@ -1,6 +1,7 @@
#include <compiler/fzl_analyzer.h> #include <compiler/fzl_analyzer.h>
#include <compiler/fzl_parser.h> #include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h> #include <compiler/fzl_token.h>
#include <compiler/internals/analyzer.h>
#include <compiler/utils/fzl_symtable.h> #include <compiler/utils/fzl_symtable.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
+22 -4
View File
@@ -27,10 +27,31 @@ void print_node(fzl_node_t *node, int level)
if (!node) if (!node)
return; return;
print_level(levelbuf, level);
strcpy(printbuf, levelbuf); strcpy(printbuf, levelbuf);
switch (node->type) switch (node->type)
{ {
case FZL_NODETYPE_UNARY_MINUS:
printf("%s Unary '-':\n", levelbuf);
print_node(node->unary_op.operand, level + 1);
break;
case FZL_NODETYPE_UNARY_PLUS:
printf("%s Unary '+':\n", levelbuf);
print_node(node->unary_op.operand, level + 1);
break;
case FZL_NODETYPE_BREAK:
printf("%s Break\n", levelbuf);
break;
case FZL_NODETYPE_FMT_STRING:
printf("%s Format string:\n", levelbuf);
for (size_t i = 0; i < node->list.entry_count; i++)
print_node(node->list.entries[i], level + 1);
break;
case FZL_NODETYPE_LOOP: case FZL_NODETYPE_LOOP:
printf("%s Loop:\n", levelbuf); printf("%s Loop:\n", levelbuf);
printf("-%s Init:\n", levelbuf); printf("-%s Init:\n", levelbuf);
@@ -172,9 +193,6 @@ void print_node(fzl_node_t *node, int level)
case FZL_NODETYPE_IDENTIFIER: case FZL_NODETYPE_IDENTIFIER:
printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value); printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value);
break; break;
default:
printf("%s Secure node\n", levelbuf);
} }
} }
@@ -185,7 +203,7 @@ void fzl_debug_print_ast(fzl_node_t *root)
fzl_node_t *curr = root; fzl_node_t *curr = root;
while (curr != NULL) while (curr != NULL)
{ {
print_node(curr, 3); print_node(curr, 2);
curr = curr->next; curr = curr->next;
} }
+19 -1247
View File
File diff suppressed because it is too large Load Diff
+49 -259
View File
@@ -1,6 +1,7 @@
#include "compiler/fzl_token.h"
#include "compiler/utils/fzl_linked.h"
#include <compiler/fzl_reader.h> #include <compiler/fzl_reader.h>
#include <compiler/fzl_token.h>
#include <compiler/internals/reader.h>
#include <compiler/utils/fzl_linked.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
@@ -8,7 +9,7 @@
#include <string.h> #include <string.h>
// Last error from reader // Last error from reader
static char last_err[256] = {0}; char last_err[256];
fzl_reader_t *fzl_mkreader(fzl_linked_t *linked) fzl_reader_t *fzl_mkreader(fzl_linked_t *linked)
{ {
@@ -24,308 +25,99 @@ fzl_reader_t *fzl_mkreader(fzl_linked_t *linked)
reader->size = linked->total_sz; reader->size = linked->total_sz;
reader->position = 0; reader->position = 0;
memset(last_err, 0, sizeof(last_err));
return reader; return reader;
} }
// --- Read zone ---
// Move cursor forward
// @deprecated
static size_t move(fzl_reader_t *reader, size_t len)
{
if (!reader)
return 0;
return reader->position += len;
}
// Try parse name
fzl_token_t *parse_name(fzl_reader_t *reader)
{
size_t lpos = reader->position;
size_t size = 0;
do
{
lpos++;
size++;
} while ((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 + 1);
memcpy(value, reader->source + reader->position, size);
value[size] = '\0';
size_t start = reader->position;
move(reader, size);
fzl_token_t *tok;
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, "class") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_CLASS, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "for") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_FOR, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "while") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_WHILE, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "break") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_BREAK, 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)
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, 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, l.line, l.col, value, size,
l.filename);
else if (strcmp(value, "null") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_NULL_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 ||
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, l.line, l.col, value, size, l.filename);
else
tok = fzl_mktoken(FZL_TOKTYPE_NAME, l.line, l.col, value, size, l.filename);
free(value);
return tok;
}
// Try parse string literal
fzl_token_t *parse_string_literal(fzl_reader_t *reader)
{
size_t lpos = reader->position;
size_t size = 0;
if (reader->source[lpos] != '\'' && reader->source[lpos] != '"')
return NULL;
char open_symbol = reader->source[lpos];
char close_symbol = '\0';
do
{
lpos++;
size++;
if (reader->source[lpos] == open_symbol)
close_symbol = open_symbol;
} 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), "'%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, 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, l.line, l.col, value,
size - 2, l.filename);
free(value);
move(reader, size);
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_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)
return NULL;
move(reader, size);
return tok;
}
// Add single-char token
fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type)
{
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;
}
move(reader, 1);
return fzl_mktoken(type, l.line, l.col, "", 0, l.filename);
}
// Add double-char token
fzl_token_t *add_double(fzl_reader_t *reader, fzl_toktype_t type)
{
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;
}
move(reader, 2);
return fzl_mktoken(type, l.line, l.col, "", 0, l.filename);
}
// 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) int fzl_read(fzl_reader_t *reader)
{ {
if (reader->position >= reader->size) if (reader->position >= reader->size)
return 0; return 0;
bool is_format_str = false;
do do
{ {
size_t p = reader->position; size_t p = reader->position;
fzl_token_t *token; fzl_token_t *token;
char curr = reader->source[p]; char curr = reader->source[p];
if (curr == '\n' || curr == ' ' || curr == '\r') if (is_format_str)
{
token = rd_fmtstr(reader);
if (token && token->type == FZL_TOKTYPE_STR_FORMAT_END)
is_format_str = false;
}
else if (curr == '\n' || curr == ' ' || curr == '\r')
{ {
move(reader, 1); move(reader, 1);
continue; continue;
} }
else if (curr == '\'' || curr == '"')
if (curr == '\'' || curr == '"') token = rd_string_literal(reader);
token = parse_string_literal(reader);
else if (curr == '/' && reader->source[p + 1] == '/') else if (curr == '/' && reader->source[p + 1] == '/')
{ {
skip_comment(reader); rd_comment(reader);
continue; continue;
} }
else if (curr == '=' && reader->source[p + 1] == '=') else if (curr == '=' && reader->source[p + 1] == '=')
token = add_double(reader, FZL_TOKTYPE_EQEQ); token = rd_double(reader, FZL_TOKTYPE_EQEQ);
else if (curr == '>' && reader->source[p + 1] == '=') else if (curr == '>' && reader->source[p + 1] == '=')
token = add_double(reader, FZL_TOKTYPE_REQ); token = rd_double(reader, FZL_TOKTYPE_REQ);
else if (curr == '<' && reader->source[p + 1] == '=') else if (curr == '<' && reader->source[p + 1] == '=')
token = add_double(reader, FZL_TOKTYPE_LEQ); token = rd_double(reader, FZL_TOKTYPE_LEQ);
else if (curr == '!' && reader->source[p + 1] == '=') else if (curr == '!' && reader->source[p + 1] == '=')
token = add_double(reader, FZL_TOKTYPE_NEQ); token = rd_double(reader, FZL_TOKTYPE_NEQ);
else if (curr == '|' && reader->source[p + 1] == '|') else if (curr == '|' && reader->source[p + 1] == '|')
token = add_double(reader, FZL_TOKTYPE_DVBAR); token = rd_double(reader, FZL_TOKTYPE_DVBAR);
else if (curr == '&' && reader->source[p + 1] == '&') else if (curr == '&' && reader->source[p + 1] == '&')
token = add_double(reader, FZL_TOKTYPE_DAMPERSAND); token = rd_double(reader, FZL_TOKTYPE_DAMPERSAND);
else if (curr == '>') else if (curr == '>')
token = add_single(reader, FZL_TOKTYPE_RANGLE); token = rd_single(reader, FZL_TOKTYPE_RANGLE);
else if (curr == '<') else if (curr == '<')
token = add_single(reader, FZL_TOKTYPE_LANGLE); token = rd_single(reader, FZL_TOKTYPE_LANGLE);
else if (curr == '!') else if (curr == '!')
token = add_single(reader, FZL_TOKTYPE_NOT); token = rd_single(reader, FZL_TOKTYPE_NOT);
else if (curr == ';') else if (curr == ';')
token = add_single(reader, FZL_TOKTYPE_SEMICOLON); token = rd_single(reader, FZL_TOKTYPE_SEMICOLON);
else if (curr == '{') else if (curr == '{')
token = add_single(reader, FZL_TOKTYPE_LBRACE); token = rd_single(reader, FZL_TOKTYPE_LBRACE);
else if (curr == '}') else if (curr == '}')
token = add_single(reader, FZL_TOKTYPE_RBRACE); token = rd_single(reader, FZL_TOKTYPE_RBRACE);
else if (curr == '(') else if (curr == '(')
token = add_single(reader, FZL_TOKTYPE_LPAREN); token = rd_single(reader, FZL_TOKTYPE_LPAREN);
else if (curr == ')') else if (curr == ')')
token = add_single(reader, FZL_TOKTYPE_RPAREN); token = rd_single(reader, FZL_TOKTYPE_RPAREN);
else if (curr == ',') else if (curr == ',')
token = add_single(reader, FZL_TOKTYPE_COMMA); token = rd_single(reader, FZL_TOKTYPE_COMMA);
else if (curr == '.') else if (curr == '.')
token = add_single(reader, FZL_TOKTYPE_DOT); token = rd_single(reader, FZL_TOKTYPE_DOT);
else if (curr == '*') else if (curr == '*')
token = add_single(reader, FZL_TOKTYPE_STAR); token = rd_single(reader, FZL_TOKTYPE_STAR);
else if (curr == '/') else if (curr == '/')
token = add_single(reader, FZL_TOKTYPE_SLASH); token = rd_single(reader, FZL_TOKTYPE_SLASH);
else if (curr == '+') else if (curr == '+')
token = add_single(reader, FZL_TOKTYPE_PLUS); token = rd_single(reader, FZL_TOKTYPE_PLUS);
else if (curr == '-') else if (curr == '-')
token = add_single(reader, FZL_TOKTYPE_MINUS); token = rd_single(reader, FZL_TOKTYPE_MINUS);
else if (curr == '^') else if (curr == '^')
token = add_single(reader, FZL_TOKTYPE_CARET); token = rd_single(reader, FZL_TOKTYPE_CARET);
else if (curr == '%') else if (curr == '%')
token = add_single(reader, FZL_TOKTYPE_PERCENT); token = rd_single(reader, FZL_TOKTYPE_PERCENT);
else if (curr == '=') else if (curr == '=')
token = add_single(reader, FZL_TOKTYPE_EQ); token = rd_single(reader, FZL_TOKTYPE_EQ);
else if (curr == '$' && reader->source[p + 1] == '"')
{
move(reader, 2);
is_format_str = true;
token = fzl_mktoken(FZL_TOKTYPE_STR_FORMAT_START, 0, 0, NULL, 0, "");
}
else if ((curr >= 'A' && curr <= 'Z') || (curr >= 'a' && curr <= 'z') || else if ((curr >= 'A' && curr <= 'Z') || (curr >= 'a' && curr <= 'z') ||
curr == '_') curr == '_')
token = parse_name(reader); token = rd_name(reader);
else if (curr >= '0' && curr <= '9') else if (curr >= '0' && curr <= '9')
token = parse_number(reader); token = rd_number(reader);
else else
{ {
fzl_file_loc l; fzl_file_loc l;
@@ -371,8 +163,6 @@ int fzl_read(fzl_reader_t *reader)
return 0; return 0;
} }
// --- Read zone end ---
char *fzl_reader_geterror() { return last_err; } char *fzl_reader_geterror() { return last_err; }
void free_node_and_next(fzl_reader_ll_t *node) void free_node_and_next(fzl_reader_ll_t *node)
+4
View File
@@ -30,7 +30,11 @@ fzl_token_t *fzl_mktoken(const fzl_toktype_t type, size_t line, size_t col,
// Free allocated token // Free allocated token
void fzl_freetoken(fzl_token_t *tok) void fzl_freetoken(fzl_token_t *tok)
{ {
if (!tok)
return;
if (tok->store_value) if (tok->store_value)
free(tok->value); free(tok->value);
free(tok); free(tok);
} }
+49 -18
View File
@@ -203,6 +203,20 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
{ {
switch (node->type) switch (node->type)
{ {
case FZL_NODETYPE_FMT_STRING:
{
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;
}
push_op(walker, FZL_OPCODE_BUILD_STR);
push_word(walker, node->list.entry_count);
break;
}
case FZL_NODETYPE_BREAK: case FZL_NODETYPE_BREAK:
{ {
fzl_walk_ctx_t *ctx = walker->ctx; fzl_walk_ctx_t *ctx = walker->ctx;
@@ -841,14 +855,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
case FZL_NODETYPE_IDENTIFIER: 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});
fzl_sym_t sym; fzl_sym_t sym;
bool exists = false; bool exists = false;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--) ssize_t i = walker->sym_top - 1;
for (; i >= 0; i--)
if ((exists = if ((exists =
fzl_symt_get(walker->syms[i], node->token->value, &sym))) fzl_symt_get(walker->syms[i], node->token->value, &sym)))
break; break;
@@ -862,9 +872,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
} }
push_op(walker, FZL_OPCODE_LOADCONST); if (!sym.slot_set)
push_word(walker, n); {
push_op(walker, FZL_OPCODE_LOOKUP_ENV); snprintf(wlast_err, sizeof(wlast_err),
"internal error: local '%s' has no slot", sym.name);
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
size_t depth = walker->sym_top - 1 - i;
push_op(walker, FZL_OPCODE_LOAD_LOCAL);
push_word(walker, depth);
push_word(walker, sym.slot);
current_res = (fzl_walk_res_t){ current_res = (fzl_walk_res_t){
.type = sym.type, .error = false, .classname = sym.classname}; .type = sym.type, .error = false, .classname = sym.classname};
@@ -1225,6 +1243,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
} }
if (fzl_symt_setslot(walker->syms[walker->sym_top - 1],
node->var.identifier->token->value) < 0)
{
snprintf(wlast_err, sizeof(wlast_err),
"failed to assign a local slot to '%s'",
(char *)node->var.identifier->token->value);
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
fzl_token_t *id_tok = node->var.identifier->token; fzl_token_t *id_tok = node->var.identifier->token;
size_t const_n = add_const( size_t const_n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
@@ -1269,7 +1296,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
{ {
fzl_sym_t sym; fzl_sym_t sym;
bool found = false; bool found = false;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--) ssize_t i = walker->sym_top - 1;
for (; i >= 0; i--)
if ((found = if ((found =
fzl_symt_get(walker->syms[i], fzl_symt_get(walker->syms[i],
node->binary_op.left->token->value, &sym))) node->binary_op.left->token->value, &sym)))
@@ -1294,14 +1322,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
} }
size_t const_n = add_const( if (!sym.slot_set)
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, {
.as.string.chars = sym.name, snprintf(wlast_err, sizeof(wlast_err),
.as.string.length = "internal error: local '%s' has no slot", sym.name);
node->binary_op.left->token->value_sz}); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
push_op(walker, FZL_OPCODE_LOADCONST); }
push_word(walker, const_n);
push_op(walker, FZL_OPCODE_WRITE); size_t depth = walker->sym_top - 1 - i;
push_op(walker, FZL_OPCODE_STORE_LOCAL);
push_word(walker, depth);
push_word(walker, sym.slot);
} }
else if (node->binary_op.left->type == FZL_NODETYPE_FIELD) else if (node->binary_op.left->type == FZL_NODETYPE_FIELD)
{ {
+33
View File
@@ -0,0 +1,33 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Parse conditional &&
fzl_node_t *pa_and(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_cond(parser);
fzl_node_t *right = NULL;
if (!left)
return NULL;
while (is(parser, FZL_TOKTYPE_DAMPERSAND))
{
fzl_token_t * or = consume(parser);
right = pa_cond(parser);
if (!right)
{
fzl_freeast(left);
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;
}
+42
View File
@@ -0,0 +1,42 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Assignments parsing
fzl_node_t *pa_assignment(fzl_parser_t *parser)
{
fzl_node_t *left = pa_new(parser);
if (!left)
return NULL;
if (is(parser, FZL_TOKTYPE_EQ))
{
fzl_token_t *asn = consume(parser);
if (!left || (left->type != FZL_NODETYPE_IDENTIFIER &&
left->type != FZL_NODETYPE_DECLARATION &&
left->type != FZL_NODETYPE_FIELD))
{
fzl_freeast(left);
snprintf(plast_err, sizeof(plast_err),
"'%s': cannot assign to '%s' at %ld:%ld",
last_known_tok->filename, (char *)left->token->value,
last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_node_t *right = pa_assignment(parser);
if (!right)
{
fzl_freeast(left);
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;
}
+52
View File
@@ -0,0 +1,52 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
#include <stdlib.h>
// Block statement parsing
fzl_node_t *pa_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 = pa_statement(parser);
if (!node)
{
free(stmts);
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)
{
for (size_t i = 0; i < count; i++)
fzl_freeast(stmts[i]);
free(stmts);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing '}' after block 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_BLOCK, block_tok);
node->list.entries = stmts;
node->list.entry_count = count;
return node;
}
+17
View File
@@ -0,0 +1,17 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// `break` keyword parsing
fzl_node_t *pa_break(fzl_parser_t *parser)
{
fzl_token_t *tok = consume(parser);
if (!tok)
{
snprintf(plast_err, sizeof(plast_err), "'%s': EOF",
last_known_tok->filename);
return NULL;
}
return fzl_mknode(FZL_NODETYPE_BREAK, tok);
}
+148
View File
@@ -0,0 +1,148 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Parse class definition
fzl_node_t *pa_class(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_token_t *class = consume(parser);
fzl_token_t *name = consume(parser);
if (!name || name->type != FZL_TOKTYPE_NAME)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected class name at %ld:%ld", last_known_tok->filename,
last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_token_t *lbrace = consume(parser);
if (!lbrace || lbrace->type != FZL_TOKTYPE_LBRACE)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected '{' after class name at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->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 = pa_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");
fzl_freeast(node);
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");
fzl_freeast(node);
free(fields);
return NULL;
}
}
fzl_node_t **new_args = realloc(node->fn.args, (++node->fn.args_count) *
sizeof(fzl_node_t *));
if (!new_args)
{
snprintf(plast_err, sizeof(plast_err),
"failed to reallocate method args list");
free(fields);
fzl_freeast(node);
return NULL;
}
if (node->fn.args_count > 1)
memmove(&new_args[1], new_args,
(node->fn.args_count - 1) * sizeof(fzl_node_t *));
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;
node->fn.args = new_args;
methods[mcount++] = node;
}
else
{
fzl_freeast(node);
snprintf(plast_err, sizeof(plast_err),
"'%s': unknown type of class member at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->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;
}
+59
View File
@@ -0,0 +1,59 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Parse conditional operations
fzl_node_t *pa_cond(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_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 = pa_term(parser);
if (!right)
{
fzl_freeast(left);
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;
}
+8
View File
@@ -0,0 +1,8 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Expression parsing
fzl_node_t *pa_expression(fzl_parser_t *parser)
{
return pa_assignment(parser);
}
+41
View File
@@ -0,0 +1,41 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Parse FACTOR: multiplication and division
fzl_node_t *pa_factor(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_power(parser);
fzl_node_t *right = NULL;
if (!left)
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 = pa_factor(parser);
if (!right)
{
fzl_freeast(left);
if (!plast_err[0])
snprintf(plast_err, sizeof(plast_err),
"'%s': expected expression at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
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;
}
+121
View File
@@ -0,0 +1,121 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// For-loop parsing
fzl_node_t *pa_for(fzl_parser_t *parser)
{
fzl_token_t *tok = consume(parser);
fzl_token_t *lpar = consume(parser);
if (!lpar || lpar->type != FZL_TOKTYPE_LPAREN)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected '(' after for keyword at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_node_t *start = NULL;
if (!is(parser, FZL_TOKTYPE_SEMICOLON))
{
start = pa_expression(parser);
if (!start)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected loop init or ';' at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
fzl_token_t *sc = consume(parser);
if (!sc || sc->type != FZL_TOKTYPE_SEMICOLON)
{
fzl_freeast(start);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing ';' after init section at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
}
else
consume(parser);
fzl_node_t *condition = NULL;
if (!is(parser, FZL_TOKTYPE_SEMICOLON))
{
condition = pa_expression(parser);
if (!condition)
{
fzl_freeast(start);
snprintf(plast_err, sizeof(plast_err),
"'%s': expected loop condition or ';' at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
fzl_token_t *sc = consume(parser);
if (!sc || sc->type != FZL_TOKTYPE_SEMICOLON)
{
fzl_freeast(start);
fzl_freeast(condition);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing ';' after condition at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
}
else
consume(parser);
fzl_node_t *step = NULL;
if (!is(parser, FZL_TOKTYPE_RPAREN))
{
step = pa_expression(parser);
if (!step)
{
fzl_freeast(start);
fzl_freeast(condition);
snprintf(plast_err, sizeof(plast_err),
"'%s': expected loop step end or ')' at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
}
fzl_token_t *rpar = consume(parser);
if (!rpar || rpar->type != FZL_TOKTYPE_RPAREN)
{
fzl_freeast(start);
fzl_freeast(condition);
fzl_freeast(step);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing ')' after loop setup at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_node_t *body = pa_statement(parser);
if (!body)
{
fzl_freeast(start);
fzl_freeast(condition);
fzl_freeast(step);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing body of for loop 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_LOOP, tok);
node->loop.init = start;
node->loop.cond = condition;
node->loop.step = step;
node->loop.body = body;
return node;
}
+101
View File
@@ -0,0 +1,101 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
#include <stdlib.h>
// Functions parsing
fzl_node_t *pa_function(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_token_t *fn = consume(parser);
if (!fn || fn->type != FZL_TOKTYPE_FUNCTION)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected keyword 'fn' at %ld:%ld", last_known_tok->filename,
last_known_tok->ln, last_known_tok->cn);
return NULL;
}
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 = pa_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)
{
free(args);
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 = NULL;
if (is(parser, FZL_TOKTYPE_TYPE) || is(parser, FZL_TOKTYPE_NAME))
type = consume(parser);
if (!type)
type = &null_tok;
if (!is(parser, FZL_TOKTYPE_LBRACE))
{
free(args);
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 = pa_statement(parser);
if (!body)
{
free(args);
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;
}
+61
View File
@@ -0,0 +1,61 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Parse if statement
fzl_node_t *pa_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 = pa_expression(parser);
if (!condition)
return NULL;
fzl_token_t *rparen = consume(parser);
if (rparen->type != FZL_TOKTYPE_RPAREN)
{
fzl_freeast(condition);
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 = pa_statement(parser);
if (!true_block)
{
fzl_freeast(condition);
return NULL;
}
if (is(parser, FZL_TOKTYPE_ELSE))
{
consume(parser); // else
else_block = pa_statement(parser);
if (!else_block)
{
fzl_freeast(condition);
fzl_freeast(true_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;
}
+20
View File
@@ -0,0 +1,20 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// `while` and `for` loops
fzl_node_t *pa_loop(fzl_parser_t *parser)
{
if (!peek(parser))
return NULL;
if (is(parser, FZL_TOKTYPE_FOR))
return pa_for(parser);
else if (is(parser, FZL_TOKTYPE_WHILE))
return pa_while(parser);
snprintf(plast_err, sizeof(plast_err),
"'%s': unknown type of loop at %ld:%ld", last_known_tok->filename,
last_known_tok->ln, last_known_tok->cn);
return NULL;
}
+27
View File
@@ -0,0 +1,27 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Object construct parsing
fzl_node_t *pa_new(fzl_parser_t *parser)
{
if (!is(parser, FZL_TOKTYPE_NEW))
return pa_or(parser);
fzl_token_t *new = consume(parser);
fzl_node_t *objname = pa_or(parser);
if (!objname || (objname->type != FZL_NODETYPE_IDENTIFIER &&
objname->type != FZL_NODETYPE_FIELD))
{
fzl_freeast(objname);
snprintf(plast_err, sizeof(plast_err),
"'%s': expected 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;
}
+33
View File
@@ -0,0 +1,33 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Parse OR (||)
fzl_node_t *pa_or(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_and(parser);
fzl_node_t *right = NULL;
if (!left)
return NULL;
while (is(parser, FZL_TOKTYPE_DVBAR))
{
fzl_token_t * or = consume(parser);
right = pa_and(parser);
if (!right)
{
fzl_freeast(left);
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;
}
+98
View File
@@ -0,0 +1,98 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
#include <stdlib.h>
// Parse postfix expressions (parens, index access, dots)
fzl_node_t *pa_postfix(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_primary(parser);
if (!left)
return NULL;
if (is(parser, FZL_TOKTYPE_LPAREN) || is(parser, FZL_TOKTYPE_DOT))
{
while (is(parser, FZL_TOKTYPE_LPAREN) || is(parser, FZL_TOKTYPE_DOT))
{
fzl_token_t *post = consume(parser);
if (post->type == FZL_TOKTYPE_LPAREN)
{
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 = pa_expression(parser);
if (!arg)
{
for (size_t i = 0; i < args_count; i++)
fzl_freeast(args[i]);
free(args);
fzl_freeast(left);
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)
{
for (size_t i = 0; i < args_count; i++)
fzl_freeast(args[i]);
free(args);
fzl_freeast(left);
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_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))
{
fzl_freeast(left);
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;
}
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);
left = node;
}
}
}
return left;
}
+41
View File
@@ -0,0 +1,41 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Parse power expression
fzl_node_t *pa_power(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_unary(parser);
fzl_node_t *right = NULL;
if (!left)
return NULL;
if (is(parser, FZL_TOKTYPE_CARET))
{
fzl_token_t *caret = consume(parser);
right = pa_unary(parser);
if (!right)
{
fzl_freeast(left);
if (!plast_err[0])
snprintf(plast_err, sizeof(plast_err),
"'%s': expected expression 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_POW, caret);
node->binary_op.left = left;
node->binary_op.right = right;
left = node;
}
return left;
}
+164
View File
@@ -0,0 +1,164 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
#include <stdlib.h>
// Parse primary
fzl_node_t *pa_primary(fzl_parser_t *parser)
{
if (!parser || !peek(parser))
return NULL;
if (is(parser, FZL_TOKTYPE_STR_LITERAL) ||
is(parser, FZL_TOKTYPE_NUM_LITERAL) ||
is(parser, FZL_TOKTYPE_BOOL_LITERAL) ||
is(parser, FZL_TOKTYPE_NULL_LITERAL))
{
fzl_token_t *tok = consume(parser);
return fzl_mknode(FZL_NODETYPE_LITERAL, tok);
}
else if (is(parser, FZL_TOKTYPE_STR_FORMAT_START))
{
consume(parser);
size_t remainder = 2;
size_t count = 0;
fzl_node_t **entries = malloc(remainder * sizeof(fzl_node_t *));
if (!entries)
{
snprintf(plast_err, sizeof(plast_err), "out of memory");
return NULL;
}
while (peek(parser) && peek(parser)->type != FZL_TOKTYPE_STR_FORMAT_END)
{
if (is(parser, FZL_TOKTYPE_STR_FORMAT_END))
break;
if (remainder <= count)
{
remainder *= 2;
fzl_node_t **newentries =
realloc(entries, remainder * sizeof(fzl_node_t *));
if (!newentries)
{
snprintf(plast_err, sizeof(plast_err), "out of memory");
for (size_t i = 0; i < count; i++)
fzl_freeast(entries[i]);
free(entries);
return NULL;
}
entries = newentries;
}
fzl_node_t *expr = pa_expression(parser);
if (expr == NULL)
{
for (size_t i = 0; i < count; i++)
fzl_freeast(entries[i]);
free(entries);
return NULL;
}
entries[count++] = expr;
}
fzl_token_t *end = consume(parser);
if (end->type != FZL_TOKTYPE_STR_FORMAT_END)
{
snprintf(plast_err, sizeof(plast_err),
"'%s':%zu:%zu => unterminated format string",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
for (size_t i = 0; i < count; i++)
fzl_freeast(entries[i]);
free(entries);
}
if (count < 1)
{
snprintf(plast_err, sizeof(plast_err),
"'%s':%zu:%zu => do you really need empty format string?",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
for (size_t i = 0; i < count; i++)
fzl_freeast(entries[i]);
free(entries);
return NULL;
}
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FMT_STRING, entries[0]->token);
if (!node)
{
snprintf(plast_err, sizeof(plast_err), "out of memory");
for (size_t i = 0; i < count; i++)
fzl_freeast(entries[i]);
free(entries);
return NULL;
}
node->list.entries = entries;
node->list.entry_count = count;
return node;
}
else if (is(parser, FZL_TOKTYPE_LPAREN))
{
consume(parser);
fzl_node_t *node = pa_expression(parser);
if (!is(parser, FZL_TOKTYPE_RPAREN))
{
snprintf(plast_err, sizeof(plast_err), "'%s': expected ')' at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
fzl_freeast(node);
return NULL;
}
consume(parser);
return node;
}
else if (is(parser, FZL_TOKTYPE_NAME))
{
fzl_token_t *name = consume(parser);
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))
{
fzl_token_t *type = consume(parser);
fzl_token_t *name = consume(parser);
if (!name || name->type != FZL_TOKTYPE_NAME)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': expected name after type at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
return NULL;
}
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': expected expression at %ld:%ld", last_known_tok->filename,
last_known_tok->ln, last_known_tok->cn);
return NULL;
}
+42
View File
@@ -0,0 +1,42 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Internal parsing entry point
fzl_node_t *pa_program(fzl_parser_t *parser)
{
if (!peek(parser))
return NULL;
fzl_node_t *first = NULL;
fzl_node_t *last = NULL;
fzl_node_t *node = NULL;
while (peek(parser) != NULL)
{
node = pa_statement(parser);
if (!node)
{
fzl_freeast(first);
return NULL;
}
if (!last)
{
first = node;
last = node;
}
else
{
last->next = node;
last = node;
}
}
if (!first)
{
snprintf(plast_err, sizeof(plast_err), "nothing to parse >:(");
return NULL;
}
return first;
}
+26
View File
@@ -0,0 +1,26 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Return parsing
fzl_node_t *pa_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 = pa_expression(parser);
if (!retval)
{
fzl_freeast(node);
return NULL;
}
node->ret.value = retval;
}
return node;
}
+48
View File
@@ -0,0 +1,48 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Statement parsing
fzl_node_t *pa_statement(fzl_parser_t *parser)
{
if (!peek(parser))
return NULL;
fzl_node_t *res = NULL;
if (is(parser, FZL_TOKTYPE_CLASS))
return pa_class(parser);
else if (is(parser, FZL_TOKTYPE_FUNCTION))
return pa_function(parser);
else if (is(parser, FZL_TOKTYPE_RETURN))
res = pa_return(parser);
else if (is(parser, FZL_TOKTYPE_IF))
return pa_if(parser);
else if (is(parser, FZL_TOKTYPE_LBRACE))
return pa_block(parser);
else if (is(parser, FZL_TOKTYPE_FOR) || is(parser, FZL_TOKTYPE_WHILE))
return pa_loop(parser);
else if (is(parser, FZL_TOKTYPE_BREAK))
res = pa_break(parser);
if (!res)
res = pa_expression(parser);
if (!res)
{
fzl_freeast(res);
return NULL;
}
if (!is(parser, FZL_TOKTYPE_SEMICOLON))
{
fzl_freeast(res);
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;
}
+40
View File
@@ -0,0 +1,40 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// Parse TERM: addition and subtraction
fzl_node_t *pa_term(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = pa_factor(parser);
fzl_node_t *right = NULL;
if (!left)
return NULL;
while (is(parser, FZL_TOKTYPE_PLUS) || is(parser, FZL_TOKTYPE_MINUS))
{
fzl_token_t *op = consume(parser);
right = pa_factor(parser);
if (!right)
{
fzl_freeast(left);
if (!plast_err[0])
snprintf(plast_err, sizeof(plast_err),
"'%s': expected expression at %ld:%ld",
last_known_tok->filename, last_known_tok->ln,
last_known_tok->cn);
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;
}
+24
View File
@@ -0,0 +1,24 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Parse unary operations
fzl_node_t *pa_unary(fzl_parser_t *parser)
{
if (!parser)
return NULL;
if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS) &&
!is(parser, FZL_TOKTYPE_NOT))
return pa_postfix(parser);
fzl_token_t *op = consume(parser);
fzl_node_t *operand = pa_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;
}
+52
View File
@@ -0,0 +1,52 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
#include <stdio.h>
// While-loop parsing
fzl_node_t *pa_while(fzl_parser_t *parser)
{
fzl_token_t *tok = consume(parser);
fzl_token_t *lpar = consume(parser);
if (!lpar || lpar->type != FZL_TOKTYPE_LPAREN)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': missing '(' after while keyword at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_node_t *condition = pa_expression(parser);
if (!condition)
{
snprintf(plast_err, sizeof(plast_err),
"'%s': missing condition for 'while' at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_token_t *rpar = consume(parser);
if (!rpar || rpar->type != FZL_TOKTYPE_RPAREN)
{
fzl_freeast(condition);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing ')' after while loop condition at %ld:%ld",
last_known_tok->filename, last_known_tok->ln, last_known_tok->cn);
return NULL;
}
fzl_node_t *body = pa_statement(parser);
if (!body)
{
fzl_freeast(condition);
snprintf(plast_err, sizeof(plast_err),
"'%s': missing while loop body 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_LOOP, tok);
node->loop.cond = condition;
node->loop.body = body;
return node;
}
+75
View File
@@ -0,0 +1,75 @@
#include <compiler/fzl_parser.h>
#include <compiler/internals/parser.h>
// Get token at cursor
fzl_token_t *peek(fzl_parser_t *parser)
{
if (!parser || !parser->cursor)
return NULL;
last_known_tok = parser->cursor->token;
return parser->cursor->token;
}
// Consume token at cursor and move to the next
fzl_token_t *consume(fzl_parser_t *parser)
{
if (!parser || !parser->cursor || !parser->cursor->token)
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
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
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;
}
}
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_NOT:
return FZL_NODETYPE_NOT;
default:
return 0;
}
}
+75
View File
@@ -0,0 +1,75 @@
#include <compiler/fzl_reader.h>
#include <compiler/internals/reader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Parse string format
fzl_token_t *rd_fmtstr(fzl_reader_t *reader)
{
size_t p = reader->position;
char *src = reader->source;
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 (src[p] == '{')
{
move(reader, 1);
fzl_token_t *id = rd_name(reader);
if (id == NULL || src[reader->position] != '}')
{
snprintf(last_err, sizeof(last_err),
"'%s':%zu:%zu => failed to parse format string", l.filename,
l.line, l.col);
fzl_freetoken(id);
return NULL;
}
move(reader, 1);
return id;
}
if (src[p] == '"')
{
move(reader, 1);
return fzl_mktoken(FZL_TOKTYPE_STR_FORMAT_END, l.line, l.col, NULL, 0,
l.filename);
}
size_t size = 0;
size_t lpos = p;
do
{
if (src[lpos + 1] == '"')
break;
size++;
lpos++;
} while (lpos < reader->size && reader->source[lpos] != '{' &&
reader->source[lpos] != '"');
ok = fzl_linked_getpos(reader->linked, reader->position, &l);
if (!ok)
{
snprintf(last_err, sizeof(last_err), "failed to locate line:col");
return NULL;
}
char *value = malloc(size);
memcpy(value, reader->source + reader->position, size);
fzl_token_t *tok = fzl_mktoken(FZL_TOKTYPE_STR_LITERAL, l.line, l.col, value,
size, l.filename);
free(value);
move(reader, size);
return tok;
}
+80
View File
@@ -0,0 +1,80 @@
#include <compiler/fzl_reader.h>
#include <compiler/internals/reader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Try parse name
fzl_token_t *rd_name(fzl_reader_t *reader)
{
size_t lpos = reader->position;
size_t size = 0;
do
{
lpos++;
size++;
} while ((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 + 1);
memcpy(value, reader->source + reader->position, size);
value[size] = '\0';
size_t start = reader->position;
move(reader, size);
fzl_token_t *tok;
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, "class") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_CLASS, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "for") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_FOR, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "while") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_WHILE, l.line, l.col, value, size, l.filename);
else if (strcmp(value, "break") == 0)
tok =
fzl_mktoken(FZL_TOKTYPE_BREAK, 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)
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, 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, l.line, l.col, value, size,
l.filename);
else if (strcmp(value, "null") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_NULL_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 ||
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, l.line, l.col, value, size, l.filename);
else
tok = fzl_mktoken(FZL_TOKTYPE_NAME, l.line, l.col, value, size, l.filename);
free(value);
return tok;
}
+45
View File
@@ -0,0 +1,45 @@
#include <compiler/fzl_reader.h>
#include <compiler/fzl_token.h>
#include <compiler/internals/reader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Parse numeric literal
fzl_token_t *rd_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_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)
return NULL;
move(reader, size);
return tok;
}
+62
View File
@@ -0,0 +1,62 @@
#include <compiler/fzl_reader.h>
#include <compiler/fzl_token.h>
#include <compiler/internals/reader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Try parse string literal
fzl_token_t *rd_string_literal(fzl_reader_t *reader)
{
size_t lpos = reader->position;
size_t size = 0;
if (reader->source[lpos] != '\'' && reader->source[lpos] != '"')
return NULL;
char open_symbol = reader->source[lpos];
char close_symbol = '\0';
do
{
lpos++;
size++;
if (reader->source[lpos] == open_symbol)
close_symbol = open_symbol;
} 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), "'%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, 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, l.line, l.col, value,
size - 2, l.filename);
free(value);
move(reader, size);
return tok;
}
+55
View File
@@ -0,0 +1,55 @@
#include <compiler/fzl_reader.h>
#include <compiler/internals/reader.h>
#include <stddef.h>
#include <stdio.h>
// Move cursor forward
size_t move(fzl_reader_t *reader, size_t len)
{
if (!reader)
return 0;
return reader->position += len;
}
// Add single-char token
fzl_token_t *rd_single(fzl_reader_t *reader, fzl_toktype_t type)
{
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;
}
move(reader, 1);
return fzl_mktoken(type, l.line, l.col, "", 0, l.filename);
}
// Add double-char token
fzl_token_t *rd_double(fzl_reader_t *reader, fzl_toktype_t type)
{
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;
}
move(reader, 2);
return fzl_mktoken(type, l.line, l.col, "", 0, l.filename);
}
// Skip symbols until '\n' or EOF
void rd_comment(fzl_reader_t *reader)
{
move(reader, 2);
while (reader->position < reader->size)
{
if (reader->source[reader->position++] == '\n')
break;
}
}
+30 -2
View File
@@ -13,6 +13,7 @@ fzl_symtable_t *fzl_mksymt()
if (!tbl) if (!tbl)
return NULL; return NULL;
tbl->free_slot = 0;
tbl->remainder = 2; tbl->remainder = 2;
tbl->size = 0; tbl->size = 0;
tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t)); tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t));
@@ -38,8 +39,13 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t)); symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t));
} }
symt->symbols[symt->size++] = (fzl_sym_t){ symt->symbols[symt->size++] = (fzl_sym_t){.name = name,
.name = name, .node = node, .type = type, .ip = ip, .classname = NULL}; .node = node,
.type = type,
.ip = ip,
.classname = NULL,
.slot_set = false,
.slot = 0};
return FZL_SYMTABLE_RESULT_OK; return FZL_SYMTABLE_RESULT_OK;
} }
@@ -64,6 +70,28 @@ fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
return FZL_SYMTABLE_RESULT_ERR; return FZL_SYMTABLE_RESULT_ERR;
} }
int fzl_symt_setslot(fzl_symtable_t *symt, char *name)
{
if (!symt)
return -1;
for (size_t i = 0; i < symt->size; i++)
{
if (strcmp(symt->symbols[i].name, name) == 0)
{
if (symt->symbols[i].slot_set)
return symt->symbols[i].slot;
symt->symbols[i].slot = symt->free_slot++;
symt->symbols[i].slot_set = true;
return symt->symbols[i].slot;
}
}
return -1;
}
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res) bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res)
{ {
if (!symt) if (!symt)
+6
View File
@@ -0,0 +1,6 @@
/// Converts integer [value] to string
extern fn itos(int value) str;
/// Converts string [value] to integer
extern fn stoi(str value) int;
+6
View File
@@ -0,0 +1,6 @@
/// Get the minimum of specified values
extern fn min([]int ...values) int;
/// Get the maximum of specified values
extern fn max([]int ...values) int;
+21 -800
View File
@@ -12,6 +12,7 @@
#include <vm/fzl_vm.h> #include <vm/fzl_vm.h>
#include <vm/fzl_vm_env.h> #include <vm/fzl_vm_env.h>
#include <vm/fzl_vm_object.h> #include <vm/fzl_vm_object.h>
#include <vm/internals/opcode.h>
char last_err[256]; char last_err[256];
size_t last_jmp_ip = 0; size_t last_jmp_ip = 0;
@@ -28,18 +29,7 @@ fzl_vm_t *fzl_mkvm(const char *filepath)
return NULL; return NULL;
} }
vm->consts = NULL; memset(vm, 0, sizeof(fzl_vm_t));
vm->consts_count = 0;
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));
memset(vm->cstack, 0, sizeof(vm->cstack));
memset(vm->estack, 0, sizeof(vm->estack));
FILE *fd = fopen(filepath, "rb"); FILE *fd = fopen(filepath, "rb");
if (!fd) if (!fd)
@@ -63,6 +53,7 @@ fzl_vm_t *fzl_mkvm(const char *filepath)
size_t consts_count, ops_count; size_t consts_count, ops_count;
fread(&consts_count, sizeof(size_t), 1, fd); fread(&consts_count, sizeof(size_t), 1, fd);
fread(&ops_count, sizeof(size_t), 1, fd); fread(&ops_count, sizeof(size_t), 1, fd);
// TODO: read externs count
if (ops_count < 1) if (ops_count < 1)
{ {
@@ -116,62 +107,13 @@ fzl_vm_t *fzl_mkvm(const char *filepath)
fread(&op, sizeof(uint64_t), 1, fd); fread(&op, sizeof(uint64_t), 1, fd);
vm->ops[i] = op; vm->ops[i] = op;
} }
// TODO: read externs
fclose(fd); fclose(fd);
return vm; return vm;
} }
// Escape objects with nested objects check
static void escape_recursive(fzl_vm_env_t *current, fzl_vm_env_t *parent,
fzl_vm_object_t *obj)
{
if (!current || !parent || !obj)
return;
bool found_in_current = false;
for (size_t i = 0; i < current->owned_objects_count; i++)
{
if (current->owned_objects[i] == obj)
{
current->owned_objects[i] = NULL;
found_in_current = true;
break;
}
}
if (found_in_current)
{
bool found_in_parent = false;
for (size_t i = 0; i < parent->owned_objects_count; i++)
{
if (parent->owned_objects[i] == obj)
{
found_in_parent = true;
break;
}
}
if (!found_in_parent)
{
if (parent->owned_objects_remainder <= parent->owned_objects_count)
{
parent->owned_objects_remainder *= 2;
parent->owned_objects =
realloc(parent->owned_objects, parent->owned_objects_remainder *
sizeof(fzl_vm_object_t *));
}
parent->owned_objects[parent->owned_objects_count++] = obj;
}
}
for (size_t i = 0; i < obj->values_count; i++)
{
if (obj->values[i].type == FZL_TYPE_OBJECT && obj->values[i].as.obj != NULL)
escape_recursive(current, parent, obj->values[i].as.obj);
}
}
void print_object(fzl_vm_object_t *obj) void print_object(fzl_vm_object_t *obj)
{ {
if (!obj) if (!obj)
@@ -277,7 +219,7 @@ void print_err_data(fzl_vm_t *vm)
printf("\t> [%zd] %lu\n", i, vm->cstack[i]); printf("\t> [%zd] %lu\n", i, vm->cstack[i]);
} }
int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter) int fzl_vm_run(fzl_vm_t *vm)
{ {
if (!vm) if (!vm)
{ {
@@ -285,749 +227,28 @@ int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter)
return -1; return -1;
} }
vm->ip = 0; vm->ip = 0;
int result = 0;
while (vm->ip < vm->ops_count) while (vm->ip < vm->ops_count)
{ {
fzl_opcode_t opcode = vm->ops[vm->ip]; fzl_opcode_t opcode = vm->ops[vm->ip];
size_t op_group = (opcode >> 16) & 0xFF;
size_t op_fnnum = (opcode & 0xFF);
switch (opcode) if (master_tbl[op_group] == NULL || master_tbl[op_group][op_fnnum] == NULL)
{ {
case FZL_OPCODE_DEREFERENCE: snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %u",
{ opcode);
if (vm->dtop < 1) print_err_data(vm);
{ return -1;
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t addr = vm->dstack[--vm->dtop];
if (addr.type < FZL_TYPE_INT8)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected address");
print_err_data(vm);
return -1;
}
if (addr.as.u == 0)
{
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = NULL};
break;
}
vm->dstack[vm->dtop++] = (fzl_value_t){
.type = FZL_TYPE_OBJECT, .as.obj = (fzl_vm_object_t *)addr.as.u};
break;
}
case FZL_OPCODE_CMP_EQ_STR:
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
int result = strcmp(left.as.s.value, right.as.s.value);
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result == 0};
break;
}
case FZL_OPCODE_CMP_NEQ_STR:
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
int result = strcmp(left.as.s.value, right.as.s.value);
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result != 0};
break;
}
case FZL_OPCODE_CMP_EQ_OBJ:
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] = (fzl_value_t){
.type = FZL_TYPE_BOOL, .as.b = left.as.obj == right.as.obj};
break;
}
case FZL_OPCODE_CMP_NEQ_OBJ:
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] = (fzl_value_t){
.type = FZL_TYPE_BOOL, .as.b = left.as.obj != right.as.obj};
break;
}
case FZL_OPCODE_ESCAPE_OBJECT:
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - missing value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t obj = vm->dstack[--vm->dtop];
if (obj.type != FZL_TYPE_OBJECT)
{
snprintf(last_err, sizeof(last_err),
"runtime error - cannot escape non-object type");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *parent = vm->estack[vm->etop - 2];
fzl_vm_env_t *current = vm->estack[vm->etop - 1];
if (vm->etop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - cannot escape object from global env");
print_err_data(vm);
return -1;
}
escape_recursive(current, parent, obj.as.obj);
vm->dstack[vm->dtop++] = obj;
break;
}
case FZL_OPCODE_ALLOC_OBJECT:
{
uint64_t values_count = vm->ops[++vm->ip];
fzl_vm_object_t *obj = malloc(sizeof(fzl_vm_object_t));
if (values_count > 0)
obj->values = malloc(values_count * sizeof(fzl_value_t));
obj->values_count = values_count;
memset(obj->values, 0, values_count * sizeof(fzl_value_t));
fzl_vm_env_t *current_env = vm->estack[vm->etop - 1];
if (current_env->owned_objects_remainder <=
current_env->owned_objects_count)
{
current_env->owned_objects_remainder *= 2;
current_env->owned_objects = realloc(
current_env->owned_objects,
current_env->owned_objects_remainder * sizeof(fzl_vm_object_t *));
}
current_env->owned_objects[current_env->owned_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");
print_err_data(vm);
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);
print_err_data(vm);
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);
print_err_data(vm);
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");
print_err_data(vm);
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);
print_err_data(vm);
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);
print_err_data(vm);
return -1;
}
obj->values[field_slot] = wrval;
break;
}
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");
print_err_data(vm);
return -1;
}
vm->ip = vm->cstack[--vm->ctop];
break;
}
case FZL_OPCODE_CMP_EQ:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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.u == req.as.u};
break;
case FZL_OPCODE_CMP_NEQ:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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;
case FZL_OPCODE_CMP_LT:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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;
case FZL_OPCODE_CMP_LTEQ:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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;
case FZL_OPCODE_CMP_GT:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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;
case FZL_OPCODE_CMP_GTEQ:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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;
case FZL_OPCODE_NOT:
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
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");
print_err_data(vm);
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:
{
if (vm->ops_count <= vm->ip)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected JMP addr");
return -1;
}
uint64_t jmp_ip = vm->ops[++vm->ip];
if (!allow_infinite)
{
if (jmp_ip == last_jmp_ip)
total_jmp_on_same++;
else
total_jmp_on_same = 0;
if (total_jmp_on_same > max_iter)
{
snprintf(
last_err, sizeof(last_err),
"runtime error - exceeded max loop jumps (infinite loop?)\n"
"If you thinks there's mistake, change jumps limit with "
"--max-iter=N");
return -1;
}
}
vm->ip = jmp_ip - 1;
last_jmp_ip = jmp_ip;
break;
}
case FZL_OPCODE_AND:
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
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");
print_err_data(vm);
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)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected var name in data stack");
print_err_data(vm);
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");
print_err_data(vm);
return -1;
}
fzl_value_t writevar = vm->dstack[--vm->dtop];
fzl_value_t value = vm->dstack[--vm->dtop];
char *wrname = (char *)writevar.as.s.value;
bool wrfound;
fzl_value_t wrenv;
ssize_t wrlvl = -1;
for (ssize_t i = vm->etop - 1; i >= 0; i--)
{
if ((wrfound = fzl_env_lookup(vm->estack[i], wrname, &wrenv)))
{
wrlvl = i;
break;
}
}
if (wrlvl <= -1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - name '%s' not found in this scope", wrname);
print_err_data(vm);
return -1;
}
fzl_vm_env_wresult_t wresult =
fzl_env_writevalue(vm->estack[wrlvl], (char *)wrname, 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");
print_err_data(vm);
return -1;
case FZL_VM_ENV_WRESULT_ERR_EXISTS:
snprintf(last_err, sizeof(last_err),
"runtime error - redefenition of '%s'",
writevar.as.s.value);
print_err_data(vm);
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);
print_err_data(vm);
return -1;
default:
print_err_data(vm);
return -1;
}
}
// vm->dstack[vm->dtop++] = value;
// NOTE: add POP opcode to prevent
// stack overflow!
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");
print_err_data(vm);
return -1;
}
fzl_value_t val = vm->dstack[--vm->dtop];
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;
}
case FZL_OPCODE_CALL_BUILTIN:
{
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_INT)
printf("%ld\n", arg.as.i);
else if (arg.type == FZL_TYPE_UINT)
printf("%lu\n", arg.as.u);
else if (arg.type == FZL_TYPE_BOOL)
printf("%s\n", arg.as.b ? "true" : "false");
else if (arg.type == FZL_TYPE_NULL)
printf("<null>\n");
else if (arg.type == FZL_TYPE_OBJECT)
{
print_object(arg.as.obj);
printf("\n");
}
}
else if (fn_id == FZL_BUILTIN_EXIT)
{
fzl_value_t arg = vm->dstack[--vm->dtop];
return arg.as.i;
}
else
{
snprintf(last_err, sizeof(last_err),
"runtime error - undefined builtin %lu", fn_id);
print_err_data(vm);
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");
print_err_data(vm);
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");
print_err_data(vm);
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");
print_err_data(vm);
return -1;
}
fzl_value_t sub2 = vm->dstack[--vm->dtop];
fzl_value_t sub1 = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = sub1.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");
print_err_data(vm);
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");
print_err_data(vm);
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");
print_err_data(vm);
return -1;
}
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;
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");
print_err_data(vm);
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");
print_err_data(vm);
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;
}
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;
}
default:
snprintf(last_err, sizeof(last_err),
"runtime error - unknown opcode %u", opcode);
print_err_data(vm);
return -1;
} }
opcode_handler handler = master_tbl[op_group][op_fnnum];
result = handler(vm);
if (result != 0)
return result;
vm->ip++; vm->ip++;
} }
+21 -4
View File
@@ -1,4 +1,4 @@
#include "common/fzl_opcodes.h" #include <common/fzl_opcodes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <vm/fzl_vm.h> #include <vm/fzl_vm.h>
@@ -20,6 +20,26 @@ void fzl_vm_print_ops(fzl_vm_t *vm)
switch (vm->ops[i]) switch (vm->ops[i])
{ {
case FZL_OPCODE_STORE_LOCAL:
{
size_t depth = vm->ops[++i];
size_t slot = vm->ops[++i];
printf("STORE_LOCAL D=%zu S=%zu\n", depth, slot);
break;
}
case FZL_OPCODE_LOAD_LOCAL:
{
size_t depth = vm->ops[++i];
size_t slot = vm->ops[++i];
printf("LOAD_LOCAL D=%zu S=%zu\n", depth, slot);
break;
}
case FZL_OPCODE_BUILD_STR:
printf("BUILD_STR %zu\n", vm->ops[++i]);
break;
case FZL_OPCODE_DEREFERENCE: case FZL_OPCODE_DEREFERENCE:
printf("DEREFERENCE\n"); printf("DEREFERENCE\n");
break; break;
@@ -163,9 +183,6 @@ void fzl_vm_print_ops(fzl_vm_t *vm)
case FZL_OPCODE_JMP: case FZL_OPCODE_JMP:
printf("JMP IP: %ld\n", vm->ops[++i]); printf("JMP IP: %ld\n", vm->ops[++i]);
break; break;
default:
printf("<UNKNOWN>\n");
} }
} }
} }
+36
View File
@@ -0,0 +1,36 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
int op_and(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
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};
return 0;
}
int op_or(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
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};
return 0;
}
+104
View File
@@ -0,0 +1,104 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
int op_cmp_eq(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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.u == req.as.u};
return 0;
}
int op_cmp_neq(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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};
return 0;
}
int op_cmp_lt(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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};
return 0;
}
int op_cmp_lteq(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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};
return 0;
}
int op_cmp_gt(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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};
return 0;
}
int op_cmp_gteq(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
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};
return 0;
}
+202
View File
@@ -0,0 +1,202 @@
#include "common/fzl_types.h"
#include <common/fzl_value.h>
#include <stdio.h>
#include <vm/fzl_vm.h>
#include <vm/fzl_vm_env.h>
#include <vm/internals/opcode.h>
// Allocate environment table and increment scope level
int op_mk_envtable(fzl_vm_t *vm)
{
vm->estack[vm->etop++] = fzl_mkenv();
return 0;
}
// Remove environment table and decrement scope level
int op_rm_envtable(fzl_vm_t *vm)
{
fzl_freeenv(vm->estack[--vm->etop]);
return 0;
}
// Store new variable
int op_store(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected var name in data stack");
print_err_data(vm);
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){});
return 0;
}
// Lookup environment(s) for variable using name on stack
int op_lookup_env(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - LOOKUP_ENV: empty data stack");
print_err_data(vm);
return -1;
}
fzl_value_t val = vm->dstack[--vm->dtop];
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;
return 0;
}
// Writable variable value
int op_write(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t writevar = vm->dstack[--vm->dtop];
fzl_value_t value = vm->dstack[--vm->dtop];
char *wrname = (char *)writevar.as.s.value;
bool wrfound;
fzl_value_t wrenv;
ssize_t wrlvl = -1;
for (ssize_t i = vm->etop - 1; i >= 0; i--)
{
if ((wrfound = fzl_env_lookup(vm->estack[i], wrname, &wrenv)))
{
wrlvl = i;
break;
}
}
if (wrlvl <= -1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - name '%s' not found in this scope", wrname);
print_err_data(vm);
return -1;
}
fzl_vm_env_wresult_t wresult =
fzl_env_writevalue(vm->estack[wrlvl], (char *)wrname, 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");
print_err_data(vm);
return -1;
case FZL_VM_ENV_WRESULT_ERR_EXISTS:
snprintf(last_err, sizeof(last_err),
"runtime error - redefenition of '%s'", writevar.as.s.value);
print_err_data(vm);
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);
print_err_data(vm);
return -1;
default:
print_err_data(vm);
return -1;
}
}
return 0;
}
int op_load_local(fzl_vm_t *vm)
{
size_t depth = vm->ops[++vm->ip];
size_t slot = vm->ops[++vm->ip];
if (depth >= vm->etop)
{
snprintf(last_err, sizeof(last_err),
"runtime error - local depth out of range");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
if (env->size <= slot)
{
snprintf(last_err, sizeof(last_err),
"runtime error - local slot out of range");
print_err_data(vm);
return -1;
}
vm->dstack[vm->dtop++] = env->table[slot].value;
return 0;
}
int op_store_local(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t value = vm->dstack[--vm->dtop];
size_t depth = vm->ops[++vm->ip];
size_t slot = vm->ops[++vm->ip];
if (depth >= vm->etop)
{
snprintf(last_err, sizeof(last_err),
"runtime error - local depth out of range");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
if (env->size <= slot)
{
snprintf(last_err, sizeof(last_err),
"runtime error - local slot out of range");
print_err_data(vm);
return -1;
}
if (value.type == FZL_TYPE_OBJECT && depth != 0)
{
for (size_t i = 0; i < depth; i++)
{
escape_recursive(vm->estack[vm->etop - i], vm->estack[vm->etop - 1 - i],
value.as.obj);
}
}
env->table[slot].value = value;
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// Jump at specified address
int op_jmp(fzl_vm_t *vm)
{
if (vm->ops_count <= vm->ip)
{
snprintf(last_err, sizeof(last_err), "runtime error - expected JMP addr");
return -1;
}
uint64_t jmp_ip = vm->ops[++vm->ip];
if (!vm->allow_infinite)
{
if (jmp_ip == last_jmp_ip)
total_jmp_on_same++;
else
total_jmp_on_same = 0;
if (total_jmp_on_same > vm->max_iter)
{
snprintf(last_err, sizeof(last_err),
"runtime error - exceeded max loop jumps (infinite loop?)\n"
"If you thinks there's mistake, change jumps limit with "
"--max-iter=N");
return -1;
}
}
vm->ip = jmp_ip - 1;
last_jmp_ip = jmp_ip;
return 0;
}
// Pop value from data stack and jump at specified address if value is zero
int op_jz(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
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;
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// Call function (store return address and jump into function init)
int op_call(fzl_vm_t *vm)
{
uint64_t ip = vm->ops[++vm->ip];
vm->cstack[vm->ctop++] = vm->ip;
vm->ip = ip - 1;
return 0;
}
// Pop return address from call stack and jump
int op_ret(fzl_vm_t *vm)
{
if (vm->ctop < 1)
{
snprintf(last_err, sizeof(last_err), "runtime error - no place to jump");
print_err_data(vm);
return -1;
}
vm->ip = vm->cstack[--vm->ctop];
return 0;
}
// Call externally defined function
int op_call_extern(fzl_vm_t *vm)
{
(void)vm;
// TODO: implement
return 0;
}
+126
View File
@@ -0,0 +1,126 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// Pop two numbers and push sum result
int op_add(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
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};
return 0;
}
// Pop two numbers and push sum result
int op_sub(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
return -1;
}
fzl_value_t sub2 = vm->dstack[--vm->dtop];
fzl_value_t sub1 = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = sub1.type, .as.u = sub1.as.u - sub2.as.u};
return 0;
}
// Pop two numbers and push sum result
int op_mul(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
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};
return 0;
}
// Pop two numbers and push sum result
int op_div(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
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};
return 0;
}
// Pop two numbers and push sum result
int op_pow(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
return -1;
}
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;
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};
return 0;
}
// Pop two numbers and push sum result
int op_mod(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected 2 values in stack");
print_err_data(vm);
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};
return 0;
}
// Pop two numbers and push sum result
int op_neg(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in stack");
print_err_data(vm);
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};
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
int op_not(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
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};
return 0;
}
+225
View File
@@ -0,0 +1,225 @@
#include <stdlib.h>
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// Escape objects with nested objects check
void escape_recursive(fzl_vm_env_t *current, fzl_vm_env_t *parent,
fzl_vm_object_t *obj)
{
if (!current || !parent || !obj)
return;
bool found_in_current = false;
for (size_t i = 0; i < current->owned_objects_count; i++)
{
if (current->owned_objects[i] == obj)
{
current->owned_objects[i] = NULL;
found_in_current = true;
break;
}
}
if (found_in_current)
{
bool found_in_parent = false;
for (size_t i = 0; i < parent->owned_objects_count; i++)
{
if (parent->owned_objects[i] == obj)
{
found_in_parent = true;
break;
}
}
if (!found_in_parent)
{
if (parent->owned_objects_remainder <= parent->owned_objects_count)
{
parent->owned_objects_remainder *= 2;
parent->owned_objects =
realloc(parent->owned_objects, parent->owned_objects_remainder *
sizeof(fzl_vm_object_t *));
}
parent->owned_objects[parent->owned_objects_count++] = obj;
}
}
for (size_t i = 0; i < obj->values_count; i++)
{
if (obj->values[i].type == FZL_TYPE_OBJECT && obj->values[i].as.obj != NULL)
escape_recursive(current, parent, obj->values[i].as.obj);
}
}
// Allocate specified count of fields and create object
int op_alloc_object(fzl_vm_t *vm)
{
uint64_t values_count = vm->ops[++vm->ip];
fzl_vm_object_t *obj = malloc(sizeof(fzl_vm_object_t));
if (values_count < 1)
values_count = 1;
obj->values = malloc(values_count * sizeof(fzl_value_t));
obj->values_count = values_count;
memset(obj->values, 0, values_count * sizeof(fzl_value_t));
fzl_vm_env_t *current_env = vm->estack[vm->etop - 1];
if (current_env->owned_objects_remainder <= current_env->owned_objects_count)
{
current_env->owned_objects_remainder *= 2;
current_env->owned_objects = realloc(current_env->owned_objects,
current_env->owned_objects_remainder *
sizeof(fzl_vm_object_t *));
}
current_env->owned_objects[current_env->owned_objects_count++] = obj;
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = obj};
return 0;
}
// Pop object from stack and push its field value
int op_get_field(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
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);
print_err_data(vm);
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);
print_err_data(vm);
return -1;
}
vm->dstack[vm->dtop++] = obj->values[field_slot];
return 0;
}
// Pop object and write value from stack and write value into objects' field
int op_set_field(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
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);
print_err_data(vm);
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);
print_err_data(vm);
return -1;
}
obj->values[field_slot] = wrval;
return 0;
}
// Escape object to the parent scope
int op_escape_object(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - missing value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t obj = vm->dstack[--vm->dtop];
if (obj.type != FZL_TYPE_OBJECT)
{
snprintf(last_err, sizeof(last_err),
"runtime error - cannot escape non-object type");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *parent = vm->estack[vm->etop - 2];
fzl_vm_env_t *current = vm->estack[vm->etop - 1];
if (vm->etop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - cannot escape object from global env");
print_err_data(vm);
return -1;
}
escape_recursive(current, parent, obj.as.obj);
vm->dstack[vm->dtop++] = obj;
return 0;
}
// Pop value from data stack and store dereferenced value
int op_dereference(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t addr = vm->dstack[--vm->dtop];
if (addr.type < FZL_TYPE_INT8)
{
snprintf(last_err, sizeof(last_err), "runtime error - expected address");
print_err_data(vm);
return -1;
}
if (addr.as.u == 0)
{
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = NULL};
}
else
{
vm->dstack[vm->dtop++] = (fzl_value_t){
.type = FZL_TYPE_OBJECT, .as.obj = (fzl_vm_object_t *)addr.as.u};
}
return 0;
}
+36
View File
@@ -0,0 +1,36 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
int op_cmp_eq_obj(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = left.as.obj == right.as.obj};
return 0;
}
int op_cmp_neq_obj(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = left.as.obj != right.as.obj};
return 0;
}
+78
View File
@@ -0,0 +1,78 @@
#include <common/fzl_builtins.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// Load constant onto stack
int op_loadconst(fzl_vm_t *vm)
{
uint64_t cn = vm->ops[++vm->ip];
if (vm->consts_count <= cn)
{
snprintf(last_err, sizeof(last_err), "runtime error - unknown const");
print_err_data(vm);
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};
return 0;
}
// Call builtin function
int op_call_builtin(fzl_vm_t *vm)
{
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_INT)
printf("%ld\n", arg.as.i);
else if (arg.type == FZL_TYPE_UINT)
printf("%lu\n", arg.as.u);
else if (arg.type == FZL_TYPE_BOOL)
printf("%s\n", arg.as.b ? "true" : "false");
else if (arg.type == FZL_TYPE_NULL)
printf("<null>\n");
else if (arg.type == FZL_TYPE_OBJECT)
{
print_object(arg.as.obj);
printf("\n");
}
}
else if (fn_id == FZL_BUILTIN_EXIT)
{
fzl_value_t arg = vm->dstack[--vm->dtop];
return arg.as.i;
}
else
{
snprintf(last_err, sizeof(last_err),
"runtime error - undefined builtin %lu", fn_id);
print_err_data(vm);
return -1;
}
return 0;
}
// Push number onto stack
int op_pushnum(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};
return 0;
}
// Push boolean onto stack
int op_pushbool(fzl_vm_t *vm)
{
uint64_t push_b = vm->ops[++vm->ip];
vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = push_b};
return 0;
}
+138
View File
@@ -0,0 +1,138 @@
#include <stdio.h>
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
char build_result[512];
// Build string from specified count using stack values
int op_build_str(fzl_vm_t *vm)
{
uint64_t n = vm->ops[++vm->ip];
if (vm->dtop < n)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected %zu value(s) in dstack", n);
print_err_data(vm);
return -1;
}
char temp[64];
size_t gi = 0;
memset(build_result, 0, sizeof(build_result));
for (size_t i = 0; i < n; i++)
{
if (gi >= 512)
{
for (size_t i = 511; i > 508; i--)
build_result[i] = '.';
break;
}
memset(temp, 0, sizeof(temp));
fzl_value_t val = vm->dstack[vm->dtop - n + i];
switch (val.type)
{
case FZL_TYPE_UINT:
snprintf(temp, sizeof(temp), "%zu", val.as.u);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_UINT64:
snprintf(temp, sizeof(temp), "%lu", val.as.u64);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_UINT32:
snprintf(temp, sizeof(temp), "%u", val.as.u32);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_UINT16:
snprintf(temp, sizeof(temp), "%u", val.as.u16);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_UINT8:
snprintf(temp, sizeof(temp), "%u", val.as.u8);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_INT:
snprintf(temp, sizeof(temp), "%zd", val.as.i);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_INT64:
snprintf(temp, sizeof(temp), "%ld", val.as.i64);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_INT32:
snprintf(temp, sizeof(temp), "%d", val.as.i32);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_INT16:
snprintf(temp, sizeof(temp), "%d", val.as.i16);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_INT8:
snprintf(temp, sizeof(temp), "%d", val.as.i8);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_BOOL:
snprintf(build_result + gi, sizeof(build_result) - gi, "%s",
val.as.b ? "true" : "false");
gi += val.as.b ? 4 : 5;
break;
case FZL_TYPE_CHAR:
snprintf(build_result + gi, sizeof(build_result) - gi, "%d", val.as.c);
gi += 1;
break;
case FZL_TYPE_STRING:
snprintf(build_result + gi, sizeof(build_result) - gi, "%s",
val.as.s.value);
gi += val.as.s.length;
break;
case FZL_TYPE_EXTERN_FN:
snprintf(temp, sizeof(temp), "<extern:%zu>", val.as.u);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_OBJECT:
snprintf(temp, sizeof(temp), "<ref:%p>", val.as.obj);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_NULL:
snprintf(build_result + gi, sizeof(build_result) - gi, "<null>");
gi += 6;
break;
}
}
vm->dtop -= n;
vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_STRING,
.as.s.length = strlen(build_result),
.as.s.value = build_result};
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
int op_cmp_eq_str(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
int result = strcmp(left.as.s.value, right.as.s.value);
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result == 0};
return 0;
}
int op_cmp_neq_str(fzl_vm_t *vm)
{
if (vm->dtop < 2)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected two values in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t left = vm->dstack[--vm->dtop];
fzl_value_t right = vm->dstack[--vm->dtop];
int result = strcmp(left.as.s.value, right.as.s.value);
vm->dstack[vm->dtop++] =
(fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result != 0};
return 0;
}
+81
View File
@@ -0,0 +1,81 @@
#include <vm/internals/opcode.h>
// Stack-relative opcodes group
const opcode_handler group_stack[] = {
[1] = op_loadconst,
[2] = op_call_builtin,
[3] = op_pushnum,
[4] = op_pushbool,
};
// Context-relative opcodes group
const opcode_handler group_ctx[] = {[1] = op_mk_envtable, [2] = op_rm_envtable,
[3] = op_store, [4] = op_lookup_env,
[5] = op_write, [6] = op_load_local,
[7] = op_store_local};
// Flow-relative opcodes group
const opcode_handler group_flow[] = {
[1] = op_jmp,
[2] = op_jz,
};
// Function-relative opcodes group
const opcode_handler group_fn[] = {
[1] = op_call,
[2] = op_ret,
};
// Object-relative opcodes group
const opcode_handler group_obj[] = {
[1] = op_alloc_object, [2] = op_get_field, [3] = op_set_field,
[4] = op_escape_object, [5] = op_dereference,
};
// Math-relative opcodes group
const opcode_handler group_math[] = {
[1] = op_add, [2] = op_sub, [3] = op_mul, [4] = op_div,
[5] = op_pow, [6] = op_mod, [7] = op_neg,
};
// String-relative opcodes group
const opcode_handler group_str[] = {
[1] = op_build_str,
};
// Basic comparison
const opcode_handler group_cmp[] = {
[1] = op_cmp_eq, [2] = op_cmp_neq, [3] = op_cmp_lt,
[4] = op_cmp_lteq, [5] = op_cmp_gt, [6] = op_cmp_gteq,
};
// Strings comparison
const opcode_handler group_strcmp[] = {
[1] = op_cmp_eq_str,
[2] = op_cmp_neq_str,
};
// Object comparison
const opcode_handler group_objcmp[] = {
[1] = op_cmp_eq_obj,
[2] = op_cmp_neq_obj,
};
// AND / OR opcodes
const opcode_handler group_and_or[] = {
[1] = op_and,
[2] = op_or,
};
// NOT opcode
const opcode_handler group_not[] = {
[1] = op_not,
};
// Master table
const opcode_handler *const master_tbl[] = {
[0xF0] = group_stack, [0xF1] = group_ctx, [0xF2] = group_flow,
[0xF3] = group_fn, [0xFA] = group_obj, [0xA0] = group_math,
[0xA1] = group_str, [0xB0] = group_cmp, [0xB1] = group_strcmp,
[0xB2] = group_objcmp, [0xC0] = group_and_or, [0xD0] = group_not,
};
+4 -1
View File
@@ -121,11 +121,14 @@ int main(int argc, char **argv)
return -1; return -1;
} }
vm->allow_infinite = args->allow_infinite;
vm->max_iter = args->max_iter;
if (args->debug) if (args->debug)
fzl_vm_print_ops(vm); fzl_vm_print_ops(vm);
else else
{ {
int result = fzl_vm_run(vm, args->allow_infinite, args->max_iter); int result = fzl_vm_run(vm);
if (result != 0) if (result != 0)
{ {
char *err = fzl_vm_geterror(); char *err = fzl_vm_geterror();
@@ -0,0 +1,2 @@
printline($"Something's on the {way}")
@@ -0,0 +1 @@
$"jopa {abc}
+6
View File
@@ -0,0 +1,6 @@
int a1 = 2 * 2;
printline($"First tests: build string and print. 2 * 2 = {a1}");
str a2 = "Mike Tyson";
printline($"Hello, {a2}");

Some files were not shown because too many files have changed in this diff Show More