├── .gitignore ├── LICENSE ├── Makefile ├── Makefile.win ├── README.md ├── run_tests ├── src ├── Makefile ├── functions.c ├── functions.h ├── lib │ ├── Makefile │ ├── ast.c │ ├── ast.h │ ├── buffer.c │ ├── bytecode.h │ ├── c_funcs.c │ ├── compiler.c │ ├── compiler.h │ ├── dump_ast.c │ ├── dump_bytecode.c │ ├── fh.h │ ├── fh_internal.h │ ├── gc.c │ ├── input.c │ ├── map.c │ ├── operator.c │ ├── parser.c │ ├── parser.h │ ├── parser_stacks.h │ ├── program.c │ ├── program.h │ ├── src_loc.c │ ├── stack.c │ ├── stack.h │ ├── symtab.c │ ├── tokenizer.c │ ├── tokenizer.h │ ├── util.c │ ├── value.c │ ├── value.h │ ├── vm.c │ └── vm.h └── main.c └── tests ├── 1.fh ├── 2.fh ├── args.fh ├── array.fh ├── bad_byte.fh ├── bench_call.fh ├── bench_mandel.fh ├── block.fh ├── closure.fh ├── compilation_error.fh ├── error.fh ├── error_include.fh ├── func.fh ├── gc.fh ├── half_func.fh ├── if.fh ├── include.fh ├── logic.fh ├── mandel_color.fh ├── mandel_lib.fh ├── mandel_text_color.fh ├── mandelbrot.fh ├── map.fh ├── opt.fh ├── parse_error.fh ├── prop.fh ├── run_error.fh ├── simple.fh ├── simple2.fh ├── test.fh ├── test2.fh ├── test_array.fh ├── test_closure.fh ├── test_equals.fh ├── test_mandel.fh ├── test_map.fh ├── test_prop.fh ├── tokenizer_error.fh ├── upvalue.fh ├── use_mandel_lib.fh └── while.fh /.gitignore: -------------------------------------------------------------------------------- 1 | fh 2 | core 3 | .*.swp 4 | .gdb_history 5 | 6 | # Emacs backups 7 | *~ 8 | 9 | # Prerequisites 10 | *.d 11 | 12 | # Object files 13 | *.o 14 | *.ko 15 | *.obj 16 | *.elf 17 | 18 | # Linker output 19 | *.ilk 20 | *.map 21 | *.exp 22 | 23 | # Precompiled Headers 24 | *.gch 25 | *.pch 26 | 27 | # Libraries 28 | *.lib 29 | *.a 30 | *.la 31 | *.lo 32 | 33 | # Shared objects (inc. Windows DLLs) 34 | *.dll 35 | *.so 36 | *.so.* 37 | *.dylib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | *.i*86 44 | *.x86_64 45 | *.hex 46 | 47 | # Debug files 48 | *.dSYM/ 49 | *.su 50 | *.idb 51 | *.pdb 52 | 53 | # Kernel Module Compile Results 54 | *.mod* 55 | *.cmd 56 | .tmp_versions/ 57 | modules.order 58 | Module.symvers 59 | Mkfile.old 60 | dkms.conf 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ricardo Massaro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | AR = ar rc 4 | RANLIB = ranlib 5 | CFLAGS = -Wall -Wextra -std=c99 -pedantic 6 | LDFLAGS = 7 | LIBS = -lm 8 | 9 | CHECK_SCRIPT = tests/test.fh 10 | 11 | TARGETS = debug release ubsan 12 | 13 | .PHONY: $(TARGETS) build clean check test dump_exported_symbols 14 | 15 | all: debug 16 | 17 | debug: 18 | $(MAKE) build TARGET_CFLAGS="-O1 -g" TARGET_LDFLAGS="" 19 | 20 | release: 21 | $(MAKE) build TARGET_CFLAGS="-O2" TARGET_LDFLAGS="-s" 22 | 23 | ubsan: 24 | $(MAKE) build TARGET_CFLAGS="-O1 -g -fsanitize=undefined" TARGET_LDFLAGS="-fsanitize=undefined" 25 | 26 | clean: 27 | rm -f *~ 28 | $(MAKE) -C src clean 29 | 30 | build: 31 | $(MAKE) -C src CFLAGS="$(CFLAGS) $(TARGET_CFLAGS)" CC="$(CC)" LDFLAGS="$(LDFLAGS) $(TARGET_LDFLAGS)" LIBS="$(LIBS)" AR="$(AR)" RANLIB="$(RANLIB)" 32 | @echo 33 | @echo "Compilation successful! Try these examples:" 34 | @echo 35 | @echo " src/fh tests/test.fh" 36 | @echo " src/fh tests/mandelbrot.fh" 37 | @echo " src/fh tests/mandel_color.fh" 38 | @echo 39 | 40 | check: debug 41 | valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all src/fh -d $(CHECK_SCRIPT) arg1 arg2 42 | 43 | test: debug 44 | src/fh tests/mandel_color.fh 45 | 46 | dump_exported_symbols: debug 47 | nm src/lib/libfh.a | grep " [A-TV-Zuvw] " 48 | -------------------------------------------------------------------------------- /Makefile.win: -------------------------------------------------------------------------------- 1 | 2 | CC = cl 3 | CFLAGS = -O2 4 | 5 | SRC = src/main.c \ 6 | src/functions.c \ 7 | src/lib/util.c \ 8 | src/lib/input.c \ 9 | src/lib/buffer.c \ 10 | src/lib/stack.c \ 11 | src/lib/symtab.c \ 12 | src/lib/operator.c \ 13 | src/lib/tokenizer.c \ 14 | src/lib/parser.c \ 15 | src/lib/ast.c \ 16 | src/lib/dump_ast.c \ 17 | src/lib/compiler.c \ 18 | src/lib/dump_bytecode.c \ 19 | src/lib/vm.c \ 20 | src/lib/gc.c \ 21 | src/lib/value.c \ 22 | src/lib/map.c \ 23 | src/lib/src_loc.c \ 24 | src/lib/program.c \ 25 | src/lib/c_funcs.c 26 | 27 | fh.exe: $(SRC) 28 | $(CC) $(LDFLAGS) $(CFLAGS) -Isrc/lib -Fe$@ $(SRC) 29 | 30 | clean: 31 | del *.obj 32 | del fh.exe 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BadgerScript 2 | 3 | A toy language written in C. 4 | 5 | Test with: 6 | 7 | ```text 8 | $ make 9 | $ src/fh tests/mandelbrot.fh 10 | ``` 11 | 12 | Or, with Visual Studio on Windows: 13 | 14 | ```text 15 | > nmake -f Makefile.win 16 | > fh tests\mandelbrot.fh 17 | ``` 18 | 19 | ## Features 20 | 21 | - compilation to bytecode 22 | - pretty fast register-based VM following [Lua](https://www.lua.org/)'s design 23 | - simple mark-and-sweep garbage collector 24 | - full closures 25 | - dynamic typing with `null`, `boolean`, `number`, `string`, `array`, 26 | `map`, `closure` (function), and `c_func` (C function) values 27 | 28 | ## Status 29 | 30 | Feature | Status 31 | ------------------------ | ------------------------------------ 32 | Parsing to AST | Working 33 | Bytecode generation | Working 34 | VM (bytecode execution) | Working 35 | Garbage collection | Working 36 | Closures | Working 37 | Map and array objects | Working 38 | 39 | 40 | ## Example Code 41 | 42 | ### Closures 43 | 44 | ```php 45 | function make_counter(num) { 46 | return { 47 | "next" : function() { 48 | num = num + 1; 49 | }, 50 | 51 | "read" : function() { 52 | return num; 53 | }, 54 | }; 55 | } 56 | 57 | function main() { 58 | var c1 = make_counter(0); 59 | var c2 = make_counter(10); 60 | c1.next(); 61 | c2.next(); 62 | printf("%d, %d\n", c1.read(), c2.read()); # prints 1, 11 63 | 64 | c1.next(); 65 | if (c1.read() == 2 && c2.read() == 11) { 66 | printf("ok!\n"); 67 | } else { 68 | error("this should will not happen"); 69 | } 70 | } 71 | ``` 72 | 73 | ### Mandelbrot Set 74 | 75 | ```php 76 | # check point c = (cx, cy) in the complex plane 77 | function calc_point(cx, cy, max_iter) 78 | { 79 | # start at the critical point z = (x, y) = 0 80 | var x = 0; 81 | var y = 0; 82 | 83 | var i = 0; 84 | while (i < max_iter) { 85 | # calculate next iteration: z = z^2 + c 86 | var t = x*x - y*y + cx; 87 | y = 2*x*y + cy; 88 | x = t; 89 | 90 | # stop if |z| > 2 91 | if (x*x + y*y > 4) 92 | break; 93 | i = i + 1; 94 | } 95 | return i; 96 | } 97 | 98 | function mandelbrot(x1, y1, x2, y2, size_x, size_y, max_iter) 99 | { 100 | var step_x = (x2-x1) / (size_x-1); 101 | var step_y = (y2-y1) / (size_y-1); 102 | 103 | var y = y1; 104 | while (y <= y2) { 105 | var x = x1; 106 | while (x <= x2) { 107 | var n = calc_point(x, y, max_iter); 108 | if (n == max_iter) 109 | printf("."); # in Mandelbrot set 110 | else 111 | printf("%d", n%10); # outside 112 | x = x + step_x; 113 | } 114 | y = y + step_y; 115 | printf("\n"); 116 | } 117 | } 118 | 119 | function main() 120 | { 121 | mandelbrot(-2, -2, 2, 2, 150, 50, 1500); 122 | } 123 | ``` 124 | 125 | ## License 126 | 127 | MIT License ([LICENSE](https://github.com/ricardo-massaro/badgerscript/blob/master/LICENSE)) 128 | -------------------------------------------------------------------------------- /run_tests: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | for a in tests/test_*.fh; do 4 | echo -n "$a: "; 5 | valgrind -q --leak-check=full --errors-for-leak-kinds=all src/fh $a; 6 | done 7 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | OBJS = main.o functions.o 3 | 4 | EXTRA_CFLAGS = -Ilib 5 | 6 | .PHONY: $(TARGETS) clean 7 | 8 | fh: $(OBJS) lib/libfh.a 9 | $(CC) $(LDFLAGS) -o $@ $(OBJS) lib/libfh.a $(LIBS) 10 | 11 | lib/libfh.a: 12 | $(MAKE) -C lib 13 | 14 | clean: 15 | rm -f fh *.o *~ 16 | $(MAKE) -C lib clean 17 | 18 | %.o: %.c 19 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< 20 | 21 | .c.o: 22 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< 23 | -------------------------------------------------------------------------------- /src/functions.c: -------------------------------------------------------------------------------- 1 | /* functions.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "functions.h" 8 | 9 | #if defined (__linux__) 10 | #include 11 | #elif defined (_WIN32) 12 | #include 13 | #endif 14 | 15 | static int fn_leak_mem(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 16 | { 17 | (void)prog; 18 | (void)args; 19 | (void)n_args; 20 | 21 | char *str = malloc(42); 22 | snprintf(str, 42, "Hello, world!"); 23 | *ret = fh_new_string(prog, str); 24 | return 0; 25 | } 26 | 27 | static int fn_get_term_lines(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 28 | { 29 | (void)prog; 30 | (void)args; 31 | (void)n_args; 32 | 33 | #if defined (__linux__) 34 | struct winsize term_size; 35 | if (ioctl(0, TIOCGWINSZ, &term_size) >= 0) { 36 | *ret = fh_new_number(term_size.ws_row); 37 | return 0; 38 | } 39 | #elif defined (_WIN32) 40 | CONSOLE_SCREEN_BUFFER_INFO csbi; 41 | if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) != 0 42 | || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) != 0) { 43 | *ret = fh_new_number(csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 44 | return 0; 45 | } 46 | #endif 47 | 48 | *ret = fh_new_number(25); 49 | return 0; 50 | } 51 | 52 | static int fn_gc(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 53 | { 54 | (void)args; 55 | (void)n_args; 56 | 57 | fh_collect_garbage(prog); 58 | *ret = fh_new_null(); 59 | return 0; 60 | } 61 | 62 | int add_functions(struct fh_program *prog) 63 | { 64 | #define ADD_FUNC(name) { #name, fn_##name } 65 | static const struct fh_named_c_func c_funcs[] = { 66 | ADD_FUNC(get_term_lines), 67 | ADD_FUNC(gc), 68 | ADD_FUNC(leak_mem), 69 | }; 70 | return fh_add_c_funcs(prog, c_funcs, sizeof(c_funcs)/sizeof(c_funcs[0])); 71 | } 72 | -------------------------------------------------------------------------------- /src/functions.h: -------------------------------------------------------------------------------- 1 | /* functions.h */ 2 | 3 | #ifndef FUNCTIONS_H_FILE 4 | #define FUNCTIONS_H_FILE 5 | 6 | int add_functions(struct fh_program *prog); 7 | 8 | #endif /* FUNCTIONS_H_FILE */ 9 | -------------------------------------------------------------------------------- /src/lib/Makefile: -------------------------------------------------------------------------------- 1 | 2 | OBJS = util.o input.o buffer.o stack.o symtab.o \ 3 | operator.o tokenizer.o parser.o ast.o dump_ast.o \ 4 | compiler.o dump_bytecode.o vm.o gc.o \ 5 | map.o value.o src_loc.o program.o c_funcs.o 6 | 7 | libfh.a: $(OBJS) 8 | $(AR) $@ $(OBJS) 9 | $(RANLIB) $@ 10 | 11 | clean: 12 | rm -f *.a *.o *~ 13 | 14 | %.o: %.c 15 | $(CC) $(CFLAGS) -o $@ -c $< 16 | -------------------------------------------------------------------------------- /src/lib/ast.c: -------------------------------------------------------------------------------- 1 | /* ast.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ast.h" 8 | 9 | struct fh_ast *fh_new_ast(struct fh_symtab *file_names) 10 | { 11 | struct fh_ast *ast = malloc(sizeof(struct fh_ast)); 12 | if (! ast) 13 | return NULL; 14 | ast->func_list = NULL; 15 | ast->file_names = file_names; 16 | fh_init_symtab(&ast->symtab); 17 | fh_init_buffer(&ast->string_pool); 18 | return ast; 19 | } 20 | 21 | void fh_free_ast(struct fh_ast *ast) 22 | { 23 | fh_free_named_func_list(ast->func_list); 24 | fh_destroy_buffer(&ast->string_pool); 25 | fh_destroy_symtab(&ast->symtab); 26 | free(ast); 27 | } 28 | 29 | const char *fh_get_ast_symbol(struct fh_ast *ast, fh_symbol_id id) 30 | { 31 | return fh_get_symbol_name(&ast->symtab, id); 32 | } 33 | 34 | const char *fh_get_ast_string(struct fh_ast *ast, fh_string_id id) 35 | { 36 | return ast->string_pool.p + id; 37 | } 38 | 39 | fh_symbol_id fh_add_ast_file_name(struct fh_ast *ast, const char *filename) 40 | { 41 | return fh_add_symbol(ast->file_names, filename); 42 | } 43 | 44 | const char *fh_get_ast_file_name(struct fh_ast *ast, fh_symbol_id file_id) 45 | { 46 | return fh_get_symbol_name(ast->file_names, file_id); 47 | } 48 | 49 | /* node creation */ 50 | 51 | struct fh_p_named_func *fh_new_named_func(struct fh_ast *ast, struct fh_src_loc loc) 52 | { 53 | UNUSED(ast); 54 | struct fh_p_named_func *func = malloc(sizeof(struct fh_p_named_func)); 55 | func->next = NULL; 56 | func->loc = loc; 57 | return func; 58 | } 59 | 60 | struct fh_p_expr *fh_new_expr(struct fh_ast *ast, struct fh_src_loc loc, enum fh_expr_type type, size_t extra_size) 61 | { 62 | UNUSED(ast); 63 | struct fh_p_expr *expr = malloc(sizeof(struct fh_p_expr) + extra_size); 64 | if (! expr) 65 | return NULL; 66 | expr->next = NULL; 67 | expr->type = type; 68 | expr->loc = loc; 69 | return expr; 70 | } 71 | 72 | struct fh_p_stmt *fh_new_stmt(struct fh_ast *ast, struct fh_src_loc loc, enum fh_stmt_type type, size_t extra_size) 73 | { 74 | UNUSED(ast); 75 | struct fh_p_stmt *stmt = malloc(sizeof(struct fh_p_stmt) + extra_size); 76 | if (! stmt) 77 | return NULL; 78 | stmt->next = NULL; 79 | stmt->type = type; 80 | stmt->loc = loc; 81 | return stmt; 82 | } 83 | 84 | /* node utility functions */ 85 | int fh_expr_list_size(struct fh_p_expr *list) 86 | { 87 | int n = 0; 88 | for (struct fh_p_expr *e = list; e != NULL; e = e->next) 89 | n++; 90 | return n; 91 | } 92 | 93 | int fh_stmt_list_size(struct fh_p_stmt *list) 94 | { 95 | int n = 0; 96 | for (struct fh_p_stmt *s = list; s != NULL; s = s->next) 97 | n++; 98 | return n; 99 | } 100 | 101 | /* node destruction */ 102 | 103 | void fh_free_named_func(struct fh_p_named_func *func) 104 | { 105 | fh_free_expr(func->func); 106 | free(func); 107 | } 108 | 109 | void fh_free_named_func_list(struct fh_p_named_func *list) 110 | { 111 | struct fh_p_named_func *f = list; 112 | while (f != NULL) { 113 | struct fh_p_named_func *next = f->next; 114 | fh_free_named_func(f); 115 | f = next; 116 | } 117 | } 118 | 119 | void fh_free_expr_children(struct fh_p_expr *expr) 120 | { 121 | switch (expr->type) { 122 | case EXPR_NONE: return; 123 | case EXPR_VAR: return; 124 | case EXPR_NULL: return; 125 | case EXPR_BOOL: return; 126 | case EXPR_NUMBER: return; 127 | case EXPR_STRING: return; 128 | 129 | case EXPR_UN_OP: 130 | fh_free_expr(expr->data.un_op.arg); 131 | return; 132 | 133 | case EXPR_BIN_OP: 134 | fh_free_expr(expr->data.bin_op.left); 135 | fh_free_expr(expr->data.bin_op.right); 136 | return; 137 | 138 | case EXPR_INDEX: 139 | fh_free_expr(expr->data.index.container); 140 | fh_free_expr(expr->data.index.index); 141 | return; 142 | 143 | case EXPR_FUNC_CALL: 144 | fh_free_expr(expr->data.func_call.func); 145 | fh_free_expr_list(expr->data.func_call.arg_list); 146 | return; 147 | 148 | case EXPR_ARRAY_LIT: 149 | fh_free_expr_list(expr->data.array_lit.elem_list); 150 | return; 151 | 152 | case EXPR_MAP_LIT: 153 | fh_free_expr_list(expr->data.map_lit.elem_list); 154 | return; 155 | 156 | case EXPR_FUNC: 157 | fh_free_block(expr->data.func.body); 158 | return; 159 | } 160 | 161 | fprintf(stderr, "INTERNAL ERROR: unknown expression type '%d'\n", expr->type); 162 | } 163 | 164 | void fh_free_expr(struct fh_p_expr *expr) 165 | { 166 | if (expr) { 167 | fh_free_expr_children(expr); 168 | free(expr); 169 | } 170 | } 171 | 172 | void fh_free_expr_list(struct fh_p_expr *list) 173 | { 174 | struct fh_p_expr *e = list; 175 | while (e != NULL) { 176 | struct fh_p_expr *next = e->next; 177 | fh_free_expr(e); 178 | e = next; 179 | } 180 | } 181 | 182 | void fh_free_stmt_children(struct fh_p_stmt *stmt) 183 | { 184 | switch (stmt->type) { 185 | case STMT_NONE: return; 186 | case STMT_EMPTY: return; 187 | case STMT_BREAK: return; 188 | case STMT_CONTINUE: return; 189 | 190 | case STMT_EXPR: 191 | fh_free_expr(stmt->data.expr); 192 | return; 193 | 194 | case STMT_VAR_DECL: 195 | fh_free_expr(stmt->data.decl.val); 196 | return; 197 | 198 | case STMT_BLOCK: 199 | fh_free_block(stmt->data.block); 200 | return; 201 | 202 | case STMT_RETURN: 203 | fh_free_expr(stmt->data.ret.val); 204 | return; 205 | 206 | case STMT_IF: 207 | fh_free_expr(stmt->data.stmt_if.test); 208 | fh_free_stmt(stmt->data.stmt_if.true_stmt); 209 | fh_free_stmt(stmt->data.stmt_if.false_stmt); 210 | return; 211 | 212 | case STMT_WHILE: 213 | fh_free_expr(stmt->data.stmt_while.test); 214 | fh_free_stmt(stmt->data.stmt_while.stmt); 215 | return; 216 | } 217 | 218 | fprintf(stderr, "INTERNAL ERROR: unknown statement type '%d'\n", stmt->type); 219 | } 220 | 221 | void fh_free_stmt(struct fh_p_stmt *stmt) 222 | { 223 | if (stmt) { 224 | fh_free_stmt_children(stmt); 225 | free(stmt); 226 | } 227 | } 228 | 229 | void fh_free_stmt_list(struct fh_p_stmt *list) 230 | { 231 | struct fh_p_stmt *s = list; 232 | while (s != NULL) { 233 | struct fh_p_stmt *next = s->next; 234 | fh_free_stmt(s); 235 | s = next; 236 | } 237 | } 238 | 239 | void fh_free_block(struct fh_p_stmt_block block) 240 | { 241 | fh_free_stmt_list(block.stmt_list); 242 | } 243 | 244 | int fh_ast_visit_expr_nodes(struct fh_p_expr *expr, int (*visit)(struct fh_p_expr *expr, void *data), void *data) 245 | { 246 | int ret; 247 | 248 | if ((ret = visit(expr, data)) != 0) 249 | return ret; 250 | 251 | switch (expr->type) { 252 | case EXPR_NONE: return 0; 253 | case EXPR_VAR: return 0; 254 | case EXPR_NULL: return 0; 255 | case EXPR_BOOL: return 0; 256 | case EXPR_NUMBER: return 0; 257 | case EXPR_STRING: return 0; 258 | case EXPR_FUNC: return 0; 259 | 260 | case EXPR_UN_OP: 261 | if ((ret = fh_ast_visit_expr_nodes(expr->data.un_op.arg, visit, data)) != 0) 262 | return ret; 263 | return 0; 264 | 265 | case EXPR_BIN_OP: 266 | if ((ret = fh_ast_visit_expr_nodes(expr->data.bin_op.left, visit, data)) != 0) 267 | return ret; 268 | if ((ret = fh_ast_visit_expr_nodes(expr->data.bin_op.right, visit, data)) != 0) 269 | return ret; 270 | return 0; 271 | 272 | case EXPR_INDEX: 273 | if ((ret = fh_ast_visit_expr_nodes(expr->data.index.container, visit, data)) != 0) 274 | return ret; 275 | if ((ret = fh_ast_visit_expr_nodes(expr->data.index.index, visit, data)) != 0) 276 | return ret; 277 | return 0; 278 | 279 | case EXPR_FUNC_CALL: 280 | if ((ret = fh_ast_visit_expr_nodes(expr->data.func_call.func, visit, data)) != 0) 281 | return ret; 282 | for (struct fh_p_expr *e = expr->data.func_call.arg_list; e != NULL; e = e->next) { 283 | if ((ret = fh_ast_visit_expr_nodes(e, visit, data)) != 0) 284 | return ret; 285 | } 286 | return 0; 287 | 288 | case EXPR_ARRAY_LIT: 289 | for (struct fh_p_expr *e = expr->data.array_lit.elem_list; e != NULL; e = e->next) { 290 | if ((ret = fh_ast_visit_expr_nodes(e, visit, data)) != 0) 291 | return ret; 292 | } 293 | return 0; 294 | 295 | case EXPR_MAP_LIT: 296 | for (struct fh_p_expr *e = expr->data.map_lit.elem_list; e != NULL; e = e->next) { 297 | if ((ret = fh_ast_visit_expr_nodes(e, visit, data)) != 0) 298 | return ret; 299 | } 300 | return 0; 301 | } 302 | 303 | fprintf(stderr, "INTERNAL ERROR: unknown expression type '%d'\n", expr->type); 304 | return 0; 305 | } 306 | -------------------------------------------------------------------------------- /src/lib/ast.h: -------------------------------------------------------------------------------- 1 | /* ast.h */ 2 | 3 | #ifndef AST_H_FILE 4 | #define AST_H_FILE 5 | 6 | #include "fh_internal.h" 7 | 8 | #define FUNC_CALL_PREC 1000 9 | 10 | enum { 11 | AST_OP_UNM = 256, 12 | AST_OP_EQ, 13 | AST_OP_NEQ, 14 | AST_OP_GT, 15 | AST_OP_GE, 16 | AST_OP_LT, 17 | AST_OP_LE, 18 | AST_OP_OR, 19 | AST_OP_AND, 20 | }; 21 | 22 | /* =========================================== */ 23 | /* == statements ============================= */ 24 | 25 | enum fh_stmt_type { 26 | STMT_NONE, 27 | STMT_EMPTY, 28 | STMT_VAR_DECL, 29 | STMT_EXPR, 30 | STMT_BLOCK, 31 | STMT_RETURN, 32 | STMT_IF, 33 | STMT_WHILE, 34 | STMT_BREAK, 35 | STMT_CONTINUE, 36 | }; 37 | 38 | struct fh_p_stmt_decl { 39 | fh_symbol_id var; 40 | struct fh_p_expr *val; 41 | }; 42 | 43 | struct fh_p_stmt_return { 44 | struct fh_p_expr *val; 45 | }; 46 | 47 | struct fh_p_stmt_if { 48 | struct fh_p_expr *test; 49 | struct fh_p_stmt *true_stmt; 50 | struct fh_p_stmt *false_stmt; 51 | }; 52 | 53 | struct fh_p_stmt_while { 54 | struct fh_p_expr *test; 55 | struct fh_p_stmt *stmt; 56 | }; 57 | 58 | struct fh_p_stmt_block { 59 | struct fh_p_stmt *stmt_list; 60 | }; 61 | 62 | struct fh_p_stmt { 63 | enum fh_stmt_type type; 64 | struct fh_src_loc loc; 65 | struct fh_p_stmt *next; 66 | union { 67 | struct fh_p_stmt_decl decl; 68 | struct fh_p_stmt_block block; 69 | struct fh_p_stmt_return ret; 70 | struct fh_p_stmt_if stmt_if; 71 | struct fh_p_stmt_while stmt_while; 72 | struct fh_p_expr *expr; 73 | } data; 74 | }; 75 | 76 | /* =========================================== */ 77 | /* == expressions ============================ */ 78 | 79 | enum fh_expr_type { 80 | EXPR_NONE, 81 | EXPR_VAR, 82 | EXPR_NULL, 83 | EXPR_BOOL, 84 | EXPR_NUMBER, 85 | EXPR_STRING, 86 | EXPR_BIN_OP, 87 | EXPR_UN_OP, 88 | EXPR_FUNC, 89 | EXPR_FUNC_CALL, 90 | EXPR_INDEX, 91 | EXPR_ARRAY_LIT, 92 | EXPR_MAP_LIT, 93 | }; 94 | 95 | struct fh_p_expr_bin_op { 96 | uint32_t op; 97 | struct fh_p_expr *left; 98 | struct fh_p_expr *right; 99 | }; 100 | 101 | struct fh_p_expr_un_op { 102 | uint32_t op; 103 | struct fh_p_expr *arg; 104 | }; 105 | 106 | struct fh_p_expr_func_call { 107 | struct fh_p_expr *func; 108 | struct fh_p_expr *arg_list; 109 | }; 110 | 111 | struct fh_p_expr_func { 112 | int n_params; 113 | fh_symbol_id *params; 114 | struct fh_p_stmt_block body; 115 | }; 116 | 117 | struct fh_p_expr_index { 118 | struct fh_p_expr *container; 119 | struct fh_p_expr *index; 120 | }; 121 | 122 | struct fh_p_expr_array_lit { 123 | struct fh_p_expr *elem_list; 124 | }; 125 | 126 | struct fh_p_expr_map_lit { 127 | struct fh_p_expr *elem_list; 128 | }; 129 | 130 | struct fh_p_expr { 131 | enum fh_expr_type type; 132 | struct fh_src_loc loc; 133 | struct fh_p_expr *next; 134 | union { 135 | fh_symbol_id var; 136 | double num; 137 | bool b; 138 | fh_string_id str; 139 | struct fh_p_expr_bin_op bin_op; 140 | struct fh_p_expr_un_op un_op; 141 | struct fh_p_expr_func func; 142 | struct fh_p_expr_func_call func_call; 143 | struct fh_p_expr_index index; 144 | struct fh_p_expr_array_lit array_lit; 145 | struct fh_p_expr_map_lit map_lit; 146 | } data; 147 | }; 148 | 149 | /* =========================================== */ 150 | /* == named function ========================= */ 151 | 152 | struct fh_p_named_func { 153 | struct fh_p_named_func *next; 154 | fh_symbol_id name; 155 | struct fh_src_loc loc; 156 | struct fh_p_expr *func; 157 | }; 158 | 159 | /* =========================================== */ 160 | 161 | struct fh_ast { 162 | struct fh_buffer string_pool; 163 | struct fh_symtab symtab; 164 | struct fh_symtab *file_names; 165 | struct fh_p_named_func *func_list; 166 | }; 167 | 168 | struct fh_ast *fh_new_ast(struct fh_symtab *file_names); 169 | void fh_free_ast(struct fh_ast *ast); 170 | const char *fh_get_ast_symbol(struct fh_ast *ast, fh_symbol_id id); 171 | const char *fh_get_ast_string(struct fh_ast *ast, fh_string_id id); 172 | const char *fh_get_ast_file_name(struct fh_ast *ast, fh_symbol_id file_id); 173 | fh_symbol_id fh_add_ast_file_name(struct fh_ast *ast, const char *filename); 174 | 175 | int fh_ast_visit_expr_nodes(struct fh_p_expr *expr, int (*visit)(struct fh_p_expr *expr, void *data), void *data); 176 | 177 | struct fh_p_expr *fh_new_expr(struct fh_ast *ast, struct fh_src_loc loc, enum fh_expr_type type, size_t extra_size); 178 | struct fh_p_stmt *fh_new_stmt(struct fh_ast *ast, struct fh_src_loc loc, enum fh_stmt_type type, size_t extra_size); 179 | struct fh_p_named_func *fh_new_named_func(struct fh_ast *ast, struct fh_src_loc loc); 180 | 181 | int fh_expr_list_size(struct fh_p_expr *list); 182 | int fh_stmt_list_size(struct fh_p_stmt *list); 183 | 184 | void fh_free_named_func(struct fh_p_named_func *func); 185 | void fh_free_named_func_list(struct fh_p_named_func *list); 186 | void fh_free_block(struct fh_p_stmt_block block); 187 | void fh_free_stmt(struct fh_p_stmt *stmt); 188 | void fh_free_stmt_children(struct fh_p_stmt *stmt); 189 | void fh_free_stmt_list(struct fh_p_stmt *list); 190 | void fh_free_expr(struct fh_p_expr *expr); 191 | void fh_free_expr_children(struct fh_p_expr *expr); 192 | void fh_free_expr_list(struct fh_p_expr *list); 193 | 194 | void fh_dump_named_func(struct fh_ast *ast, struct fh_p_named_func *func); 195 | void fh_dump_expr(struct fh_ast *ast, struct fh_p_expr *expr); 196 | void fh_dump_ast(struct fh_ast *p); 197 | 198 | #endif /* AST_H_FILE */ 199 | -------------------------------------------------------------------------------- /src/lib/buffer.c: -------------------------------------------------------------------------------- 1 | /* buffer.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "fh_internal.h" 7 | 8 | void fh_init_buffer(struct fh_buffer *buf) 9 | { 10 | buf->p = NULL; 11 | buf->size = 0; 12 | buf->cap = 0; 13 | } 14 | 15 | void fh_destroy_buffer(struct fh_buffer *buf) 16 | { 17 | if (buf->p != NULL) 18 | free(buf->p); 19 | buf->p = NULL; 20 | buf->size = 0; 21 | buf->cap = 0; 22 | } 23 | 24 | int fh_buf_grow(struct fh_buffer *buf, size_t add_size) 25 | { 26 | size_t new_size = buf->size + add_size; 27 | if (new_size > INT_MAX || new_size < (size_t)buf->size) 28 | return -1; 29 | if ((int)new_size > buf->cap) { 30 | size_t new_cap = ((new_size + 1024 + 1) / 1024) * 1024; 31 | if (new_cap > INT_MAX || new_cap < new_size) 32 | return -1; 33 | void *new_p = realloc(buf->p, new_cap); 34 | if (new_p == NULL) 35 | return -1; 36 | buf->p = new_p; 37 | buf->cap = (int) new_cap; 38 | } 39 | 40 | buf->size = (int) new_size; 41 | return 0; 42 | } 43 | 44 | int fh_buf_shrink_to_fit(struct fh_buffer *buf) 45 | { 46 | if (buf->size == 0) { 47 | if (buf->p) { 48 | free(buf->p); 49 | buf->p = NULL; 50 | } 51 | return 0; 52 | } 53 | 54 | void *new_p = realloc(buf->p, buf->size); 55 | if (new_p == NULL) 56 | return -1; 57 | buf->p = new_p; 58 | return 0; 59 | } 60 | 61 | int fh_buf_add_string(struct fh_buffer *buf, const void *str, size_t str_size) 62 | { 63 | int pos = buf->size; 64 | if (fh_buf_grow(buf, str_size + 1) < 0) 65 | return -1; 66 | memcpy(buf->p + pos, str, str_size); 67 | buf->p[pos + str_size] = '\0'; 68 | return pos; 69 | } 70 | 71 | int fh_buf_add_byte(struct fh_buffer *buf, uint8_t c) 72 | { 73 | int pos = buf->size; 74 | if (fh_buf_grow(buf, 1) < 0) 75 | return -1; 76 | buf->p[pos++] = c; 77 | return pos; 78 | } 79 | 80 | int fh_buf_add_u16(struct fh_buffer *buf, uint16_t c) 81 | { 82 | int pos = buf->size; 83 | if (fh_buf_grow(buf, 2) < 0) 84 | return -1; 85 | buf->p[pos++] = c & 0xff; 86 | buf->p[pos++] = (c >> 8) & 0xff; 87 | return pos; 88 | } 89 | -------------------------------------------------------------------------------- /src/lib/bytecode.h: -------------------------------------------------------------------------------- 1 | /* bytecode.h */ 2 | 3 | #ifndef BYTECODE_H_FILE 4 | #define BYTECODE_H_FILE 5 | 6 | #include "fh_internal.h" 7 | #include "value.h" 8 | 9 | #define MAX_FUNC_REGS 256 10 | 11 | enum fh_bc_opcode { 12 | OPC_RET, 13 | OPC_CALL, 14 | 15 | OPC_CLOSURE, 16 | OPC_GETUPVAL, 17 | OPC_SETUPVAL, 18 | 19 | OPC_MOV, 20 | OPC_LDNULL, 21 | OPC_LDC, 22 | 23 | OPC_JMP, 24 | OPC_TEST, 25 | OPC_CMP_EQ, 26 | OPC_CMP_LT, 27 | OPC_CMP_LE, 28 | 29 | OPC_GETEL, 30 | OPC_SETEL, 31 | OPC_NEWARRAY, 32 | OPC_NEWMAP, 33 | 34 | OPC_ADD, 35 | OPC_SUB, 36 | OPC_MUL, 37 | OPC_DIV, 38 | OPC_MOD, 39 | OPC_NEG, 40 | OPC_NOT, 41 | }; 42 | 43 | #define GET_INSTR_OP(instr) (((uint32_t)(instr))&0x3f) 44 | #define GET_INSTR_RA(instr) ((((uint32_t)(instr))>>6)&0xff) 45 | #define GET_INSTR_RB(instr) ((((uint32_t)(instr))>>14)&0x1ff) 46 | #define GET_INSTR_RC(instr) ((((uint32_t)(instr))>>23)&0x1ff) 47 | #define GET_INSTR_RU(instr) ((((uint32_t)(instr))>>14)&0x3ffff) 48 | #define GET_INSTR_RS(instr) (((int32_t)GET_INSTR_RU(instr))-(1<<17)) 49 | 50 | #define PLACE_INSTR_OP(op) ((uint32_t)(op)&0x3f ) 51 | #define PLACE_INSTR_RA(ra) (((uint32_t)(ra)&0xff )<<6) 52 | #define PLACE_INSTR_RB(rb) (((uint32_t)(rb)&0x1ff)<<14) 53 | #define PLACE_INSTR_RC(rc) (((uint32_t)(rc)&0x1ff)<<23) 54 | #define PLACE_INSTR_RU(ru) (((uint32_t)(ru)&0x3ffff)<<14) 55 | #define PLACE_INSTR_RS(rs) PLACE_INSTR_RU((rs)+(1<<17)) 56 | 57 | #define INSTR_OP_MASK 0x3f 58 | #define INSTR_RA_MASK ( 0xff<<6) 59 | #define INSTR_RB_MASK (0x1ff<<14) 60 | #define INSTR_RC_MASK (0x1ff<<23) 61 | #define INSTR_RU_MASK ((uint32_t)0x3ffff<<14) 62 | #define INSTR_RS_MASK INSTR_RU_MASK 63 | 64 | #define MAKE_INSTR_A(op, ra) (PLACE_INSTR_OP(op) | PLACE_INSTR_RA(ra)) 65 | #define MAKE_INSTR_AB(op, ra, rb) (PLACE_INSTR_OP(op) | PLACE_INSTR_RA(ra) | PLACE_INSTR_RB(rb)) 66 | #define MAKE_INSTR_ABC(op, ra, rb, rc) (PLACE_INSTR_OP(op) | PLACE_INSTR_RA(ra) | PLACE_INSTR_RB(rb) | PLACE_INSTR_RC(rc)) 67 | #define MAKE_INSTR_AU(op, ra, ru) (PLACE_INSTR_OP(op) | PLACE_INSTR_RA(ra) | PLACE_INSTR_RU(ru)) 68 | #define MAKE_INSTR_AS(op, ra, rs) (PLACE_INSTR_OP(op) | PLACE_INSTR_RA(ra) | PLACE_INSTR_RS(rs)) 69 | 70 | void fh_dump_bc_instr(struct fh_program *prog, int32_t addr, uint32_t instr); 71 | 72 | #endif /* BYTECODE_H_FILE */ 73 | -------------------------------------------------------------------------------- /src/lib/c_funcs.c: -------------------------------------------------------------------------------- 1 | /* c_funcs.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fh.h" 8 | #include "value.h" 9 | #include "fh_internal.h" 10 | 11 | static void print_value(struct fh_value *val) 12 | { 13 | if (val->type == FH_VAL_UPVAL) 14 | val = GET_OBJ_UPVAL(val->data.obj)->val; 15 | 16 | switch (val->type) { 17 | case FH_VAL_NULL: printf("null"); return; 18 | case FH_VAL_BOOL: printf("%s", (val->data.b) ? "true" : "false"); return; 19 | case FH_VAL_NUMBER: printf("%g", val->data.num); return; 20 | case FH_VAL_STRING: printf("%s", GET_VAL_STRING_DATA(val)); return; 21 | case FH_VAL_ARRAY: printf("", GET_VAL_ARRAY(val)->len); return; 22 | case FH_VAL_MAP: printf("", GET_VAL_MAP(val)->len); return; 23 | case FH_VAL_CLOSURE: printf("", val->data.obj); return; 24 | case FH_VAL_UPVAL: printf(""); return; 25 | case FH_VAL_FUNC_DEF: printf("", val->data.obj); return; 26 | case FH_VAL_C_FUNC: 27 | { 28 | printf("data.c_func; 30 | p += sizeof(val->data.c_func); 31 | for (size_t i = 0; i < sizeof(val->data.c_func); i++) 32 | if (*--p) 33 | printf("%02x", *p); 34 | printf(">"); 35 | return; 36 | } 37 | } 38 | printf("", val->type); 39 | } 40 | 41 | static int check_n_args(struct fh_program *prog, const char *func_name, int n_expected, int n_received) 42 | { 43 | if (n_expected >= 0 && n_received != n_expected) 44 | return fh_set_error(prog, "%s: expected %d argument(s), got %d", func_name, n_expected, n_received); 45 | if (n_received != INT_MIN && n_received < -n_expected) 46 | return fh_set_error(prog, "%s: expected at least %d argument(s), got %d", func_name, -n_expected, n_received); 47 | return 0; 48 | } 49 | 50 | 51 | static int fn_error(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 52 | { 53 | (void)ret; 54 | 55 | if (check_n_args(prog, "error()", 1, n_args)) 56 | return -1; 57 | 58 | const char *str = GET_VAL_STRING_DATA(&args[0]); 59 | if (! str) 60 | return fh_set_error(prog, "error(): argument 1 must be a string"); 61 | return fh_set_error(prog, "%s", str); 62 | } 63 | 64 | static int fn_len(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 65 | { 66 | if (check_n_args(prog, "len()", 1, n_args)) 67 | return -1; 68 | 69 | struct fh_array *arr = GET_VAL_ARRAY(&args[0]); 70 | if (arr) { 71 | *ret = fh_make_number(arr->len); 72 | return 0; 73 | } 74 | struct fh_map *map = GET_VAL_MAP(&args[0]); 75 | if (map) { 76 | *ret = fh_make_number(map->len); 77 | return 0; 78 | } 79 | return fh_set_error(prog, "len(): argument 1 must be an array or map"); 80 | } 81 | 82 | static int fn_delete(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 83 | { 84 | if (check_n_args(prog, "delete()", 2, n_args)) 85 | return -1; 86 | 87 | struct fh_array *arr = GET_VAL_ARRAY(&args[0]); 88 | if (arr) { 89 | if (! fh_is_number(&args[1])) 90 | return fh_set_error(prog, "delete(): argument 2 must be a number"); 91 | uint32_t index = (uint32_t) (int) fh_get_number(&args[1]); 92 | if (index >= arr->len) 93 | return fh_set_error(prog, "delete(): array index out of bounds: %d", index); 94 | *ret = arr->items[index]; 95 | if (index+1 < arr->len) 96 | memmove(arr->items + index, arr->items + index + 1, (arr->len - (index + 1)) * sizeof(struct fh_value)); 97 | arr->len--; 98 | return 0; 99 | } 100 | struct fh_map *map = GET_VAL_MAP(&args[0]); 101 | if (map) { 102 | if (fh_get_map_object_value(map, &args[1], ret) < 0 103 | || fh_delete_map_object_entry(map, &args[1]) < 0) 104 | return fh_set_error(prog, "delete(): key not in map"); 105 | return 0; 106 | } 107 | return fh_set_error(prog, "delete(): argument 1 must be an array or map"); 108 | } 109 | 110 | static int fn_next_key(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 111 | { 112 | if (check_n_args(prog, "next_key()", 2, n_args)) 113 | return -1; 114 | 115 | struct fh_map *map = GET_VAL_MAP(&args[0]); 116 | if (! map) 117 | return fh_set_error(prog, "next_key(): argument 1 must be a map"); 118 | if (fh_next_map_object_key(map, &args[1], ret) < 0) 119 | return fh_set_error(prog, "next_key(): key not in map"); 120 | return 0; 121 | } 122 | 123 | static int fn_contains_key(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 124 | { 125 | if (check_n_args(prog, "contains_key()", 2, n_args)) 126 | return -1; 127 | 128 | struct fh_map *map = GET_VAL_MAP(&args[0]); 129 | if (! map) 130 | return fh_set_error(prog, "contains_key(): argument 1 must be a map"); 131 | if (fh_get_map_object_value(map, &args[1], ret) < 0) { 132 | *ret = fh_make_bool(false); 133 | } else { 134 | printf("key "); print_value(&args[1]); printf(" has value "); print_value(ret); printf("\n"); 135 | *ret = fh_make_bool(true); 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | static int fn_append(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 142 | { 143 | if (check_n_args(prog, "append()", -2, n_args)) 144 | return -1; 145 | struct fh_array *arr = GET_VAL_ARRAY(&args[0]); 146 | if (! arr) 147 | return fh_set_error(prog, "append(): argument 1 must be an array"); 148 | struct fh_value *new_items = fh_grow_array_object(prog, arr, n_args-1); 149 | if (! new_items) 150 | return fh_set_error(prog, "out of memory"); 151 | memcpy(new_items, args + 1, sizeof(struct fh_value) * (n_args-1)); 152 | *ret = args[0]; 153 | return 0; 154 | } 155 | 156 | static int fn_print(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 157 | { 158 | (void)prog; 159 | 160 | for (int i = 0; i < n_args; i++) 161 | print_value(&args[i]); 162 | *ret = fh_make_null(); 163 | return 0; 164 | } 165 | 166 | static int fn_printf(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args) 167 | { 168 | if (n_args == 0 || ! fh_is_string(&args[0])) 169 | goto end; 170 | 171 | int next_arg = 1; 172 | for (const char *c = fh_get_string(&args[0]); *c != '\0'; c++) { 173 | if (*c != '%') { 174 | putchar(*c); 175 | continue; 176 | } 177 | c++; 178 | if (*c == '%') { 179 | putchar('%'); 180 | continue; 181 | } 182 | if (next_arg >= n_args) 183 | return fh_set_error(prog, "printf(): no argument supplied for '%%%c'", *c); 184 | 185 | switch (*c) { 186 | case 'd': 187 | if (! fh_is_number(&args[next_arg])) 188 | return fh_set_error(prog, "printf(): invalid argument type for '%%%c'", *c); 189 | printf("%lld", (long long) (int64_t) args[next_arg].data.num); 190 | break; 191 | 192 | case 'u': 193 | case 'x': 194 | if (! fh_is_number(&args[next_arg])) 195 | return fh_set_error(prog, "printf(): invalid argument type for '%%%c'", *c); 196 | printf((*c == 'u') ? "%llu" : "%llx", (unsigned long long) (int64_t) args[next_arg].data.num); 197 | break; 198 | 199 | case 'f': 200 | case 'g': 201 | if (! fh_is_number(&args[next_arg])) 202 | return fh_set_error(prog, "printf(): invalid argument type for '%%%c'", *c); 203 | printf((*c == 'f') ? "%f" : "%g", args[next_arg].data.num); 204 | break; 205 | 206 | case 's': 207 | print_value(&args[next_arg]); 208 | break; 209 | 210 | default: 211 | return fh_set_error(prog, "printf(): invalid format specifier: '%%%c'", *c); 212 | } 213 | next_arg++; 214 | } 215 | 216 | end: 217 | *ret = fh_make_null(); 218 | return 0; 219 | } 220 | 221 | #define DEF_FN(name) { #name, fn_##name } 222 | const struct fh_named_c_func fh_std_c_funcs[] = { 223 | DEF_FN(error), 224 | DEF_FN(print), 225 | DEF_FN(printf), 226 | DEF_FN(len), 227 | DEF_FN(next_key), 228 | DEF_FN(contains_key), 229 | DEF_FN(append), 230 | DEF_FN(delete), 231 | }; 232 | const int fh_std_c_funcs_len = sizeof(fh_std_c_funcs)/sizeof(fh_std_c_funcs[0]); 233 | -------------------------------------------------------------------------------- /src/lib/compiler.h: -------------------------------------------------------------------------------- 1 | /* compiler.h */ 2 | 3 | #ifndef COMPILER_H_FILE 4 | #define COMPILER_H_FILE 5 | 6 | #include "fh_internal.h" 7 | #include "stack.h" 8 | #include "value.h" 9 | 10 | DECLARE_STACK(int_stack, int); 11 | DECLARE_STACK(code_stack, uint32_t); 12 | 13 | DECLARE_STACK(upval_def_stack, struct fh_upval_def); 14 | 15 | enum compiler_block_type { 16 | COMP_BLOCK_PLAIN, 17 | COMP_BLOCK_FUNC, 18 | COMP_BLOCK_WHILE, 19 | }; 20 | 21 | struct block_info { 22 | enum compiler_block_type type; 23 | int32_t start_addr; 24 | int parent_num_regs; 25 | }; 26 | 27 | DECLARE_STACK(block_info_stack, struct block_info); 28 | 29 | struct reg_info { 30 | fh_symbol_id var; 31 | bool alloc; 32 | bool used_by_inner_func; 33 | }; 34 | 35 | DECLARE_STACK(reg_stack, struct reg_info); 36 | 37 | struct func_info { 38 | struct func_info *parent; 39 | int num_regs; 40 | struct reg_stack regs; 41 | struct int_stack break_addrs; 42 | struct block_info_stack blocks; 43 | struct code_stack code; 44 | struct value_stack consts; 45 | struct upval_def_stack upvals; 46 | struct fh_src_loc last_instr_src_loc; 47 | struct fh_buffer code_src_loc; 48 | }; 49 | 50 | DECLARE_STACK(func_info_stack, struct func_info); 51 | 52 | struct fh_compiler { 53 | struct fh_program *prog; 54 | struct fh_ast *ast; 55 | struct func_info_stack funcs; 56 | }; 57 | 58 | void fh_init_compiler(struct fh_compiler *c, struct fh_program *prog); 59 | void fh_destroy_compiler(struct fh_compiler *c); 60 | int fh_compile(struct fh_compiler *c, struct fh_ast *ast); 61 | int fh_compiler_error(struct fh_compiler *c, struct fh_src_loc loc, char *fmt, ...) FH_PRINTF_FORMAT(3, 4); 62 | uint32_t *fh_get_compiler_instructions(struct fh_compiler *c, int32_t *len); 63 | 64 | #endif /* COMPILER_H_FILE */ 65 | -------------------------------------------------------------------------------- /src/lib/dump_ast.c: -------------------------------------------------------------------------------- 1 | /* dump_ast.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fh_internal.h" 8 | #include "ast.h" 9 | 10 | #define INDENT 4 11 | 12 | static bool expr_needs_paren(struct fh_p_expr *expr) 13 | { 14 | switch (expr->type) { 15 | case EXPR_VAR: 16 | case EXPR_NUMBER: 17 | case EXPR_STRING: 18 | case EXPR_FUNC_CALL: 19 | return false; 20 | 21 | default: 22 | return true; 23 | } 24 | } 25 | 26 | static void dump_string(struct fh_ast *ast, const char *str) 27 | { 28 | UNUSED(ast); 29 | printf("\""); 30 | for (const char *p = str; *p != '\0'; p++) { 31 | switch (*p) { 32 | case '\n': printf("\\n"); break; 33 | case '\r': printf("\\r"); break; 34 | case '\t': printf("\\t"); break; 35 | case '\\': printf("\\\\"); break; 36 | case '"': printf("\\\""); break; 37 | default: 38 | if (*p < 32) 39 | printf("\\x%02x", (unsigned char) *p); 40 | else 41 | printf("%c", *p); 42 | break; 43 | } 44 | } 45 | printf("\""); 46 | } 47 | 48 | static void dump_expr(struct fh_ast *ast, int indent, struct fh_p_expr *expr) 49 | { 50 | switch (expr->type) { 51 | case EXPR_NONE: 52 | printf(""); 53 | return; 54 | 55 | case EXPR_VAR: 56 | printf("%s", fh_get_ast_symbol(ast, expr->data.var)); 57 | return; 58 | 59 | case EXPR_NULL: 60 | printf("null"); 61 | return; 62 | 63 | case EXPR_BOOL: 64 | printf("%s", (expr->data.b) ? "true" : "false"); 65 | return; 66 | 67 | case EXPR_NUMBER: 68 | printf("%g", expr->data.num); 69 | return; 70 | 71 | case EXPR_STRING: 72 | dump_string(ast, fh_get_ast_string(ast, expr->data.str)); 73 | return; 74 | 75 | case EXPR_BIN_OP: 76 | if (expr_needs_paren(expr->data.bin_op.left)) printf("("); 77 | dump_expr(ast, indent, expr->data.bin_op.left); 78 | if (expr_needs_paren(expr->data.bin_op.left)) printf(")"); 79 | printf(" %s ", fh_get_op_name(expr->data.bin_op.op)); 80 | if (expr_needs_paren(expr->data.bin_op.right)) printf("("); 81 | dump_expr(ast, indent, expr->data.bin_op.right); 82 | if (expr_needs_paren(expr->data.bin_op.right)) printf(")"); 83 | return; 84 | 85 | case EXPR_INDEX: 86 | if (expr_needs_paren(expr->data.index.container)) printf("("); 87 | dump_expr(ast, indent, expr->data.index.container); 88 | if (expr_needs_paren(expr->data.index.container)) printf(")"); 89 | printf("["); 90 | dump_expr(ast, indent, expr->data.index.index); 91 | printf("]"); 92 | return; 93 | 94 | case EXPR_UN_OP: 95 | printf("%s", fh_get_op_name(expr->data.un_op.op)); 96 | if (expr_needs_paren(expr->data.un_op.arg)) printf("("); 97 | dump_expr(ast, indent, expr->data.un_op.arg); 98 | if (expr_needs_paren(expr->data.un_op.arg)) printf(")"); 99 | return; 100 | 101 | case EXPR_FUNC_CALL: 102 | if (expr_needs_paren(expr->data.func_call.func)) printf("("); 103 | dump_expr(ast, indent, expr->data.func_call.func); 104 | if (expr_needs_paren(expr->data.func_call.func)) printf(")"); 105 | printf("("); 106 | for (struct fh_p_expr *e = expr->data.func_call.arg_list; e != NULL; e = e->next) { 107 | dump_expr(ast, indent, e); 108 | if (e->next) 109 | printf(", "); 110 | } 111 | printf(")"); 112 | return; 113 | 114 | case EXPR_ARRAY_LIT: 115 | printf("[ "); 116 | for (struct fh_p_expr *e = expr->data.array_lit.elem_list; e != NULL; e = e->next) { 117 | dump_expr(ast, indent, e); 118 | if (e->next) 119 | printf(", "); 120 | } 121 | printf(" ]"); 122 | return; 123 | 124 | case EXPR_MAP_LIT: 125 | printf("{"); 126 | for (struct fh_p_expr *e = expr->data.map_lit.elem_list; e != NULL; e = e->next) { 127 | printf(" "); 128 | dump_expr(ast, indent, e); 129 | printf(" : "); 130 | if (! (e = e->next)) { 131 | printf(""); 132 | break; 133 | } 134 | dump_expr(ast, indent, e); 135 | if (e->next) 136 | printf(","); 137 | } 138 | printf("}"); 139 | return; 140 | 141 | case EXPR_FUNC: 142 | printf("<...func...>"); 143 | return; 144 | } 145 | 146 | printf("", expr->type); 147 | } 148 | 149 | static void dump_block(struct fh_ast *ast, int indent, struct fh_p_stmt_block block); 150 | 151 | static void dump_stmt(struct fh_ast *ast, int indent, struct fh_p_stmt *stmt) 152 | { 153 | switch (stmt->type) { 154 | case STMT_NONE: 155 | printf("%*s;", indent, ""); 156 | return; 157 | 158 | case STMT_EMPTY: 159 | printf("%*s;\n", indent, ""); 160 | return; 161 | 162 | case STMT_BREAK: 163 | printf("%*sbreak;\n", indent, ""); 164 | return; 165 | 166 | case STMT_CONTINUE: 167 | printf("%*scontinue;\n", indent, ""); 168 | return; 169 | 170 | case STMT_VAR_DECL: 171 | printf("%*svar %s", indent, "", fh_get_ast_symbol(ast, stmt->data.decl.var)); 172 | if (stmt->data.decl.val) { 173 | printf(" = "); 174 | dump_expr(ast, indent+INDENT, stmt->data.decl.val); 175 | } 176 | printf(";\n"); 177 | return; 178 | 179 | case STMT_EXPR: 180 | printf("%*s", indent, ""); 181 | dump_expr(ast, indent+INDENT, stmt->data.expr); 182 | printf(";\n"); 183 | return; 184 | 185 | case STMT_RETURN: 186 | printf("%*sreturn", indent, ""); 187 | if (stmt->data.ret.val) { 188 | printf(" "); 189 | dump_expr(ast, indent+INDENT, stmt->data.ret.val); 190 | } 191 | printf(";\n"); 192 | return; 193 | 194 | case STMT_BLOCK: 195 | printf("%*s", indent, ""); 196 | dump_block(ast, indent, stmt->data.block); 197 | printf("\n"); 198 | return; 199 | 200 | case STMT_IF: 201 | printf("%*sif (", indent, ""); 202 | dump_expr(ast, indent+INDENT, stmt->data.stmt_if.test); 203 | printf(")"); 204 | 205 | if (stmt->data.stmt_if.true_stmt->type == STMT_BLOCK) { 206 | printf(" "); 207 | dump_block(ast, indent, stmt->data.stmt_if.true_stmt->data.block); 208 | } else { 209 | printf("\n"); 210 | dump_stmt(ast, indent+INDENT, stmt->data.stmt_if.true_stmt); 211 | } 212 | 213 | if (stmt->data.stmt_if.false_stmt) { 214 | if (stmt->data.stmt_if.true_stmt->type == STMT_BLOCK) 215 | printf(" else"); 216 | else 217 | printf("%*selse", indent, ""); 218 | if (stmt->data.stmt_if.false_stmt->type == STMT_BLOCK) { 219 | printf(" "); 220 | dump_block(ast, indent, stmt->data.stmt_if.false_stmt->data.block); 221 | printf("\n"); 222 | } else { 223 | printf("\n"); 224 | dump_stmt(ast, indent+INDENT, stmt->data.stmt_if.false_stmt); 225 | } 226 | } else { 227 | if (stmt->data.stmt_if.true_stmt->type == STMT_BLOCK) 228 | printf("\n"); 229 | } 230 | return; 231 | 232 | case STMT_WHILE: 233 | printf("%*swhile (", indent, ""); 234 | dump_expr(ast, indent+INDENT, stmt->data.stmt_while.test); 235 | printf(")"); 236 | 237 | if (stmt->data.stmt_while.stmt->type == STMT_BLOCK) { 238 | printf(" "); 239 | dump_block(ast, indent, stmt->data.stmt_while.stmt->data.block); 240 | printf("\n"); 241 | } else { 242 | printf("\n"); 243 | dump_stmt(ast, indent+INDENT, stmt->data.stmt_while.stmt); 244 | } 245 | return; 246 | } 247 | 248 | printf("%*s# unknown statement type: %d\n", indent, "", stmt->type); 249 | } 250 | 251 | static void dump_block(struct fh_ast *ast, int indent, struct fh_p_stmt_block block) 252 | { 253 | printf("{\n"); 254 | for (struct fh_p_stmt *s = block.stmt_list; s != NULL; s = s->next) 255 | dump_stmt(ast, indent+INDENT, s); 256 | printf("%*s}", indent, ""); 257 | } 258 | 259 | void fh_dump_expr(struct fh_ast *ast, struct fh_p_expr *expr) 260 | { 261 | dump_expr(ast, 0, expr); 262 | } 263 | 264 | void fh_dump_block(struct fh_ast *ast, struct fh_p_stmt_block block) 265 | { 266 | dump_block(ast, 0, block); 267 | } 268 | 269 | void fh_dump_named_func(struct fh_ast *ast, struct fh_p_named_func *func) 270 | { 271 | printf("function %s(", fh_get_ast_symbol(ast, func->name)); 272 | for (int i = 0; i < func->func->data.func.n_params; i++) { 273 | printf("%s", fh_get_ast_symbol(ast, func->func->data.func.params[i])); 274 | if (i+1 < func->func->data.func.n_params) 275 | printf(", "); 276 | } 277 | printf(") "); 278 | dump_block(ast, 0, func->func->data.func.body); 279 | printf("\n"); 280 | } 281 | 282 | void fh_dump_ast(struct fh_ast *ast) 283 | { 284 | for (struct fh_p_named_func *f = ast->func_list; f != NULL; f = f->next) { 285 | fh_dump_named_func(ast, f); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/lib/dump_bytecode.c: -------------------------------------------------------------------------------- 1 | /* dump_bytecode.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "bytecode.h" 8 | 9 | static void dump_string(const char *str) 10 | { 11 | printf("\""); 12 | for (const char *p = str; *p != '\0'; p++) { 13 | switch (*p) { 14 | case '\n': printf("\\n"); break; 15 | case '\r': printf("\\r"); break; 16 | case '\t': printf("\\t"); break; 17 | case '\\': printf("\\\\"); break; 18 | case '"': printf("\\\""); break; 19 | default: 20 | if (*p < 32) 21 | printf("\\x%02x", (unsigned char) *p); 22 | else 23 | printf("%c", *p); 24 | break; 25 | } 26 | } 27 | printf("\""); 28 | } 29 | 30 | static void dump_instr_ret(uint32_t instr) 31 | { 32 | if (GET_INSTR_RA(instr) == 0) { 33 | printf("\n"); 34 | return; 35 | } 36 | 37 | int b = GET_INSTR_RB(instr); 38 | if (b <= MAX_FUNC_REGS) 39 | printf("r%d\n", b); 40 | else 41 | printf("c[%d]\n", b - MAX_FUNC_REGS - 1); 42 | } 43 | 44 | static void dump_instr_jmp(uint32_t instr, int32_t addr) 45 | { 46 | int n_close = GET_INSTR_RA(instr); 47 | if (n_close) 48 | printf("<%d> %d\n", n_close, addr + 1 + GET_INSTR_RS(instr)); 49 | else 50 | printf("%d\n", addr + 1 + GET_INSTR_RS(instr)); 51 | } 52 | 53 | static void dump_instr_up_rkb(uint32_t instr) 54 | { 55 | int a = GET_INSTR_RA(instr); 56 | int b = GET_INSTR_RB(instr); 57 | 58 | printf("u[%d], ", a); 59 | if (b <= MAX_FUNC_REGS) 60 | printf("r%d", b); 61 | else 62 | printf("c[%d]", b-MAX_FUNC_REGS-1); 63 | printf("\n"); 64 | } 65 | 66 | static void dump_instr_ra_up(uint32_t instr) 67 | { 68 | int a = GET_INSTR_RA(instr); 69 | int b = GET_INSTR_RB(instr); 70 | 71 | printf("r%d, ", a); 72 | printf("u[%d]\n", b); 73 | } 74 | 75 | static void dump_instr_abc(uint32_t instr) 76 | { 77 | int a = GET_INSTR_RA(instr); 78 | int b = GET_INSTR_RB(instr); 79 | int c = GET_INSTR_RC(instr); 80 | 81 | printf("%d, %d, %d\n", a, b, c); 82 | } 83 | 84 | static void dump_instr_a_rkb(uint32_t instr) 85 | { 86 | int a = GET_INSTR_RA(instr); 87 | int b = GET_INSTR_RB(instr); 88 | 89 | printf("%d, ", a); 90 | if (b <= MAX_FUNC_REGS) 91 | printf("r%d", b); 92 | else 93 | printf("c[%d]", b-MAX_FUNC_REGS-1); 94 | printf("\n"); 95 | } 96 | 97 | static void dump_instr_a_rkb_rkc(uint32_t instr) 98 | { 99 | int a = GET_INSTR_RA(instr); 100 | int b = GET_INSTR_RB(instr); 101 | int c = GET_INSTR_RC(instr); 102 | 103 | printf("%d, ", a); 104 | if (b <= MAX_FUNC_REGS) 105 | printf("r%d, ", b); 106 | else 107 | printf("c[%d], ", b-MAX_FUNC_REGS-1); 108 | if (c <= MAX_FUNC_REGS) 109 | printf("r%d", c); 110 | else 111 | printf("c[%d]", c-MAX_FUNC_REGS-1); 112 | printf("\n"); 113 | } 114 | 115 | static void dump_instr_ra_rkb_rkc(uint32_t instr) 116 | { 117 | int a = GET_INSTR_RA(instr); 118 | int b = GET_INSTR_RB(instr); 119 | int c = GET_INSTR_RC(instr); 120 | 121 | printf("r%d, ", a); 122 | if (b <= MAX_FUNC_REGS) 123 | printf("r%d, ", b); 124 | else 125 | printf("c[%d], ", b-MAX_FUNC_REGS-1); 126 | if (c <= MAX_FUNC_REGS) 127 | printf("r%d", c); 128 | else 129 | printf("c[%d]", c-MAX_FUNC_REGS-1); 130 | printf("\n"); 131 | } 132 | 133 | static void dump_instr_ra_rkb(uint32_t instr) 134 | { 135 | int a = GET_INSTR_RA(instr); 136 | int b = GET_INSTR_RB(instr); 137 | 138 | printf("r%d, ", a); 139 | if (b <= MAX_FUNC_REGS) 140 | printf("r%d", b); 141 | else 142 | printf("c[%d]", b-MAX_FUNC_REGS-1); 143 | printf("\n"); 144 | } 145 | 146 | static void dump_instr_ra_b(uint32_t instr) 147 | { 148 | int a = GET_INSTR_RA(instr); 149 | int b = GET_INSTR_RB(instr); 150 | 151 | printf("r%d, ", a); 152 | printf("%d\n", b); 153 | } 154 | 155 | static void dump_instr_ra_u(uint32_t instr) 156 | { 157 | int a = GET_INSTR_RA(instr); 158 | int u = GET_INSTR_RU(instr); 159 | 160 | printf("r%d, %d\n", a, u); 161 | } 162 | 163 | void fh_dump_bc_instr(struct fh_program *prog, int32_t addr, uint32_t instr) 164 | { 165 | UNUSED(prog); 166 | 167 | if (addr >= 0) 168 | printf("%5d ", addr); 169 | else 170 | printf(" "); 171 | //printf("%08x ", instr); 172 | enum fh_bc_opcode opc = GET_INSTR_OP(instr); 173 | switch (opc) { 174 | case OPC_RET: printf("ret "); dump_instr_ret(instr); return; 175 | case OPC_CALL: printf("call "); dump_instr_ra_b(instr); return; 176 | case OPC_CLOSURE: printf("closure "); dump_instr_ra_rkb(instr); return; 177 | case OPC_GETUPVAL: printf("getupval "); dump_instr_ra_up(instr); return; 178 | case OPC_SETUPVAL: printf("setupval "); dump_instr_up_rkb(instr); return; 179 | 180 | case OPC_ADD: printf("add "); dump_instr_ra_rkb_rkc(instr); return; 181 | case OPC_SUB: printf("sub "); dump_instr_ra_rkb_rkc(instr); return; 182 | case OPC_MUL: printf("mul "); dump_instr_ra_rkb_rkc(instr); return; 183 | case OPC_DIV: printf("div "); dump_instr_ra_rkb_rkc(instr); return; 184 | case OPC_MOD: printf("mod "); dump_instr_ra_rkb_rkc(instr); return; 185 | case OPC_NEG: printf("neg "); dump_instr_ra_rkb(instr); return; 186 | case OPC_MOV: printf("mov "); dump_instr_ra_rkb(instr); return; 187 | case OPC_NOT: printf("not "); dump_instr_ra_rkb(instr); return; 188 | 189 | case OPC_GETEL: printf("getel "); dump_instr_ra_rkb_rkc(instr); return; 190 | case OPC_SETEL: printf("setel "); dump_instr_ra_rkb_rkc(instr); return; 191 | case OPC_NEWARRAY: printf("newarray "); dump_instr_ra_u(instr); return; 192 | case OPC_NEWMAP: printf("newmap "); dump_instr_ra_u(instr); return; 193 | 194 | case OPC_CMP_EQ: printf("cmp.eq "); dump_instr_a_rkb_rkc(instr); return; 195 | case OPC_CMP_LT: printf("cmp.lt "); dump_instr_a_rkb_rkc(instr); return; 196 | case OPC_CMP_LE: printf("cmp.le "); dump_instr_a_rkb_rkc(instr); return; 197 | case OPC_TEST: printf("test "); dump_instr_a_rkb(instr); return; 198 | case OPC_JMP: printf("jmp "); dump_instr_jmp(instr, addr); return; 199 | 200 | case OPC_LDNULL: printf("ldnull r%d\n", GET_INSTR_RA(instr)); return; 201 | case OPC_LDC: printf("ldc r%d, c[%d]\n", GET_INSTR_RA(instr), GET_INSTR_RU(instr)); return; 202 | } 203 | 204 | printf("??? "); dump_instr_abc(instr); return; 205 | } 206 | 207 | static void dump_const(struct fh_program *prog, struct fh_value *c) 208 | { 209 | switch (c->type) { 210 | case FH_VAL_NULL: printf("null\n"); return; 211 | case FH_VAL_BOOL: printf("%s\n", (c->data.b) ? "true" : "false"); return; 212 | case FH_VAL_NUMBER: printf("%f\n", c->data.num); return; 213 | case FH_VAL_STRING: dump_string(fh_get_string(c)); printf("\n"); return; 214 | case FH_VAL_ARRAY: printf("\n", fh_get_array_len(c)); return; 215 | case FH_VAL_MAP: printf("\n", GET_OBJ_MAP(c->data.obj)->len, GET_OBJ_MAP(c->data.obj)->cap); return; 216 | case FH_VAL_UPVAL: printf("\n"); return; 217 | 218 | case FH_VAL_CLOSURE: 219 | { 220 | struct fh_closure *closure = GET_OBJ_CLOSURE(c->data.obj); 221 | if (closure->func_def->name) { 222 | printf("\n", (void *) closure, GET_OBJ_STRING_DATA(closure->func_def->name)); 223 | } else { 224 | printf("\n", (void *) closure, (void *) closure->func_def); 225 | } 226 | return; 227 | } 228 | 229 | case FH_VAL_FUNC_DEF: 230 | { 231 | struct fh_func_def *func_def = GET_OBJ_FUNC_DEF(c->data.obj); 232 | if (func_def->name) { 233 | printf("\n", GET_OBJ_STRING_DATA(func_def->name)); 234 | } else { 235 | printf("\n", c->data.obj); 236 | } 237 | return; 238 | } 239 | 240 | case FH_VAL_C_FUNC: 241 | { 242 | const char *name = fh_get_c_func_name(prog, c->data.c_func); 243 | if (! name) 244 | printf("\n"); 245 | else 246 | printf("\n", name); 247 | } 248 | return; 249 | } 250 | 251 | printf("\n", c->type); 252 | } 253 | 254 | static void dump_func_def(struct fh_program *prog, struct fh_func_def *func_def) 255 | { 256 | const char *func_name = fh_get_func_def_name(func_def); 257 | 258 | if (func_name) 259 | printf("; function %s(): %u parameters, %d regs\n", func_name, func_def->n_params, func_def->n_regs); 260 | else 261 | printf("; function at %p: %u parameters, %d regs\n", (void *) func_def, func_def->n_params, func_def->n_regs); 262 | 263 | struct fh_src_loc loc = fh_make_src_loc(0,0,0); 264 | const char *code_src_loc = func_def->code_src_loc; 265 | const char *code_src_loc_end = code_src_loc + func_def->code_src_loc_size; 266 | 267 | //for (int i = 0; i < func_def->code_src_loc_size; i++) printf("%02x ", (uint8_t) code_src_loc[i]); printf("\n"); 268 | 269 | for (int i = 0; i < func_def->code_size; i++) { 270 | if (code_src_loc) { 271 | code_src_loc = fh_decode_src_loc(code_src_loc, code_src_loc_end - code_src_loc, &loc, 1); 272 | printf("<%d> %4d:%-4d ", loc.file_id, loc.line, loc.col); 273 | } else { 274 | printf(" "); 275 | } 276 | fh_dump_bc_instr(prog, i, func_def->code[i]); 277 | } 278 | 279 | if (func_def->n_consts) { 280 | printf("; %d constants:\n", func_def->n_consts); 281 | for (int i = 0; i < func_def->n_consts; i++) { 282 | printf("c[%d] = ", i); 283 | dump_const(prog, &func_def->consts[i]); 284 | } 285 | } 286 | 287 | if (func_def->n_upvals) { 288 | printf("; %d upvals:\n", func_def->n_upvals); 289 | for (int i = 0; i < func_def->n_upvals; i++) { 290 | struct fh_upval_def *ud = &func_def->upvals[i]; 291 | printf("u[%d]: parent's ", i); 292 | if (ud->type == FH_UPVAL_TYPE_UPVAL) 293 | printf("u[%d]\n", ud->num); 294 | else 295 | printf("r%d\n", ud->num); 296 | } 297 | } 298 | 299 | printf("; ===================================================\n"); 300 | 301 | // dump child function definitions 302 | for (int i = 0; i < func_def->n_consts; i++) { 303 | if (func_def->consts[i].type == FH_VAL_FUNC_DEF) { 304 | dump_func_def(prog, GET_VAL_FUNC_DEF(&func_def->consts[i])); 305 | } 306 | } 307 | } 308 | 309 | void fh_dump_bytecode(struct fh_program *prog) 310 | { 311 | printf("; === BYTECODE ======================================\n"); 312 | int n_funcs = fh_get_num_global_funcs(prog); 313 | for (int i = 0; i < n_funcs; i++) { 314 | struct fh_closure *closure = fh_get_global_func_by_index(prog, i); 315 | dump_func_def(prog, closure->func_def); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/lib/fh.h: -------------------------------------------------------------------------------- 1 | /* fh.h */ 2 | 3 | #ifndef FH_H_FILE 4 | #define FH_H_FILE 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if defined(__GNUC__) 12 | #define FH_PRINTF_FORMAT(x,y) __attribute__((format (printf, (x), (y)))) 13 | #else 14 | #define FH_PRINTF_FORMAT(x,y) 15 | #endif 16 | 17 | struct fh_input; 18 | struct fh_program; 19 | struct fh_value; 20 | 21 | struct fh_input_funcs { 22 | struct fh_input *(*open)(struct fh_input *in, const char *filename); 23 | int (*read)(struct fh_input *in, char *line, int max_len); 24 | int (*close)(struct fh_input *in); 25 | }; 26 | 27 | enum fh_value_type { 28 | // non-object values (completely contained inside struct fh_value) 29 | FH_VAL_NULL, 30 | FH_VAL_BOOL, 31 | FH_VAL_NUMBER, 32 | FH_VAL_C_FUNC, 33 | 34 | #define FH_FIRST_OBJECT_VAL FH_VAL_STRING 35 | // objects 36 | FH_VAL_STRING, 37 | FH_VAL_ARRAY, 38 | FH_VAL_MAP, 39 | FH_VAL_UPVAL, 40 | FH_VAL_CLOSURE, 41 | FH_VAL_FUNC_DEF, 42 | }; 43 | 44 | typedef int (*fh_c_func)(struct fh_program *prog, struct fh_value *ret, struct fh_value *args, int n_args); 45 | 46 | struct fh_named_c_func { 47 | const char *name; 48 | fh_c_func func; 49 | }; 50 | 51 | struct fh_value { 52 | union { 53 | void *obj; 54 | fh_c_func c_func; 55 | double num; 56 | bool b; 57 | } data; 58 | enum fh_value_type type; 59 | }; 60 | 61 | struct fh_input *fh_open_input_file(const char *filename); 62 | struct fh_input *fh_open_input_string(const char *string); 63 | struct fh_input *fh_new_input(const char *filename, void *user_data, struct fh_input_funcs *funcs); 64 | void *fh_get_input_user_data(struct fh_input *in); 65 | const char *fh_get_input_filename(struct fh_input *in); 66 | struct fh_input *fh_open_input(struct fh_input *in, const char *filename); 67 | int fh_close_input(struct fh_input *in); 68 | int fh_read_input(struct fh_input *in, char *line, int max_len); 69 | 70 | struct fh_program *fh_new_program(void); 71 | void fh_free_program(struct fh_program *prog); 72 | void fh_set_gc_frequency(struct fh_program *prog, int frequency); 73 | int fh_add_c_func(struct fh_program *prog, const char *name, fh_c_func func); 74 | int fh_add_c_funcs(struct fh_program *prog, const struct fh_named_c_func *funcs, int n_funcs); 75 | int fh_compile_input(struct fh_program *prog, struct fh_input *in); 76 | int fh_compile_file(struct fh_program *prog, const char *filename); 77 | void fh_dump_bytecode(struct fh_program *prog); 78 | int fh_call_function(struct fh_program *prog, const char *func_name, struct fh_value *args, int n_args, struct fh_value *ret); 79 | 80 | const char *fh_get_error(struct fh_program *prog); 81 | int fh_set_error(struct fh_program *prog, const char *fmt, ...) FH_PRINTF_FORMAT(2,3); 82 | int fh_set_verror(struct fh_program *prog, const char *fmt, va_list ap); 83 | void fh_collect_garbage(struct fh_program *prog); 84 | 85 | bool fh_val_is_true(struct fh_value *val); 86 | bool fh_vals_are_equal(struct fh_value *v1, struct fh_value *v2); 87 | 88 | #define fh_is_null(v) ((v)->type == FH_VAL_NULL) 89 | #define fh_is_bool(v) ((v)->type == FH_VAL_BOOL) 90 | #define fh_is_number(v) ((v)->type == FH_VAL_NUMBER) 91 | #define fh_is_c_func(v) ((v)->type == FH_VAL_C_FUNC) 92 | #define fh_is_string(v) ((v)->type == FH_VAL_STRING) 93 | #define fh_is_closure(v) ((v)->type == FH_VAL_CLOSURE) 94 | #define fh_is_array(v) ((v)->type == FH_VAL_ARRAY) 95 | #define fh_is_map(v) ((v)->type == FH_VAL_MAP) 96 | 97 | #define fh_new_null() ((struct fh_value) { .type = FH_VAL_NULL }) 98 | 99 | #define fh_new_bool(bv) ((struct fh_value) { .type = FH_VAL_BOOL, .data = { .b = !!(bv) }}) 100 | #define fh_get_bool(v) ((v)->data.b) 101 | 102 | #define fh_new_c_func(f) ((struct fh_value) { .type = FH_VAL_C_FUNC, .data = { .c_func = (f) }}) 103 | #define fh_get_c_func(v) ((v)->data.c_func) 104 | 105 | #define fh_new_number(n) ((struct fh_value) { .type = FH_VAL_NUMBER, .data = { .num = (n) }}) 106 | #define fh_get_number(v) ((v)->data.num) 107 | 108 | struct fh_value fh_new_string(struct fh_program *prog, const char *str); 109 | struct fh_value fh_new_string_n(struct fh_program *prog, const char *str, size_t str_len); 110 | const char *fh_get_string(const struct fh_value *str); 111 | 112 | struct fh_value fh_new_array(struct fh_program *prog); 113 | int fh_get_array_len(const struct fh_value *arr); 114 | struct fh_value *fh_get_array_item(struct fh_value *arr, uint32_t index); 115 | struct fh_value *fh_grow_array(struct fh_program *prog, struct fh_value *val, uint32_t num_items); 116 | 117 | struct fh_value fh_new_map(struct fh_program *prog); 118 | int fh_alloc_map_len(struct fh_value *map, uint32_t len); 119 | int fh_next_map_key(struct fh_value *map, struct fh_value *key, struct fh_value *next_key); 120 | int fh_get_map_value(struct fh_value *map, struct fh_value *key, struct fh_value *val); 121 | int fh_add_map_entry(struct fh_program *prog, struct fh_value *map, struct fh_value *key, struct fh_value *val); 122 | int fh_delete_map_entry(struct fh_value *map, struct fh_value *key); 123 | 124 | #endif /* FH_H_FILE */ 125 | -------------------------------------------------------------------------------- /src/lib/fh_internal.h: -------------------------------------------------------------------------------- 1 | /* fh_internal.h */ 2 | 3 | #ifndef FH_INTERNAL_H_FILE 4 | #define FH_INTERNAL_H_FILE 5 | 6 | #include "fh.h" 7 | #include "stack.h" 8 | 9 | #define ARRAY_SIZE(arr) ((int)(sizeof(arr)/sizeof(arr[0]))) 10 | #define UNUSED(x) ((void)(x)) 11 | 12 | enum fh_op_assoc { 13 | FH_ASSOC_PREFIX = (1<<0), 14 | FH_ASSOC_LEFT = (1<<1), 15 | FH_ASSOC_RIGHT = (1<<2), 16 | }; 17 | 18 | struct fh_src_loc { 19 | uint16_t line; 20 | uint16_t col; 21 | uint16_t file_id; 22 | }; 23 | 24 | #define fh_make_src_loc(f, l, c) ((struct fh_src_loc) { .file_id = (f), .line = (l), .col = (c) }) 25 | 26 | struct fh_buffer { 27 | char *p; 28 | int size; 29 | int cap; 30 | }; 31 | 32 | struct fh_operator { 33 | uint32_t op; 34 | char name[4]; 35 | enum fh_op_assoc assoc; 36 | int32_t prec; 37 | }; 38 | 39 | struct fh_symtab { 40 | int num; 41 | int cap; 42 | int *entries; 43 | struct fh_buffer symbols; 44 | }; 45 | 46 | typedef int32_t fh_symbol_id; 47 | typedef int fh_string_id; 48 | 49 | struct fh_value; 50 | struct fh_vm; 51 | struct fh_func_def; 52 | struct fh_closure; 53 | struct fh_symtab; 54 | struct fh_tokenizer; 55 | struct fh_parser; 56 | struct fh_compiler; 57 | struct fh_program; 58 | struct fh_map; 59 | 60 | extern const struct fh_named_c_func fh_std_c_funcs[]; 61 | extern const int fh_std_c_funcs_len; 62 | 63 | uint32_t fh_hash(const void *data, size_t len); 64 | int fh_utf8_len(char *str, size_t str_size); 65 | void fh_dump_value(const struct fh_value *val); 66 | void fh_dump_string(const char *str); 67 | void fh_dump_map(struct fh_map *map); 68 | const void *fh_decode_src_loc(const void *encoded, int encoded_len, struct fh_src_loc *src_loc, int n_instr); 69 | int fh_encode_src_loc_change(struct fh_buffer *buf, struct fh_src_loc *old_loc, struct fh_src_loc *new_loc); 70 | struct fh_src_loc fh_get_addr_src_loc(struct fh_func_def *func_def, int addr); 71 | 72 | void fh_init_buffer(struct fh_buffer *buf); 73 | void fh_destroy_buffer(struct fh_buffer *buf); 74 | int fh_buf_grow(struct fh_buffer *buf, size_t add_size); 75 | int fh_buf_shrink_to_fit(struct fh_buffer *buf); 76 | int fh_buf_add_string(struct fh_buffer *buf, const void *str, size_t str_size); 77 | int fh_buf_add_byte(struct fh_buffer *buf, uint8_t c); 78 | int fh_buf_add_u16(struct fh_buffer *buf, uint16_t c); 79 | 80 | void fh_init_symtab(struct fh_symtab *s); 81 | void fh_destroy_symtab(struct fh_symtab *s); 82 | fh_symbol_id fh_add_symbol(struct fh_symtab *s, const void *symbol); 83 | fh_symbol_id fh_get_symbol_id(struct fh_symtab *s, const void *symbol); 84 | const char *fh_get_symbol_name(struct fh_symtab *s, fh_symbol_id id); 85 | 86 | struct fh_operator *fh_get_binary_op(const char *name); 87 | struct fh_operator *fh_get_prefix_op(const char *name); 88 | struct fh_operator *fh_get_op(const char *name); 89 | struct fh_operator *fh_get_op_by_id(uint32_t op); 90 | const char *fh_get_op_name(uint32_t op); 91 | 92 | void fh_free_program_objects(struct fh_program *prog); 93 | int fh_get_pin_state(struct fh_program *prog); 94 | void fh_restore_pin_state(struct fh_program *prog, int state); 95 | 96 | fh_c_func fh_get_c_func_by_name(struct fh_program *prog, const char *name); 97 | const char *fh_get_c_func_name(struct fh_program *prog, fh_c_func func); 98 | 99 | int fh_add_global_func(struct fh_program *prog, struct fh_closure *closure); 100 | int fh_get_num_global_funcs(struct fh_program *prog); 101 | struct fh_closure *fh_get_global_func_by_index(struct fh_program *prog, int index); 102 | struct fh_closure *fh_get_global_func_by_name(struct fh_program *prog, const char *name); 103 | 104 | #endif /* FH_INTERNAL_H_FILE */ 105 | -------------------------------------------------------------------------------- /src/lib/gc.c: -------------------------------------------------------------------------------- 1 | /* gc.c */ 2 | 3 | #include 4 | 5 | #include "fh_internal.h" 6 | #include "program.h" 7 | 8 | //#define COUNT_MEM_USAGE 9 | //#define DEBUG_GC 10 | 11 | #ifdef DEBUG_GC 12 | #define debug_log(x) printf(x) 13 | #define debug_log1(x,y) printf(x,y) 14 | #else 15 | #define debug_log(x) 16 | #define debug_log1(x,y) 17 | #define debug_obj(x, y) 18 | #endif 19 | 20 | struct fh_gc_state { 21 | union fh_object *container_list; 22 | #ifdef COUNT_MEM_USAGE 23 | size_t used; 24 | size_t released; 25 | #endif 26 | }; 27 | 28 | #ifdef DEBUG_GC 29 | /* NOTE: we can't access any objects referenced by the object being 30 | * dumped, as they might already be free */ 31 | static void debug_obj(const char *prefix, union fh_object *obj) 32 | { 33 | printf("%s object %p of type %d", prefix, (void *) obj, obj->header.type); 34 | switch (obj->header.type) { 35 | case FH_VAL_STRING: printf(" (string) "); fh_dump_string(GET_OBJ_STRING_DATA(obj)); printf("\n"); break; 36 | case FH_VAL_UPVAL: printf(" (upval)\n"); break; 37 | case FH_VAL_CLOSURE: printf(" (closure)\n"); break; 38 | case FH_VAL_FUNC_DEF: printf(" (func def)\n"); break; 39 | case FH_VAL_ARRAY: printf(" (array of len %d)\n", GET_OBJ_ARRAY(obj)->len); break; 40 | case FH_VAL_MAP: printf(" (map of len %d, cap %d)\n", GET_OBJ_MAP(obj)->len, GET_OBJ_MAP(obj)->cap); break; 41 | default: 42 | printf(" (UNEXPECTED TYPE)\n"); 43 | } 44 | } 45 | #endif 46 | 47 | #ifdef COUNT_MEM_USAGE 48 | static inline size_t obj_size(union fh_object *obj) 49 | { 50 | switch (obj->header.type) { 51 | case FH_VAL_STRING: return sizeof(struct fh_string) + GET_OBJ_STRING(obj)->size; 52 | case FH_VAL_CLOSURE: return sizeof(struct fh_closure) + sizeof(struct fh_upval *)*GET_OBJ_CLOSURE(obj)->n_upvals; 53 | case FH_VAL_UPVAL: return sizeof(struct fh_upval); 54 | case FH_VAL_ARRAY: return sizeof(struct fh_array) + sizeof(struct fh_value)*GET_OBJ_ARRAY(obj)->cap; 55 | case FH_VAL_MAP: return sizeof(struct fh_map) + sizeof(struct fh_map_entry)*GET_OBJ_MAP(obj)->cap; 56 | case FH_VAL_FUNC_DEF: 57 | { 58 | struct fh_func_def *f = GET_OBJ_FUNC_DEF(obj); 59 | return (sizeof(struct fh_func_def) 60 | + f->code_src_loc_size 61 | + f->code_size * sizeof(uint32_t) 62 | + f->n_consts * sizeof(struct fh_value) 63 | + f->n_upvals * sizeof(struct fh_upval_def)); 64 | } 65 | default: 66 | return 0; 67 | } 68 | } 69 | #define count_used(gc, obj) do { (gc)->used += obj_size((obj)); } while (0) 70 | #define count_released(gc, obj) do { (gc)->released += obj_size((obj)); } while (0) 71 | #else 72 | #define count_used(gc, obj) (void)0 73 | #define count_released(gc, obj) (void)0 74 | #endif 75 | 76 | static void sweep(struct fh_gc_state *gc, struct fh_program *prog) 77 | { 78 | UNUSED(gc); 79 | debug_log("***** sweeping\n"); 80 | union fh_object **objs = &prog->objects; 81 | union fh_object *cur; 82 | while ((cur = *objs) != NULL) { 83 | if (cur->header.gc_bits & (GC_BIT_MARK|GC_BIT_PIN)) { 84 | objs = &cur->header.next; 85 | cur->header.gc_bits &= ~GC_BIT_MARK; 86 | count_used(gc, cur); 87 | debug_obj("-> keeping", cur); 88 | } else { 89 | *objs = cur->header.next; 90 | count_released(gc, cur); 91 | debug_obj("-> FREEING", cur); 92 | fh_free_object(cur); 93 | } 94 | } 95 | } 96 | 97 | void fh_free_program_objects(struct fh_program *prog) 98 | { 99 | debug_log("***** FREEING ALL OBJECTS\n"); 100 | union fh_object *o = prog->objects; 101 | while (o) { 102 | union fh_object *next = o->header.next; 103 | debug_obj("-> FREEING", o); 104 | fh_free_object(o); 105 | o = next; 106 | } 107 | } 108 | 109 | #define MARK_VALUE(gc, v) do { if (VAL_IS_OBJECT(v)) MARK_OBJECT((gc), (union fh_object *)((v)->data.obj)); } while (0) 110 | #define MARK_OBJECT(gc, o) do { if (((o)->header.gc_bits&GC_BIT_MARK) == 0) mark_object((gc), (union fh_object *)(&(o)->header)); } while (0) 111 | 112 | static void mark_object(struct fh_gc_state *gc, union fh_object *obj) 113 | { 114 | debug_obj("-> marking", obj); 115 | 116 | GC_SET_BIT(obj, GC_BIT_MARK); 117 | switch (obj->header.type) { 118 | case FH_VAL_STRING: 119 | return; 120 | 121 | case FH_VAL_CLOSURE: 122 | GET_OBJ_CLOSURE(obj)->gc_next_container = gc->container_list; 123 | gc->container_list = obj; 124 | return; 125 | 126 | case FH_VAL_UPVAL: 127 | GET_OBJ_UPVAL(obj)->gc_next_container = gc->container_list; 128 | gc->container_list = obj; 129 | return; 130 | 131 | case FH_VAL_FUNC_DEF: 132 | GET_OBJ_FUNC_DEF(obj)->gc_next_container = gc->container_list; 133 | gc->container_list = obj; 134 | return; 135 | 136 | case FH_VAL_ARRAY: 137 | GET_OBJ_ARRAY(obj)->gc_next_container = gc->container_list; 138 | gc->container_list = obj; 139 | return; 140 | 141 | case FH_VAL_MAP: 142 | GET_OBJ_MAP(obj)->gc_next_container = gc->container_list; 143 | gc->container_list = obj; 144 | return; 145 | 146 | case FH_VAL_NULL: 147 | case FH_VAL_BOOL: 148 | case FH_VAL_NUMBER: 149 | case FH_VAL_C_FUNC: 150 | fprintf(stderr, "GC ERROR: marking non-object type %d\n", obj->header.type); 151 | return; 152 | } 153 | 154 | fprintf(stderr, "GC ERROR: marking invalid object type %d\n", obj->header.type); 155 | } 156 | 157 | static void mark_func_def_children(struct fh_gc_state *gc, struct fh_func_def *func_def) 158 | { 159 | if (func_def->name) 160 | MARK_OBJECT(gc, func_def->name); 161 | for (int i = 0; i < func_def->n_consts; i++) 162 | MARK_VALUE(gc, &func_def->consts[i]); 163 | } 164 | 165 | static void mark_closure_children(struct fh_gc_state *gc, struct fh_closure *closure) 166 | { 167 | MARK_OBJECT(gc, closure->func_def); 168 | for (int i = 0; i < closure->n_upvals; i++) 169 | MARK_OBJECT(gc, closure->upvals[i]); 170 | } 171 | 172 | static void mark_upval_children(struct fh_gc_state *gc, struct fh_upval *upval) 173 | { 174 | MARK_VALUE(gc, upval->val); 175 | if (upval->val != &upval->data.storage && upval->data.next != NULL) 176 | MARK_OBJECT(gc, upval->data.next); 177 | } 178 | 179 | static void mark_array_children(struct fh_gc_state *gc, struct fh_array *arr) 180 | { 181 | for (uint32_t i = 0; i < arr->len; i++) 182 | MARK_VALUE(gc, &arr->items[i]); 183 | } 184 | 185 | static void mark_map_children(struct fh_gc_state *gc, struct fh_map *map) 186 | { 187 | for (uint32_t i = 0; i < map->cap; i++) { 188 | if (map->entries[i].key.type != FH_VAL_NULL) { 189 | MARK_VALUE(gc, &map->entries[i].key); 190 | MARK_VALUE(gc, &map->entries[i].val); 191 | } 192 | } 193 | } 194 | 195 | static void mark_container_children(struct fh_gc_state *gc) 196 | { 197 | while (gc->container_list) { 198 | switch (gc->container_list->header.type) { 199 | case FH_VAL_CLOSURE: 200 | { 201 | struct fh_closure *c = GET_OBJ_CLOSURE(gc->container_list); 202 | gc->container_list = c->gc_next_container; 203 | mark_closure_children(gc, c); 204 | } 205 | continue; 206 | 207 | case FH_VAL_UPVAL: 208 | { 209 | struct fh_upval *uv = GET_OBJ_UPVAL(gc->container_list); 210 | gc->container_list = uv->gc_next_container; 211 | mark_upval_children(gc, uv); 212 | } 213 | continue; 214 | 215 | case FH_VAL_FUNC_DEF: 216 | { 217 | struct fh_func_def *f = GET_OBJ_FUNC_DEF(gc->container_list); 218 | gc->container_list = f->gc_next_container; 219 | mark_func_def_children(gc, f); 220 | } 221 | continue; 222 | 223 | case FH_VAL_ARRAY: 224 | { 225 | struct fh_array *a = GET_OBJ_ARRAY(gc->container_list); 226 | gc->container_list = a->gc_next_container; 227 | mark_array_children(gc, a); 228 | } 229 | continue; 230 | 231 | case FH_VAL_MAP: 232 | { 233 | struct fh_map *m = GET_OBJ_MAP(gc->container_list); 234 | gc->container_list = m->gc_next_container; 235 | mark_map_children(gc, m); 236 | } 237 | continue; 238 | 239 | case FH_VAL_NULL: 240 | case FH_VAL_BOOL: 241 | case FH_VAL_NUMBER: 242 | case FH_VAL_C_FUNC: 243 | case FH_VAL_STRING: 244 | fprintf(stderr, "GC ERROR: found non-container object (type %d)\n", gc->container_list->header.type); 245 | continue; 246 | } 247 | 248 | fprintf(stderr, "GC ERROR: found on invalid object of type %d\n", gc->container_list->header.type); 249 | } 250 | } 251 | 252 | static void mark_roots(struct fh_gc_state *gc, struct fh_program *prog) 253 | { 254 | // mark global functions 255 | debug_log("***** marking global functions\n"); 256 | stack_foreach(struct fh_closure *, *, pc, &prog->global_funcs) { 257 | MARK_OBJECT(gc, *pc); 258 | } 259 | 260 | // mark stack 261 | struct fh_vm_call_frame *cur_frame = call_frame_stack_top(&prog->vm.call_stack); 262 | if (cur_frame) { 263 | int stack_size = cur_frame->base + ((cur_frame->closure) ? cur_frame->closure->func_def->n_regs : 0); 264 | struct fh_value *stack = prog->vm.stack; 265 | debug_log1("***** marking %d stack values\n", stack_size); 266 | for (int i = 0; i < stack_size; i++) 267 | MARK_VALUE(gc, &stack[i]); 268 | } 269 | 270 | // mark open upvals 271 | debug_log("***** marking first open upval\n"); 272 | if (prog->vm.open_upvals) 273 | MARK_OBJECT(gc, prog->vm.open_upvals); 274 | 275 | // mark pinned values 276 | debug_log1("***** marking %d pinned values\n", p_object_stack_size(&prog->pinned_objs)); 277 | stack_foreach(union fh_object *, *, o, &prog->pinned_objs) { 278 | MARK_OBJECT(gc, *o); 279 | } 280 | 281 | // mark C values 282 | debug_log1("***** marking %d C tmp values\n", value_stack_size(&prog->c_vals)); 283 | stack_foreach(struct fh_value, *, v, &prog->c_vals) { 284 | MARK_VALUE(gc, v); 285 | } 286 | } 287 | 288 | static void mark(struct fh_gc_state *gc, struct fh_program *prog) 289 | { 290 | mark_roots(gc, prog); 291 | 292 | debug_log("***** marking container children\n"); 293 | mark_container_children(gc); 294 | } 295 | 296 | void fh_collect_garbage(struct fh_program *prog) 297 | { 298 | struct fh_gc_state gc = { 299 | .container_list = NULL, 300 | }; 301 | 302 | debug_log("== STARTING GC ==================\n"); 303 | mark(&gc, prog); 304 | sweep(&gc, prog); 305 | prog->n_created_objs_since_last_gc = 0; 306 | debug_log("== GC DONE ======================\n"); 307 | 308 | #ifdef COUNT_MEM_USAGE 309 | printf("gc used: %zu\n", gc.used); 310 | printf("gc released: %zu\n", gc.released); 311 | #endif 312 | } 313 | -------------------------------------------------------------------------------- /src/lib/input.c: -------------------------------------------------------------------------------- 1 | /* input.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fh.h" 9 | 10 | struct fh_input { 11 | void *user_data; 12 | struct fh_input_funcs *funcs; 13 | }; 14 | 15 | struct fh_input *fh_new_input(const char *filename, void *user_data, struct fh_input_funcs *funcs) 16 | { 17 | struct fh_input *in = malloc(sizeof(struct fh_input) + strlen(filename) + 1); 18 | if (! in) 19 | return NULL; 20 | in->user_data = user_data; 21 | in->funcs = funcs; 22 | strcpy((char *)in + sizeof(struct fh_input), filename); 23 | return in; 24 | } 25 | 26 | const char *fh_get_input_filename(struct fh_input *in) 27 | { 28 | return (char *)in + sizeof(struct fh_input);; 29 | } 30 | 31 | void *fh_get_input_user_data(struct fh_input *in) 32 | { 33 | return in->user_data; 34 | } 35 | 36 | struct fh_input *fh_open_input(struct fh_input *in, const char *filename) 37 | { 38 | /* 39 | * If 'filename' is not an absolute path, base it on the directory 40 | * of the parent input 41 | */ 42 | if (filename[0] != '/' && filename[0] != '\\') { 43 | const char *base_filename = fh_get_input_filename(in); 44 | const char *last_slash = strrchr(base_filename, '/'); 45 | if (! last_slash) 46 | last_slash = strrchr(base_filename, '\\'); 47 | if (last_slash) { 48 | size_t base_len = last_slash + 1 - base_filename; 49 | 50 | char path[256]; 51 | if (base_len + strlen(filename) + 1 > sizeof(path)) 52 | return NULL; // path is too big 53 | memcpy(path, base_filename, base_len); 54 | strcpy(path + base_len, filename); 55 | return in->funcs->open(in, path); 56 | } 57 | } 58 | return in->funcs->open(in, filename); 59 | } 60 | 61 | int fh_close_input(struct fh_input *in) 62 | { 63 | int ret = in->funcs->close(in); 64 | free(in); 65 | return ret; 66 | } 67 | 68 | int fh_read_input(struct fh_input *in, char *line, int max_len) 69 | { 70 | return in->funcs->read(in, line, max_len); 71 | } 72 | 73 | /* ======================================= */ 74 | /* === file input ======================== */ 75 | 76 | static int file_read(struct fh_input *in, char *line, int max_len) 77 | { 78 | FILE *f = in->user_data; 79 | 80 | size_t ret = fread(line, 1, max_len, f); 81 | if (ret == 0) { 82 | if (ferror(f) || feof(f)) 83 | return -1; 84 | } 85 | return ret; 86 | } 87 | 88 | static int file_close(struct fh_input *in) 89 | { 90 | FILE *f = in->user_data; 91 | 92 | return fclose(f); 93 | } 94 | 95 | struct fh_input *file_open(struct fh_input *in, const char *filename) 96 | { 97 | (void)in; 98 | return fh_open_input_file(filename); 99 | } 100 | 101 | struct fh_input *fh_open_input_file(const char *filename) 102 | { 103 | static struct fh_input_funcs file_input_funcs = { 104 | .open = file_open, 105 | .read = file_read, 106 | .close = file_close, 107 | }; 108 | 109 | FILE *f = fopen(filename, "rb"); 110 | if (! f) 111 | return NULL; 112 | 113 | struct fh_input *in = fh_new_input(filename, f, &file_input_funcs); 114 | if (! in) { 115 | fclose(f); 116 | return NULL; 117 | } 118 | return in; 119 | } 120 | 121 | /* ======================================= */ 122 | /* === string input ====================== */ 123 | 124 | struct string_input_data { 125 | char *data; 126 | int len; 127 | }; 128 | 129 | static int string_read(struct fh_input *in, char *line, int max_len) 130 | { 131 | struct string_input_data *str = in->user_data; 132 | 133 | if (str->len == 0) 134 | return -1; 135 | 136 | int len = (max_len < str->len) ? max_len : str->len; 137 | memcpy(line, str->data, len); 138 | str->data += len; 139 | str->len -= len; 140 | return len; 141 | } 142 | 143 | static int string_close(struct fh_input *in) 144 | { 145 | free(in->user_data); 146 | return 0; 147 | } 148 | 149 | struct fh_input *string_open(struct fh_input *in, const char *filename) 150 | { 151 | (void)in; 152 | return fh_open_input_file(filename); 153 | } 154 | 155 | struct fh_input *fh_open_input_string(const char *input) 156 | { 157 | static struct fh_input_funcs string_input_funcs = { 158 | .open = string_open, 159 | .read = string_read, 160 | .close = string_close, 161 | }; 162 | 163 | size_t input_len = strlen(input); 164 | if (input_len > INT_MAX) 165 | return NULL; 166 | struct string_input_data *str = malloc(sizeof(struct string_input_data) + input_len + 1); 167 | if (! str) 168 | return NULL; 169 | str->data = (char *)str + sizeof(struct string_input_data); 170 | str->len = input_len; 171 | memcpy(str->data, input, input_len); 172 | 173 | struct fh_input *in = fh_new_input("(string)", str, &string_input_funcs); 174 | if (! in) { 175 | free(str); 176 | return NULL; 177 | } 178 | return in; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /src/lib/map.c: -------------------------------------------------------------------------------- 1 | /* map.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "program.h" 7 | #include "value.h" 8 | 9 | #define OCCUPIED(e) ((e)->key.type != FH_VAL_NULL) 10 | 11 | static uint32_t val_hash(struct fh_value *val, uint32_t cap) 12 | { 13 | if (val->type == FH_VAL_STRING) 14 | return GET_VAL_STRING(val)->hash & (cap-1); 15 | 16 | size_t len; 17 | switch (val->type) { 18 | case FH_VAL_NULL: len = 0; break; 19 | case FH_VAL_BOOL: len = sizeof(bool); break; 20 | case FH_VAL_NUMBER: len = sizeof(double); break; 21 | case FH_VAL_C_FUNC: len = sizeof(fh_c_func); break; 22 | default: len = sizeof(void *); break; 23 | } 24 | uint32_t h = fh_hash((unsigned char *) &val->data, len); 25 | //printf("hash(p, %5zu) returns %08x --> pos=%d\n", len, h, h&(cap-1)); 26 | return h & (cap - 1); 27 | } 28 | 29 | 30 | static uint32_t find_slot(struct fh_map_entry *entries, uint32_t cap, struct fh_value *key) 31 | { 32 | uint32_t i = val_hash(key, cap); 33 | while (OCCUPIED(&entries[i]) && ! fh_vals_are_equal(key, &entries[i].key)) 34 | i = (i+1) & (cap-1); 35 | return i; 36 | } 37 | 38 | static uint32_t insert(struct fh_map_entry *entries, uint32_t cap, struct fh_value *key, struct fh_value *val) 39 | { 40 | uint32_t i = find_slot(entries, cap, key); 41 | //printf("-> inserting k="); fh_dump_value(key); 42 | //printf("; val="); fh_dump_value(val); 43 | //printf(" at position %u (occupied=%d)\n", i, OCCUPIED(&entries[i])); 44 | if (! OCCUPIED(&entries[i])) 45 | entries[i].key = *key; 46 | entries[i].val = *val; 47 | return i; 48 | } 49 | 50 | static int rebuild(struct fh_map *map, uint32_t cap) 51 | { 52 | struct fh_map_entry *entries = malloc(cap * sizeof(struct fh_map_entry)); 53 | if (! entries) 54 | return -1; 55 | memset(entries, 0, cap * sizeof(struct fh_map_entry)); 56 | 57 | //printf("rebuilding map with cap %u\n", cap); 58 | for (uint32_t i = 0; i < map->cap; i++) { 59 | struct fh_map_entry *e = &map->entries[i]; 60 | if (e->key.type != FH_VAL_NULL) 61 | insert(entries, cap, &e->key, &e->val); 62 | } 63 | //printf("done rebuilding\n"); 64 | 65 | free(map->entries); 66 | map->entries = entries; 67 | map->cap = cap; 68 | return 0; 69 | } 70 | 71 | void fh_dump_map(struct fh_map *map) 72 | { 73 | for (uint32_t i = 0; i < map->cap; i++) { 74 | struct fh_map_entry *e = &map->entries[i]; 75 | printf("[%3u] ", i); 76 | if (e->key.type == FH_VAL_NULL) { 77 | printf("--\n"); 78 | } else { 79 | fh_dump_value(&e->key); 80 | printf(" -> "); 81 | fh_dump_value(&e->val); 82 | printf("\n"); 83 | } 84 | } 85 | } 86 | 87 | int fh_get_map_object_value(struct fh_map *map, struct fh_value *key, struct fh_value *val) 88 | { 89 | if (map->cap == 0) 90 | return -1; 91 | 92 | uint32_t i = find_slot(map->entries, map->cap, key); 93 | if (! OCCUPIED(&map->entries[i])) 94 | return -1; 95 | *val = map->entries[i].val; 96 | return 0; 97 | } 98 | 99 | int fh_add_map_object_entry(struct fh_program *prog, struct fh_map *map, struct fh_value *key, struct fh_value *val) 100 | { 101 | if (key->type == FH_VAL_NULL) { 102 | fh_set_error(prog, "can't insert null key in map"); 103 | return -1; 104 | } 105 | 106 | uint32_t i = 0; 107 | if (map->cap > 0) { 108 | i = find_slot(map->entries, map->cap, key); 109 | if (OCCUPIED(&map->entries[i])) { 110 | map->entries[i].val = *val; 111 | return 0; 112 | } 113 | } 114 | 115 | if (map->cap == 0 || map->len+1 > map->cap/2) { 116 | if (rebuild(map, (map->cap == 0) ? 8 : 2*map->cap) < 0) { 117 | fh_set_error(prog, "out of memory"); 118 | return -1; 119 | } 120 | i = find_slot(map->entries, map->cap, key); 121 | } 122 | map->len++; 123 | map->entries[i].key = *key; 124 | map->entries[i].val = *val; 125 | return 0; 126 | } 127 | 128 | int fh_next_map_object_key(struct fh_map *map, struct fh_value *key, struct fh_value *next_key) 129 | { 130 | uint32_t next_i; 131 | if (key->type == FH_VAL_NULL || map->cap == 0) { 132 | next_i = 0; 133 | } else { 134 | next_i = find_slot(map->entries, map->cap, key); 135 | if (OCCUPIED(&map->entries[next_i])) 136 | next_i++; 137 | } 138 | 139 | for (uint32_t i = next_i; i < map->cap; i++) { 140 | if (OCCUPIED(&map->entries[i])) { 141 | *next_key = map->entries[i].key; 142 | return 0; 143 | } 144 | } 145 | *next_key = fh_new_null(); 146 | return 0; 147 | } 148 | 149 | int fh_delete_map_object_entry(struct fh_map *map, struct fh_value *key) 150 | { 151 | if (map->cap == 0) 152 | return -1; 153 | 154 | //printf("--------------------------\n"); 155 | //printf("DELETING "); fh_dump_value(key); 156 | //printf("\nBEFORE:\n"); fh_dump_map(map); 157 | 158 | uint32_t i = find_slot(map->entries, map->cap, key); 159 | if (! OCCUPIED(&map->entries[i])) 160 | return -1; 161 | uint32_t j = i; 162 | while (true) { 163 | map->entries[i].key.type = FH_VAL_NULL; 164 | start: 165 | j = (j+1) & (map->cap-1); 166 | if (! OCCUPIED(&map->entries[j])) 167 | break; 168 | uint32_t k = val_hash(&map->entries[j].key, map->cap); 169 | if ((i < j) ? (ientries[i] = map->entries[j]; 172 | i = j; 173 | } 174 | map->len--; 175 | 176 | //printf("AFTER:\n"); fh_dump_map(map); 177 | //printf("--------------------------\n"); 178 | return 0; 179 | } 180 | 181 | int fh_alloc_map_object_len(struct fh_map *map, uint32_t len) 182 | { 183 | // round len up to the nearest power of 2 184 | len--; 185 | len |= len >> 1; 186 | len |= len >> 2; 187 | len |= len >> 4; 188 | len |= len >> 8; 189 | len |= len >> 16; 190 | len++; 191 | 192 | if (len < map->len) 193 | return -1; 194 | return rebuild(map, 2*len); 195 | } 196 | 197 | /* value functions */ 198 | 199 | int fh_alloc_map_len(struct fh_value *map, uint32_t len) 200 | { 201 | struct fh_map *m = GET_VAL_MAP(map); 202 | if (! m) 203 | return -1; 204 | return fh_alloc_map_object_len(m, len); 205 | } 206 | 207 | int fh_delete_map_entry(struct fh_value *map, struct fh_value *key) 208 | { 209 | struct fh_map *m = GET_VAL_MAP(map); 210 | if (! m) 211 | return -1; 212 | return fh_delete_map_object_entry(m, key); 213 | } 214 | 215 | int fh_next_map_key(struct fh_value *map, struct fh_value *key, struct fh_value *next_key) 216 | { 217 | struct fh_map *m = GET_VAL_MAP(map); 218 | if (! m) 219 | return -1; 220 | return fh_next_map_object_key(m, key, next_key); 221 | } 222 | 223 | int fh_get_map_value(struct fh_value *map, struct fh_value *key, struct fh_value *val) 224 | { 225 | struct fh_map *m = GET_VAL_MAP(map); 226 | if (! m) 227 | return -1; 228 | return fh_get_map_object_value(m, key, val); 229 | } 230 | 231 | int fh_add_map_entry(struct fh_program *prog, struct fh_value *map, struct fh_value *key, struct fh_value *val) 232 | { 233 | struct fh_map *m = GET_VAL_MAP(map); 234 | if (! m) 235 | return -1; 236 | return fh_add_map_object_entry(prog, m, key, val); 237 | } 238 | -------------------------------------------------------------------------------- /src/lib/operator.c: -------------------------------------------------------------------------------- 1 | /* operator.h */ 2 | 3 | #include 4 | #include 5 | 6 | #include "fh_internal.h" 7 | #include "ast.h" 8 | 9 | static struct fh_operator ops[] = { 10 | { '=', "=", FH_ASSOC_RIGHT, 10 }, 11 | 12 | { AST_OP_OR, "||", FH_ASSOC_LEFT, 20 }, 13 | { AST_OP_AND, "&&", FH_ASSOC_LEFT, 30 }, 14 | 15 | { '|', "|", FH_ASSOC_LEFT, 40 }, 16 | { '&', "&", FH_ASSOC_LEFT, 50 }, 17 | 18 | { AST_OP_EQ, "==", FH_ASSOC_LEFT, 60 }, 19 | { AST_OP_NEQ, "!=", FH_ASSOC_LEFT, 60 }, 20 | { '<', "<", FH_ASSOC_LEFT, 70 }, 21 | { '>', ">", FH_ASSOC_LEFT, 70 }, 22 | { AST_OP_LE, "<=", FH_ASSOC_LEFT, 70 }, 23 | { AST_OP_GE, ">=", FH_ASSOC_LEFT, 70 }, 24 | 25 | { '+', "+", FH_ASSOC_LEFT, 80 }, 26 | { '-', "-", FH_ASSOC_LEFT, 80 }, 27 | { '*', "*", FH_ASSOC_LEFT, 90 }, 28 | { '/', "/", FH_ASSOC_LEFT, 90 }, 29 | { '%', "%", FH_ASSOC_LEFT, 90 }, 30 | 31 | { AST_OP_UNM, "-", FH_ASSOC_PREFIX, 100 }, 32 | { '!', "!", FH_ASSOC_PREFIX, 100 }, 33 | 34 | { '^', "^", FH_ASSOC_RIGHT, 110 }, 35 | }; 36 | 37 | static struct fh_operator *find_op(const char *name, unsigned int assoc_mask) 38 | { 39 | for (int i = 0; i < ARRAY_SIZE(ops); i++) { 40 | if ((ops[i].assoc & assoc_mask) != 0 && strcmp(ops[i].name, name) == 0) 41 | return &ops[i]; 42 | } 43 | return NULL; 44 | } 45 | 46 | struct fh_operator *fh_get_binary_op(const char *name) 47 | { 48 | return find_op(name, FH_ASSOC_LEFT|FH_ASSOC_RIGHT); 49 | } 50 | 51 | struct fh_operator *fh_get_prefix_op(const char *name) 52 | { 53 | return find_op(name, FH_ASSOC_PREFIX); 54 | } 55 | 56 | struct fh_operator *fh_get_op(const char *name) 57 | { 58 | struct fh_operator *op; 59 | if ((op = fh_get_prefix_op(name)) != NULL) 60 | return op; 61 | if ((op = fh_get_binary_op(name)) != NULL) 62 | return op; 63 | return NULL; 64 | } 65 | 66 | struct fh_operator *fh_get_op_by_id(uint32_t op) 67 | { 68 | for (int i = 0; i < ARRAY_SIZE(ops); i++) 69 | if (ops[i].op == op) 70 | return &ops[i]; 71 | return NULL; 72 | } 73 | 74 | const char *fh_get_op_name(uint32_t op) 75 | { 76 | struct fh_operator *opr = fh_get_op_by_id(op); 77 | if (opr == NULL) 78 | return NULL; 79 | return opr->name; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/lib/parser.h: -------------------------------------------------------------------------------- 1 | /* parser.h */ 2 | 3 | #ifndef PARSER_H_FILE 4 | #define PARSER_H_FILE 5 | 6 | #include "fh_internal.h" 7 | #include "tokenizer.h" 8 | 9 | struct fh_ast; 10 | 11 | struct fh_parser { 12 | struct fh_program *prog; 13 | struct fh_tokenizer *tokenizer; 14 | struct fh_ast *ast; 15 | struct fh_buffer tmp_buf; 16 | struct fh_src_loc last_loc; 17 | int has_saved_tok; 18 | struct fh_token saved_tok; 19 | }; 20 | 21 | void fh_init_parser(struct fh_parser *p, struct fh_program *prog); 22 | void fh_destroy_parser(struct fh_parser *p); 23 | int fh_parse(struct fh_parser *p, struct fh_ast *ast, struct fh_input *in); 24 | void *fh_parse_error(struct fh_parser *p, struct fh_src_loc loc, char *fmt, ...) FH_PRINTF_FORMAT(3, 4); 25 | void *fh_parse_error_oom(struct fh_parser *p, struct fh_src_loc loc); 26 | void *fh_parse_error_expected(struct fh_parser *p, struct fh_src_loc loc, char *expected); 27 | 28 | #endif /* PARSER_H_FILE */ 29 | -------------------------------------------------------------------------------- /src/lib/parser_stacks.h: -------------------------------------------------------------------------------- 1 | /* parser_stacks.h */ 2 | 3 | #ifndef PARSER_STACKS_H_FILE 4 | #define PARSER_STACKS_H_FILE 5 | 6 | struct opr_info { 7 | struct opr_info *next; 8 | struct fh_operator *op; 9 | struct fh_src_loc loc; 10 | }; 11 | 12 | static void opr_stack_free(struct opr_info *oprs) 13 | { 14 | while (oprs != NULL) { 15 | struct opr_info *next = oprs->next; 16 | free(oprs); 17 | oprs = next; 18 | } 19 | } 20 | 21 | static int opr_stack_size(struct opr_info **oprs) 22 | { 23 | int n = 0; 24 | for (struct opr_info *o = *oprs; o != NULL; o = o->next) 25 | n++; 26 | return n; 27 | } 28 | 29 | static int opr_stack_top(struct opr_info **oprs, struct fh_operator **op, struct fh_src_loc *loc) 30 | { 31 | if (*oprs == NULL) 32 | return -1; 33 | struct opr_info *o = *oprs; 34 | if (op) 35 | *op = o->op; 36 | if (loc) 37 | *loc = o->loc; 38 | return 0; 39 | } 40 | 41 | static int pop_opr(struct opr_info **oprs) 42 | { 43 | if (*oprs == NULL) 44 | return -1; 45 | struct opr_info *o = *oprs; 46 | *oprs = o->next; 47 | free(o); 48 | return 0; 49 | } 50 | 51 | static int push_opr(struct opr_info **oprs, struct fh_operator *op, struct fh_src_loc loc) 52 | { 53 | struct opr_info *o = malloc(sizeof(struct opr_info)); 54 | if (! o) 55 | return -1; 56 | o->op = op; 57 | o->loc = loc; 58 | o->next = *oprs; 59 | *oprs = o; 60 | return 0; 61 | } 62 | 63 | static int opn_stack_size(struct fh_p_expr **opns) 64 | { 65 | int n = 0; 66 | for (struct fh_p_expr *e = *opns; e != NULL; e = e->next) 67 | n++; 68 | return n; 69 | } 70 | 71 | static struct fh_p_expr *pop_opn(struct fh_p_expr **opns) 72 | { 73 | if (*opns == NULL) 74 | return NULL; 75 | struct fh_p_expr *e = *opns; 76 | *opns = e->next; 77 | e->next = NULL; 78 | return e; 79 | } 80 | 81 | static void push_opn(struct fh_p_expr **opns, struct fh_p_expr *expr) 82 | { 83 | expr->next = *opns; 84 | *opns = expr; 85 | } 86 | 87 | #endif /* PARSER_STACKS_H_FILE */ 88 | -------------------------------------------------------------------------------- /src/lib/program.c: -------------------------------------------------------------------------------- 1 | /* fh.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "program.h" 7 | 8 | struct fh_program *fh_new_program(void) 9 | { 10 | struct fh_program *prog = malloc(sizeof(struct fh_program)); 11 | if (! prog) 12 | return NULL; 13 | prog->gc_frequency = 100; // collect every `gc_frequency` object creations 14 | prog->n_created_objs_since_last_gc = 0; 15 | prog->objects = NULL; 16 | prog->null_value.type = FH_VAL_NULL; 17 | prog->last_error_msg[0] = '\0'; 18 | fh_init_symtab(&prog->src_file_names); 19 | p_closure_stack_init(&prog->global_funcs); 20 | named_c_func_stack_init(&prog->c_funcs); 21 | 22 | fh_init_vm(&prog->vm, prog); 23 | fh_init_parser(&prog->parser, prog); 24 | fh_init_compiler(&prog->compiler, prog); 25 | value_stack_init(&prog->c_vals); 26 | p_object_stack_init(&prog->pinned_objs); 27 | 28 | if (fh_add_c_funcs(prog, fh_std_c_funcs, fh_std_c_funcs_len) < 0) 29 | goto err; 30 | 31 | return prog; 32 | 33 | err: 34 | fh_destroy_symtab(&prog->src_file_names); 35 | p_closure_stack_free(&prog->global_funcs); 36 | p_object_stack_free(&prog->pinned_objs); 37 | named_c_func_stack_free(&prog->c_funcs); 38 | value_stack_free(&prog->c_vals); 39 | fh_destroy_compiler(&prog->compiler); 40 | fh_destroy_parser(&prog->parser); 41 | free(prog); 42 | return NULL; 43 | } 44 | 45 | void fh_free_program(struct fh_program *prog) 46 | { 47 | if (p_object_stack_size(&prog->pinned_objs) > 0) 48 | fprintf(stderr, "*** WARNING: %d pinned object(s) on exit\n", p_object_stack_size(&prog->pinned_objs)); 49 | 50 | fh_destroy_symtab(&prog->src_file_names); 51 | p_closure_stack_free(&prog->global_funcs); 52 | p_object_stack_free(&prog->pinned_objs); 53 | named_c_func_stack_free(&prog->c_funcs); 54 | value_stack_free(&prog->c_vals); 55 | 56 | fh_collect_garbage(prog); 57 | fh_free_program_objects(prog); 58 | 59 | fh_destroy_vm(&prog->vm); 60 | fh_destroy_compiler(&prog->compiler); 61 | fh_destroy_parser(&prog->parser); 62 | 63 | free(prog); 64 | } 65 | 66 | void fh_set_gc_frequency(struct fh_program *prog, int frequency) 67 | { 68 | prog->gc_frequency = frequency; 69 | } 70 | 71 | const char *fh_get_error(struct fh_program *prog) 72 | { 73 | if (prog->vm.last_error_addr < 0) 74 | return prog->last_error_msg; 75 | char tmp[512]; 76 | struct fh_src_loc *loc = &prog->vm.last_error_loc; 77 | snprintf(tmp, sizeof(tmp), "%s:%d:%d: %s", 78 | fh_get_symbol_name(&prog->src_file_names, loc->file_id), 79 | loc->line, loc->col, prog->last_error_msg); 80 | size_t size = (sizeof(tmp) > sizeof(prog->last_error_msg)) ? sizeof(prog->last_error_msg) : sizeof(tmp); 81 | memcpy(prog->last_error_msg, tmp, size); 82 | return prog->last_error_msg; 83 | } 84 | 85 | int fh_set_error(struct fh_program *prog, const char *fmt, ...) 86 | { 87 | va_list ap; 88 | va_start(ap, fmt); 89 | vsnprintf(prog->last_error_msg, sizeof(prog->last_error_msg), fmt, ap); 90 | va_end(ap); 91 | prog->vm.last_error_addr = -1; 92 | return -1; 93 | } 94 | 95 | int fh_set_verror(struct fh_program *prog, const char *fmt, va_list ap) 96 | { 97 | vsnprintf(prog->last_error_msg, sizeof(prog->last_error_msg), fmt, ap); 98 | prog->vm.last_error_addr = -1; 99 | return -1; 100 | } 101 | 102 | int fh_get_pin_state(struct fh_program *prog) 103 | { 104 | return p_object_stack_size(&prog->pinned_objs); 105 | } 106 | 107 | void fh_restore_pin_state(struct fh_program *prog, int state) 108 | { 109 | if (state > p_object_stack_size(&prog->pinned_objs)) { 110 | fprintf(stderr, "ERROR: invalid pin state\n"); 111 | return; 112 | } 113 | p_object_stack_set_size(&prog->pinned_objs, state); 114 | } 115 | 116 | int fh_add_c_func(struct fh_program *prog, const char *name, fh_c_func func) 117 | { 118 | struct named_c_func *cf = named_c_func_stack_push(&prog->c_funcs, NULL); 119 | if (! cf) 120 | return fh_set_error(prog, "out of memory"); 121 | cf->name = name; 122 | cf->func = func; 123 | return 0; 124 | } 125 | 126 | int fh_add_c_funcs(struct fh_program *prog, const struct fh_named_c_func *funcs, int n_funcs) 127 | { 128 | for (int i = 0; i < n_funcs; i++) 129 | if (fh_add_c_func(prog, funcs[i].name, funcs[i].func) < 0) 130 | return -1; 131 | return 0; 132 | } 133 | 134 | const char *fh_get_c_func_name(struct fh_program *prog, fh_c_func func) 135 | { 136 | stack_foreach(struct named_c_func, *, c_func, &prog->c_funcs) { 137 | if (c_func->func == func) 138 | return c_func->name; 139 | } 140 | return NULL; 141 | } 142 | 143 | fh_c_func fh_get_c_func_by_name(struct fh_program *prog, const char *name) 144 | { 145 | stack_foreach(struct named_c_func, *, c_func, &prog->c_funcs) { 146 | if (strcmp(name, c_func->name) == 0) 147 | return c_func->func; 148 | } 149 | return NULL; 150 | } 151 | 152 | int fh_add_global_func(struct fh_program *prog, struct fh_closure *closure) 153 | { 154 | stack_foreach(struct fh_closure *, *, pc, &prog->global_funcs) { 155 | struct fh_closure *cur_closure = *pc; 156 | if (closure->func_def->name != NULL && strcmp(GET_OBJ_STRING_DATA(closure->func_def->name), GET_OBJ_STRING_DATA(cur_closure->func_def->name)) == 0) { 157 | *pc = closure; // replace with new function 158 | return 0; 159 | } 160 | } 161 | if (! p_closure_stack_push(&prog->global_funcs, &closure)) 162 | return fh_set_error(prog, "out of memory"); 163 | return 0; 164 | } 165 | 166 | int fh_get_num_global_funcs(struct fh_program *prog) 167 | { 168 | return p_closure_stack_size(&prog->global_funcs); 169 | } 170 | 171 | struct fh_closure *fh_get_global_func_by_index(struct fh_program *prog, int index) 172 | { 173 | struct fh_closure **pc = p_closure_stack_item(&prog->global_funcs, index); 174 | if (! pc) 175 | return NULL; 176 | return *pc; 177 | } 178 | 179 | struct fh_closure *fh_get_global_func_by_name(struct fh_program *prog, const char *name) 180 | { 181 | stack_foreach(struct fh_closure *, *, pc, &prog->global_funcs) { 182 | struct fh_closure *closure = *pc; 183 | if (closure->func_def->name != NULL && strcmp(GET_OBJ_STRING_DATA(closure->func_def->name), name) == 0) 184 | return closure; 185 | } 186 | return NULL; 187 | } 188 | 189 | int fh_compile_input(struct fh_program *prog, struct fh_input *in) 190 | { 191 | struct fh_ast *ast = fh_new_ast(&prog->src_file_names); 192 | if (! ast) { 193 | fh_close_input(in); 194 | fh_set_error(prog, "out of memory for AST"); 195 | return -1; 196 | } 197 | if (fh_parse(&prog->parser, ast, in) < 0) 198 | goto err; 199 | //fh_dump_ast(ast); 200 | 201 | if (fh_compile(&prog->compiler, ast) < 0) 202 | goto err; 203 | 204 | fh_free_ast(ast); 205 | return 0; 206 | 207 | err: 208 | if (ast) 209 | fh_free_ast(ast); 210 | return -1; 211 | } 212 | 213 | int fh_compile_file(struct fh_program *prog, const char *filename) 214 | { 215 | struct fh_input *in = fh_open_input_file(filename); 216 | if (! in) { 217 | fh_set_error(prog, "can't open '%s'", filename); 218 | return -1; 219 | } 220 | return fh_compile_input(prog, in); 221 | } 222 | 223 | int fh_call_function(struct fh_program *prog, const char *func_name, struct fh_value *args, int n_args, struct fh_value *ret) 224 | { 225 | struct fh_closure *closure = fh_get_global_func_by_name(prog, func_name); 226 | if (! closure) 227 | return fh_set_error(prog, "function '%s' doesn't exist", func_name); 228 | return fh_call_vm_function(&prog->vm, closure, args, n_args, ret); 229 | } 230 | -------------------------------------------------------------------------------- /src/lib/program.h: -------------------------------------------------------------------------------- 1 | /* program.h */ 2 | 3 | #ifndef PROGRAM_H_FILE 4 | #define PROGRAM_H_FILE 5 | 6 | #include "fh.h" 7 | #include "ast.h" 8 | #include "bytecode.h" 9 | #include "vm.h" 10 | #include "parser.h" 11 | #include "compiler.h" 12 | #include "value.h" 13 | 14 | struct named_c_func { 15 | const char *name; 16 | fh_c_func func; 17 | }; 18 | 19 | DECLARE_STACK(named_c_func_stack, struct named_c_func); 20 | DECLARE_STACK(p_closure_stack, struct fh_closure *); 21 | DECLARE_STACK(p_object_stack, union fh_object *); 22 | 23 | struct fh_program { 24 | char last_error_msg[256]; 25 | int gc_frequency; 26 | int n_created_objs_since_last_gc; 27 | struct fh_value null_value; 28 | struct fh_parser parser; 29 | struct fh_compiler compiler; 30 | struct fh_symtab src_file_names; 31 | struct named_c_func_stack c_funcs; 32 | struct fh_vm vm; // GC roots (VM stack) 33 | struct p_closure_stack global_funcs; // GC roots (global functions) 34 | struct p_object_stack pinned_objs; // GC roots (temporarily pinned objects) 35 | struct value_stack c_vals; // GC roots (values held by running C functions) 36 | union fh_object *objects; // all created objects 37 | }; 38 | 39 | #endif /* PROGRAM_H_FILE */ 40 | -------------------------------------------------------------------------------- /src/lib/src_loc.c: -------------------------------------------------------------------------------- 1 | /* src_loc.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "fh_internal.h" 7 | #include "value.h" 8 | 9 | struct fh_src_loc fh_get_addr_src_loc(struct fh_func_def *func_def, int addr) 10 | { 11 | struct fh_src_loc loc = fh_make_src_loc(0,0,0); 12 | //printf("decoding src_loc for address %d\n", addr); 13 | fh_decode_src_loc(func_def->code_src_loc, func_def->code_src_loc_size, &loc, addr); 14 | return loc; 15 | } 16 | 17 | const void *fh_decode_src_loc(const void *encoded, int encoded_len, struct fh_src_loc *src_loc, int n_instr) 18 | { 19 | struct fh_src_loc loc = *src_loc; 20 | const uint8_t *p = encoded; 21 | const uint8_t *end = p + encoded_len; 22 | 23 | for (int pc = 0; pc < n_instr; pc++) { 24 | if (p >= end) 25 | break; 26 | uint8_t b = *p++; 27 | if ((b & 0xc0) == 0xc0) { 28 | if (p + 6 > end) 29 | return NULL; 30 | loc.col = p[0] | (p[1] << 8); 31 | loc.line = p[2] | (p[3] << 8); 32 | loc.file_id = p[4] | (p[5] << 8); 33 | p += 6; 34 | //printf("decoded absolute %d:%d:%d\n", loc.file_id, loc.line, loc.col); 35 | continue; 36 | } 37 | if ((b & 0xc0) == 0x80) { 38 | if (p + 1 > end) 39 | return NULL; 40 | uint8_t c = *p++; 41 | int delta_line = ((b & 0x3f) << 1) | (c >> 7); 42 | int delta_col = c & 0x7f; 43 | loc.col += delta_col - 63; 44 | loc.line += delta_line - 63; 45 | //printf("decoded relative %3d:%-3d [%02x %02x]\n", delta_col-63, delta_line-63, b, c); 46 | continue; 47 | } 48 | loc.col += (int)b - 63; 49 | //printf("decoded relative %3d [%02x]\n", (int)b - 63, b); 50 | } 51 | *src_loc = loc; 52 | return p; 53 | } 54 | 55 | static int get_encoded_delta(uint16_t old, uint16_t new) 56 | { 57 | if (old < new) { 58 | if (new - old > 64) 59 | return -1; 60 | return (new - old) + 63; 61 | } else { 62 | if (old - new > 63) 63 | return -1; 64 | return (new + 63) - old; 65 | } 66 | } 67 | 68 | int fh_encode_src_loc_change(struct fh_buffer *buf, struct fh_src_loc *old_loc, struct fh_src_loc *new_loc) 69 | { 70 | //printf("---- old=%3d:%-3d, new=%3d:%-3d ", old_loc->line, old_loc->col, new_loc->line, new_loc->col); 71 | 72 | int delta_line = get_encoded_delta(old_loc->line, new_loc->line); 73 | int delta_col = get_encoded_delta(old_loc->col, new_loc->col); 74 | if (buf->size == 0 || old_loc->file_id != new_loc->file_id || delta_col < 0 || delta_line < 0) { 75 | // absolute 76 | //printf("-> encoding absolute %d:%d:%d\n", new_loc->file_id, new_loc->line, new_loc->col); 77 | if (fh_buf_add_byte(buf, 0xff) < 0 78 | || fh_buf_add_u16(buf, new_loc->col) < 0 79 | || fh_buf_add_u16(buf, new_loc->line) < 0 80 | || fh_buf_add_u16(buf, new_loc->file_id) < 0) 81 | return -1; 82 | } else if (delta_line != 63) { 83 | // relative line, col 84 | //printf("-> encoding relative %3d:%-3d [%02x,%02x]\n", delta_line-63, delta_col-63, delta_line, delta_col); 85 | if (fh_buf_add_byte(buf, 0x80 | (delta_line>>1)) < 0 86 | || fh_buf_add_byte(buf, (delta_line<<7) | (delta_col&0x7f)) < 0) 87 | return -1; 88 | } else { 89 | // relative col 90 | //printf("-> encoding relative %3d [%02x]\n", delta_col-63, delta_col); 91 | if (fh_buf_add_byte(buf, delta_col) < 0) 92 | return -1; 93 | } 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /src/lib/stack.c: -------------------------------------------------------------------------------- 1 | /* stack.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "stack.h" 7 | 8 | void fh_init_stack(struct fh_stack *s) 9 | { 10 | s->data = NULL; 11 | s->num = 0; 12 | s->cap = 0; 13 | } 14 | 15 | void fh_free_stack(struct fh_stack *s) 16 | { 17 | if (s->data) { 18 | free(s->data); 19 | s->data = NULL; 20 | } 21 | s->num = 0; 22 | s->cap = 0; 23 | } 24 | 25 | int fh_stack_size(struct fh_stack *s) 26 | { 27 | return s->num; 28 | } 29 | 30 | void *fh_stack_top(struct fh_stack *s, size_t item_size) 31 | { 32 | return fh_stack_item(s, s->num-1, item_size); 33 | } 34 | 35 | void *fh_stack_item(struct fh_stack *s, int index, size_t item_size) 36 | { 37 | if (index < 0 || index >= s->num) 38 | return NULL; 39 | return (char *) s->data + index*item_size; 40 | } 41 | 42 | int fh_stack_shrink_to_fit(struct fh_stack *s, size_t item_size) 43 | { 44 | s->cap = s->num; 45 | 46 | if (s->num == 0) { 47 | if (s->data) 48 | free(s->data); 49 | s->data = NULL; 50 | } else { 51 | void *new_data = realloc(s->data, s->num * item_size); 52 | if (new_data == NULL) 53 | return -1; 54 | s->data = new_data; 55 | } 56 | return 0; 57 | } 58 | 59 | int fh_stack_ensure_cap(struct fh_stack *s, int n_items, size_t item_size) 60 | { 61 | if (s->num + n_items > s->cap) { 62 | int new_cap = (s->num + n_items + 15) / 16 * 16; 63 | void *new_data = realloc(s->data, new_cap * item_size); 64 | if (new_data == NULL) 65 | return -1; 66 | s->data = new_data; 67 | s->cap = new_cap; 68 | } 69 | return 0; 70 | } 71 | 72 | int fh_copy_stack(struct fh_stack *dst, const struct fh_stack *src, size_t item_size) 73 | { 74 | if (dst->cap < src->num) 75 | if (fh_stack_ensure_cap(dst, src->num - dst->num, item_size) < 0) 76 | return -1; 77 | if (src->num > 0) 78 | memcpy(dst->data, src->data, item_size*src->num); 79 | dst->num = src->num; 80 | return 0; 81 | } 82 | 83 | void *fh_push(struct fh_stack *s, void *item, size_t item_size) 84 | { 85 | if (fh_stack_ensure_cap(s, 1, item_size) < 0) 86 | return NULL; 87 | 88 | if (item) 89 | memcpy((char *) s->data + s->num*item_size, item, item_size); 90 | else 91 | memset((char *) s->data + s->num*item_size, 0, item_size); 92 | return (char *) s->data + (s->num++)*item_size; 93 | } 94 | 95 | int fh_pop(struct fh_stack *s, void *item, size_t item_size) 96 | { 97 | if (s->num == 0) 98 | return -1; 99 | 100 | s->num--; 101 | if (item) 102 | memcpy(item, (char *) s->data + s->num*item_size, item_size); 103 | return 0; 104 | } 105 | 106 | void *fh_stack_next(struct fh_stack *s, void *cur, size_t item_size) 107 | { 108 | if (cur == NULL) 109 | return (s->num > 0) ? s->data : NULL; 110 | cur = (char *) cur + item_size; 111 | if (cur >= (void *) ((char *) s->data + s->num*item_size)) 112 | return NULL; 113 | return cur; 114 | } 115 | -------------------------------------------------------------------------------- /src/lib/stack.h: -------------------------------------------------------------------------------- 1 | /* stack.h */ 2 | 3 | #ifndef STACK_H_FILE 4 | #define STACK_H_FILE 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | struct fh_stack { 13 | void *data; 14 | int num; 15 | int cap; 16 | }; 17 | 18 | void fh_init_stack(struct fh_stack *s); 19 | void fh_free_stack(struct fh_stack *s); 20 | int fh_stack_shrink_to_fit(struct fh_stack *s, size_t item_size); 21 | int fh_copy_stack(struct fh_stack *dst, const struct fh_stack *src, size_t item_size); 22 | int fh_stack_size(struct fh_stack *s); 23 | void *fh_push(struct fh_stack *s, void *item, size_t item_size); 24 | int fh_pop(struct fh_stack *s, void *item, size_t item_size); 25 | void *fh_stack_item(struct fh_stack *s, int index, size_t item_size); 26 | void *fh_stack_top(struct fh_stack *s, size_t item_size); 27 | void *fh_stack_next(struct fh_stack *s, void *cur, size_t item_size); 28 | 29 | int fh_stack_ensure_cap(struct fh_stack *s, int n_items, size_t item_size); 30 | 31 | #if 0 32 | #define DECLARE_STACK(name, type) \ 33 | struct name { struct fh_stack s; }; \ 34 | static inline void name##_init(struct name *n) { \ 35 | fh_init_stack(&n->s); \ 36 | } \ 37 | static inline void name##_free(struct name *n) { \ 38 | fh_free_stack(&n->s); \ 39 | } \ 40 | static inline int name##_shrink_to_fit(struct name *n) { \ 41 | return fh_stack_shrink_to_fit(&n->s, sizeof(type)); \ 42 | } \ 43 | static inline int name##_copy(struct name *dst, struct name *src) { \ 44 | return fh_copy_stack(&dst->s, &src->s, sizeof(type)); \ 45 | } \ 46 | static inline int name##_size(struct name *n) { \ 47 | return n->s.num; \ 48 | } \ 49 | static inline void name##_set_size(struct name *n, int size) { \ 50 | n->s.num = size; \ 51 | } \ 52 | static inline type *name##_data(struct name *n) { \ 53 | return n->s.data; \ 54 | } \ 55 | static inline type *name##_item(struct name *n, int index) { \ 56 | return fh_stack_item(&n->s, index, sizeof(type)); \ 57 | } \ 58 | static inline type *name##_top(struct name *n) { \ 59 | return fh_stack_top(&n->s, sizeof(type)); \ 60 | } \ 61 | static inline type *name##_next(struct name *n, type *item) { \ 62 | return fh_stack_next(&n->s, item, sizeof(type)); \ 63 | } \ 64 | static inline int name##_pop(struct name *n, type *item) { \ 65 | return fh_pop(&n->s, item, sizeof(type)); \ 66 | } \ 67 | static inline type *name##_push(struct name *n, type *item) { \ 68 | return fh_push(&n->s, item, sizeof(type)); \ 69 | } \ 70 | struct name /* keep some compilers happy about extra ';' */ 71 | #else 72 | /* 73 | * Inlining push, pop, item, top makes a considerable difference for 74 | * function calls (30% faster on tests/bench_call.fh). It increases 75 | * the code size a bit, though. 76 | */ 77 | #define DECLARE_STACK(name, type) \ 78 | struct name { struct fh_stack s; }; \ 79 | static inline void name##_init(struct name *n) { \ 80 | fh_init_stack(&n->s); \ 81 | } \ 82 | static inline void name##_free(struct name *n) { \ 83 | fh_free_stack(&n->s); \ 84 | } \ 85 | static inline int name##_size(struct name *n) { \ 86 | return n->s.num; \ 87 | } \ 88 | static inline type *name##_data(struct name *n) { \ 89 | return n->s.data; \ 90 | } \ 91 | static inline int name##_shrink_to_fit(struct name *n) { \ 92 | return fh_stack_shrink_to_fit(&n->s, sizeof(type)); \ 93 | } \ 94 | static inline int name##_copy(struct name *dst, struct name *src) { \ 95 | return fh_copy_stack(&dst->s, &src->s, sizeof(type)); \ 96 | } \ 97 | static inline void name##_set_size(struct name *n, int size) { \ 98 | n->s.num = size; \ 99 | } \ 100 | static inline type *name##_item(struct name *n, int index) { \ 101 | if (index < 0 || index >= n->s.num) return NULL; \ 102 | return (type*)n->s.data + index; \ 103 | } \ 104 | static inline type *name##_top(struct name *n) { \ 105 | if (n->s.num == 0) return NULL; \ 106 | return (type*) n->s.data + (n->s.num-1); \ 107 | } \ 108 | static inline type *name##_next(struct name *n, type *item) { \ 109 | return fh_stack_next(&n->s, item, sizeof(type)); \ 110 | } \ 111 | static inline int name##_pop(struct name *n, type *item) { \ 112 | if (n->s.num == 0) return -1; \ 113 | n->s.num--; \ 114 | if (item) *item = ((type*)n->s.data)[n->s.num]; \ 115 | return 0; \ 116 | } \ 117 | static inline type *name##_push(struct name *n, type *item) { \ 118 | if (n->s.num + 1 > n->s.cap && \ 119 | fh_stack_ensure_cap(&n->s, 1, sizeof(type)) < 0) \ 120 | return NULL; \ 121 | type *dest = (type*)n->s.data + n->s.num; \ 122 | if (item) *dest = *item; else memset(dest, 0, sizeof(type)); \ 123 | n->s.num++; \ 124 | return dest; \ 125 | } \ 126 | struct name /* keep some compilers happy about extra ';' */ 127 | #endif 128 | 129 | #define stack_foreach(type, star, v, st) \ 130 | for (type *v = NULL; \ 131 | (v = (type *)fh_stack_next(&(st)->s,v,sizeof(type))) != NULL; \ 132 | ) 133 | 134 | #endif /* STACK_H_FILE */ 135 | -------------------------------------------------------------------------------- /src/lib/symtab.c: -------------------------------------------------------------------------------- 1 | /* symtab.c */ 2 | 3 | #include 4 | 5 | #include "fh_internal.h" 6 | 7 | void fh_init_symtab(struct fh_symtab *s) 8 | { 9 | s->num = 0; 10 | s->cap = 0; 11 | s->entries = NULL; 12 | fh_init_buffer(&s->symbols); 13 | } 14 | 15 | void fh_destroy_symtab(struct fh_symtab *s) 16 | { 17 | if (s->entries) 18 | free(s->entries); 19 | fh_destroy_buffer(&s->symbols); 20 | } 21 | 22 | fh_symbol_id fh_add_symbol(struct fh_symtab *s, const void *symbol) 23 | { 24 | fh_symbol_id cur = fh_get_symbol_id(s, symbol); 25 | if (cur >= 0) 26 | return cur; 27 | 28 | if (s->num == s->cap) { 29 | int new_cap = (s->cap + 1024 + 1) / 1024 * 1024; 30 | void *new_entries = realloc(s->entries, new_cap * sizeof(s->entries[0])); 31 | if (new_entries == NULL) 32 | return -1; 33 | s->entries = new_entries; 34 | s->cap = new_cap; 35 | } 36 | 37 | int entry_pos = fh_buf_add_string(&s->symbols, symbol, strlen(symbol)); 38 | if (entry_pos < 0) 39 | return -1; 40 | s->entries[s->num] = entry_pos; 41 | return s->num++; 42 | } 43 | 44 | fh_symbol_id fh_get_symbol_id(struct fh_symtab *s, const void *symbol) 45 | { 46 | for (fh_symbol_id i = 0; i < s->num; i++) { 47 | if (strcmp(symbol, (char*) s->symbols.p + s->entries[i]) == 0) 48 | return i; 49 | } 50 | return -1; 51 | } 52 | 53 | const char *fh_get_symbol_name(struct fh_symtab *s, fh_symbol_id id) 54 | { 55 | if (id >= 0 && id < s->num) 56 | return s->symbols.p + s->entries[id]; 57 | return NULL; 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/tokenizer.c: -------------------------------------------------------------------------------- 1 | /* tokenizer.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "tokenizer.h" 9 | #include "ast.h" 10 | 11 | #define FH_IS_SPACE(c) ((c) == ' ' || (c) == '\r' || (c) == '\n' || (c) == '\t') 12 | #define FH_IS_ALPHA(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || (c) == '_') 13 | #define FH_IS_DIGIT(c) ((c) >= '0' && (c) <= '9') 14 | #define FH_IS_ALNUM(c) (FH_IS_ALPHA(c) || FH_IS_DIGIT(c)) 15 | 16 | static const struct keyword { 17 | enum fh_keyword_type type; 18 | const char *name; 19 | } keywords[] = { 20 | { KW_INCLUDE, "include" }, 21 | { KW_FUNCTION, "function" }, 22 | { KW_RETURN, "return" }, 23 | { KW_VAR, "var" }, 24 | { KW_IF, "if" }, 25 | { KW_ELSE, "else" }, 26 | { KW_WHILE, "while" }, 27 | { KW_BREAK, "break" }, 28 | { KW_CONTINUE, "continue" }, 29 | }; 30 | 31 | struct fh_tokenizer *fh_new_tokenizer(struct fh_program *prog, struct fh_input *in, struct fh_ast *ast, struct fh_buffer *tmp_buf, uint16_t file_id) 32 | { 33 | struct fh_tokenizer *t = malloc(sizeof(struct fh_tokenizer)); 34 | if (! t) 35 | return NULL; 36 | t->next = NULL; 37 | t->prog = prog; 38 | t->in = in; 39 | t->ast = ast; 40 | t->tmp = tmp_buf; 41 | t->file_id = file_id; 42 | t->cur_loc = fh_make_src_loc(file_id, 1, 0); 43 | 44 | t->buf_pos = 0; 45 | t->buf_len = 0; 46 | t->saved_byte = -1; 47 | 48 | t->last_err_loc = t->cur_loc; 49 | return t; 50 | } 51 | 52 | void fh_free_tokenizer(struct fh_tokenizer *t) 53 | { 54 | free(t); 55 | } 56 | 57 | static void set_error(struct fh_tokenizer *t, struct fh_src_loc loc, char *fmt, ...) 58 | { 59 | char str[256]; 60 | va_list ap; 61 | va_start(ap, fmt); 62 | vsnprintf(str, sizeof(str), fmt, ap); 63 | va_end(ap); 64 | 65 | fh_set_error(t->prog, "%s:%d:%d: %s", fh_get_ast_file_name(t->ast, loc.file_id), loc.line, loc.col, str); 66 | t->last_err_loc = loc; 67 | } 68 | 69 | struct fh_src_loc fh_get_tokenizer_error_loc(struct fh_tokenizer *t) 70 | { 71 | return t->last_err_loc; 72 | } 73 | 74 | const char *fh_get_token_keyword(struct fh_token *tok) 75 | { 76 | for (int i = 0; i < ARRAY_SIZE(keywords); i++) { 77 | if (keywords[i].type == tok->data.keyword) 78 | return keywords[i].name; 79 | } 80 | return NULL; 81 | } 82 | 83 | const char *fh_get_token_symbol(struct fh_ast *ast, struct fh_token *tok) 84 | { 85 | return fh_get_symbol_name(&ast->symtab, tok->data.symbol_id); 86 | } 87 | 88 | const char *fh_get_token_op(struct fh_token *tok) 89 | { 90 | return tok->data.op_name; 91 | } 92 | 93 | const char *fh_get_token_string(struct fh_ast *ast, struct fh_token *tok) 94 | { 95 | if (tok->type == TOK_STRING) 96 | return fh_get_ast_string(ast, tok->data.str); 97 | return NULL; 98 | } 99 | 100 | static int next_byte(struct fh_tokenizer *t) 101 | { 102 | if (t->saved_byte >= 0) { 103 | uint8_t ret = t->saved_byte; 104 | struct fh_src_loc tmp = t->cur_loc; 105 | t->cur_loc = t->saved_loc; 106 | t->saved_loc = tmp; 107 | t->saved_byte = -2; 108 | return ret; 109 | } 110 | 111 | if (t->saved_byte == -2) { 112 | t->cur_loc = t->saved_loc; 113 | t->saved_byte = -1; 114 | } 115 | 116 | if (t->buf_pos == t->buf_len) { 117 | int r = fh_read_input(t->in, t->buf, sizeof(t->buf)); 118 | if (r < 0) 119 | return -1; 120 | t->buf_len = (uint32_t) r; 121 | t->buf_pos = 0; 122 | } 123 | uint8_t ret = (uint8_t) t->buf[t->buf_pos++]; 124 | if (ret == '\n') { 125 | t->cur_loc.line++; 126 | t->cur_loc.col = 0; 127 | } else { 128 | t->cur_loc.col++; 129 | } 130 | return ret; 131 | } 132 | 133 | static void unget_byte(struct fh_tokenizer *t, uint8_t b) 134 | { 135 | if (t->saved_byte >= 0) { 136 | fprintf(stderr, "ERROR: can't unget byte: buffer full"); 137 | return; 138 | } 139 | 140 | t->saved_loc = t->cur_loc; 141 | t->saved_byte = b; 142 | } 143 | 144 | static int find_keyword(const void *keyword, int keyword_size, enum fh_keyword_type *ret) 145 | { 146 | for (int i = 0; i < ARRAY_SIZE(keywords); i++) { 147 | if (strncmp(keyword, keywords[i].name, keyword_size) == 0 && keywords[i].name[keyword_size] == '\0') { 148 | *ret = keywords[i].type; 149 | return 1; 150 | } 151 | } 152 | return 0; 153 | } 154 | 155 | const char *fh_dump_token(struct fh_ast *ast, struct fh_token *tok) 156 | { 157 | static char str[256]; 158 | 159 | switch (tok->type) { 160 | case TOK_EOF: 161 | snprintf(str, sizeof(str), ""); 162 | break; 163 | 164 | case TOK_KEYWORD: 165 | snprintf(str, sizeof(str), "%s", fh_get_token_keyword(tok)); 166 | break; 167 | 168 | case TOK_SYMBOL: 169 | snprintf(str, sizeof(str), "%s", fh_get_token_symbol(ast, tok)); 170 | break; 171 | 172 | case TOK_OP: 173 | snprintf(str, sizeof(str), "%s", fh_get_token_op(tok)); 174 | break; 175 | 176 | case TOK_PUNCT: 177 | snprintf(str, sizeof(str), "%c", tok->data.punct); 178 | break; 179 | 180 | case TOK_STRING: 181 | snprintf(str, sizeof(str), "\"%s\"", fh_get_token_string(ast, tok)); 182 | break; 183 | 184 | case TOK_NUMBER: 185 | snprintf(str, sizeof(str), "%g", tok->data.num); 186 | break; 187 | 188 | default: 189 | snprintf(str, sizeof(str), "", tok->type); 190 | break; 191 | } 192 | 193 | return str; 194 | } 195 | 196 | int fh_read_token(struct fh_tokenizer *t, struct fh_token *tok) 197 | { 198 | int c; 199 | 200 | while (1) { 201 | c = next_byte(t); 202 | if (c < 0) { 203 | tok->loc = t->cur_loc; 204 | tok->type = TOK_EOF; 205 | return 0; 206 | } 207 | if (FH_IS_SPACE(c)) 208 | continue; 209 | 210 | if (c == '#') { 211 | while (c != '\n') { 212 | c = next_byte(t); 213 | if (c < 0) { 214 | tok->loc = t->cur_loc; 215 | tok->type = TOK_EOF; 216 | return 0; 217 | } 218 | } 219 | continue; 220 | } 221 | 222 | break; 223 | } 224 | 225 | tok->loc = t->cur_loc; 226 | 227 | // string 228 | if (c == '"') { 229 | t->tmp->size = 0; 230 | while (1) { 231 | c = next_byte(t); 232 | if (c < 0) { 233 | set_error(t, tok->loc, "unterminated string"); 234 | return -1; 235 | } 236 | if (c == '"') 237 | break; 238 | if (c == '\\') { 239 | int next = next_byte(t); 240 | if (next < 0) { 241 | set_error(t, tok->loc, "unterminated string"); 242 | return -1; 243 | } 244 | switch (next) { 245 | case '"': c = '"'; break; 246 | case '\\': c = '\\'; break; 247 | case '\'': c = '\''; break; 248 | case 'e': c = '\x1b'; break; 249 | case 'n': c = '\n'; break; 250 | case 't': c = '\t'; break; 251 | case 'r': c = '\r'; break; 252 | default: 253 | set_error(t, t->cur_loc, "bad escape sequence"); 254 | return -1; 255 | } 256 | } 257 | if (fh_buf_add_byte(t->tmp, c) < 0) { 258 | set_error(t, tok->loc, "out of memory"); 259 | return -1; 260 | } 261 | } 262 | if (fh_utf8_len(t->tmp->p, t->tmp->size) < 0) { 263 | set_error(t, tok->loc, "invalid utf-8 string"); 264 | return -1; 265 | } 266 | fh_string_id str_pos = fh_buf_add_string(&t->ast->string_pool, t->tmp->p, t->tmp->size); 267 | if (str_pos < 0) { 268 | set_error(t, tok->loc, "out of memory"); 269 | return -1; 270 | } 271 | tok->type = TOK_STRING; 272 | tok->data.str = str_pos; 273 | return 0; 274 | } 275 | 276 | // number 277 | if (FH_IS_DIGIT(c)) { 278 | t->tmp->size = 0; 279 | int got_point = 0; 280 | while (FH_IS_DIGIT(c) || c == '.') { 281 | if (c == '.') { 282 | if (got_point) 283 | break; 284 | got_point = 1; 285 | } 286 | if (fh_buf_add_byte(t->tmp, c) < 0) { 287 | set_error(t, tok->loc, "out of memory"); 288 | return -1; 289 | } 290 | c = next_byte(t); 291 | } 292 | if (c >= 0) 293 | unget_byte(t, c); 294 | 295 | if (fh_buf_add_byte(t->tmp, '\0') < 0) { 296 | set_error(t, tok->loc, "out of memory"); 297 | return -1; 298 | } 299 | 300 | char *end = NULL; 301 | double num = strtod(t->tmp->p, &end); 302 | if (t->tmp->p == end) { 303 | set_error(t, tok->loc, "invalid number"); 304 | return -1; 305 | } 306 | tok->type = TOK_NUMBER; 307 | tok->data.num = num; 308 | return 0; 309 | } 310 | 311 | // keyword or symbol 312 | if (FH_IS_ALPHA(c)) { 313 | t->tmp->size = 0; 314 | while (FH_IS_ALNUM(c)) { 315 | if (fh_buf_add_byte(t->tmp, c) < 0) { 316 | set_error(t, tok->loc, "out of memory"); 317 | return -1; 318 | } 319 | c = next_byte(t); 320 | } 321 | if (c >= 0) 322 | unget_byte(t, c); 323 | 324 | enum fh_keyword_type keyword; 325 | if (find_keyword(t->tmp->p, t->tmp->size, &keyword) > 0) { 326 | // keyword 327 | tok->type = TOK_KEYWORD; 328 | tok->data.keyword = keyword; 329 | } else { 330 | // other symbol 331 | if (fh_buf_add_byte(t->tmp, '\0') < 0) { 332 | set_error(t, tok->loc, "out of memory"); 333 | return -1; 334 | } 335 | fh_symbol_id symbol_id = fh_add_symbol(&t->ast->symtab, t->tmp->p); 336 | if (symbol_id < 0) { 337 | set_error(t, tok->loc, "out of memory"); 338 | return -1; 339 | } 340 | tok->type = TOK_SYMBOL; 341 | tok->data.symbol_id = symbol_id; 342 | } 343 | 344 | return 0; 345 | } 346 | 347 | // punctuation 348 | if (c == ',' || c == '.' || c == ';' || c == ':' || 349 | c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') { 350 | tok->type = TOK_PUNCT; 351 | tok->data.punct = c; 352 | return 0; 353 | } 354 | 355 | // operator 356 | struct fh_src_loc op_loc = t->cur_loc; 357 | char op_name[4] = { c }; 358 | int op_len = 1; 359 | while (1) { 360 | op_name[op_len] = '\0'; 361 | if (! fh_get_op(op_name)) { 362 | op_name[--op_len] = '\0'; 363 | unget_byte(t, c); 364 | break; 365 | } 366 | 367 | if (op_len == sizeof(op_name)-1) 368 | break; 369 | c = next_byte(t); 370 | if (c < 0) 371 | break; 372 | op_name[op_len++] = c; 373 | } 374 | if (op_len > 0) { 375 | tok->type = TOK_OP; 376 | strcpy(tok->data.op_name, op_name); 377 | return 0; 378 | } 379 | 380 | if (c >= 32 && c < 128) 381 | set_error(t, op_loc, "invalid character: '%c'", c); 382 | else 383 | set_error(t, op_loc, "invalid byte: 0x%02x", c); 384 | return -1; 385 | } 386 | -------------------------------------------------------------------------------- /src/lib/tokenizer.h: -------------------------------------------------------------------------------- 1 | /* tokenizer.h */ 2 | 3 | #ifndef TOKENIZER_H_FILE 4 | #define TOKENIZER_H_FILE 5 | 6 | #include "fh_internal.h" 7 | 8 | #define TOKENIZER_BUF_SIZE 256 9 | 10 | enum fh_token_type { 11 | TOK_EOF, 12 | TOK_KEYWORD, 13 | TOK_SYMBOL, 14 | TOK_STRING, 15 | TOK_NUMBER, 16 | TOK_OP, 17 | TOK_PUNCT, 18 | }; 19 | 20 | enum fh_keyword_type { 21 | KW_INCLUDE, 22 | KW_FUNCTION, 23 | KW_RETURN, 24 | KW_VAR, 25 | KW_IF, 26 | KW_ELSE, 27 | KW_WHILE, 28 | KW_BREAK, 29 | KW_CONTINUE, 30 | }; 31 | 32 | struct fh_token { 33 | enum fh_token_type type; 34 | struct fh_src_loc loc; 35 | union { 36 | double num; 37 | fh_string_id str; 38 | enum fh_keyword_type keyword; 39 | fh_symbol_id symbol_id; 40 | char op_name[4]; 41 | uint32_t punct; 42 | } data; 43 | }; 44 | 45 | struct fh_tokenizer { 46 | struct fh_tokenizer *next; 47 | struct fh_program *prog; 48 | struct fh_input *in; 49 | struct fh_ast *ast; 50 | struct fh_buffer *tmp; 51 | uint16_t file_id; 52 | 53 | struct fh_src_loc cur_loc; 54 | uint32_t buf_pos; 55 | uint32_t buf_len; 56 | char buf[TOKENIZER_BUF_SIZE]; 57 | 58 | struct fh_src_loc last_err_loc; 59 | 60 | int saved_byte; 61 | struct fh_src_loc saved_loc; 62 | }; 63 | 64 | struct fh_tokenizer *fh_new_tokenizer(struct fh_program *prog, struct fh_input *in, struct fh_ast *ast, struct fh_buffer *tmp_buf, uint16_t file_id); 65 | void fh_free_tokenizer(struct fh_tokenizer *t); 66 | int fh_read_token(struct fh_tokenizer *t, struct fh_token *tok); 67 | struct fh_src_loc fh_get_tokenizer_error_loc(struct fh_tokenizer *t); 68 | const char *fh_get_token_keyword(struct fh_token *tok); 69 | const char *fh_get_token_symbol(struct fh_ast *ast, struct fh_token *tok); 70 | const char *fh_get_token_string(struct fh_ast *ast, struct fh_token *tok); 71 | const char *fh_get_token_op(struct fh_token *tok); 72 | 73 | const char *fh_dump_token(struct fh_ast *ast, struct fh_token *tok); 74 | 75 | #define tok_is_eof(tok) ((tok)->type == TOK_EOF) 76 | #define tok_is_number(tok) ((tok)->type == TOK_NUMBER) 77 | #define tok_is_string(tok) ((tok)->type == TOK_STRING) 78 | #define tok_is_punct(tok, p) ((tok)->type == TOK_PUNCT && (tok)->data.punct == (p)) 79 | #define tok_is_keyword(tok, kw) ((tok)->type == TOK_KEYWORD && (tok)->data.keyword == (kw)) 80 | #define tok_is_symbol(tok) ((tok)->type == TOK_SYMBOL) 81 | 82 | #endif /* TOKENIZER_H_FILE */ 83 | -------------------------------------------------------------------------------- /src/lib/util.c: -------------------------------------------------------------------------------- 1 | /* util.c */ 2 | 3 | #include 4 | #include 5 | 6 | #include "fh_internal.h" 7 | #include "bytecode.h" 8 | 9 | uint32_t fh_hash(const void *data, size_t len) 10 | { 11 | // this is the hash used by ELF 12 | uint32_t high; 13 | const unsigned char *s = data; 14 | const unsigned char *end = s + len; 15 | uint32_t h = 0; 16 | while (s < end) { 17 | h = (h << 4) + *s++; 18 | if ((high = h & 0xF0000000) != 0) 19 | h ^= high >> 24; 20 | h &= ~high; 21 | } 22 | 23 | // this is an additional bit mix 24 | uint32_t r = h; 25 | r += r << 16; 26 | r ^= r >> 13; 27 | r += r << 4; 28 | r ^= r >> 7; 29 | r += r << 10; 30 | r ^= r >> 5; 31 | r += r << 8; 32 | r ^= r >> 16; 33 | return r; 34 | } 35 | 36 | void fh_dump_string(const char *str) 37 | { 38 | printf("\""); 39 | for (const char *p = str; *p != '\0'; p++) { 40 | switch (*p) { 41 | case '\n': printf("\\n"); break; 42 | case '\r': printf("\\r"); break; 43 | case '\t': printf("\\t"); break; 44 | case '\\': printf("\\\\"); break; 45 | case '"': printf("\\\""); break; 46 | default: 47 | if (*p < 32) 48 | printf("\\x%02x", (unsigned char) *p); 49 | else 50 | printf("%c", *p); 51 | break; 52 | } 53 | } 54 | printf("\""); 55 | } 56 | 57 | void fh_dump_value(const struct fh_value *val) 58 | { 59 | switch (val->type) { 60 | case FH_VAL_NULL: printf("NULL"); return; 61 | case FH_VAL_BOOL: printf("BOOL(%s)", (val->data.b) ? "true" : "false"); return; 62 | case FH_VAL_NUMBER: printf("NUMBER(%f)", val->data.num); return; 63 | case FH_VAL_STRING: printf("STRING("); fh_dump_string(fh_get_string(val)); printf(")"); return; 64 | case FH_VAL_ARRAY: printf("ARRAY(len=%d)", fh_get_array_len(val)); return; 65 | case FH_VAL_MAP: printf("MAP(len=%d,cap=%d)", GET_OBJ_MAP(val->data.obj)->len, GET_OBJ_MAP(val->data.obj)->cap); break; 66 | case FH_VAL_UPVAL: printf("UPVAL("); fh_dump_value(GET_OBJ_UPVAL(val->data.obj)->val); printf(")"); return; 67 | case FH_VAL_CLOSURE: printf("CLOSURE(%p)", val->data.obj); return; 68 | case FH_VAL_FUNC_DEF: printf("FUNC_DEF(%p)", val->data.obj); return; 69 | case FH_VAL_C_FUNC: printf("C_FUNC"); return; 70 | } 71 | printf("INVALID_VALUE(type=%d)", val->type); 72 | } 73 | 74 | int fh_utf8_len(char *str, size_t str_size) 75 | { 76 | int len = 0; 77 | uint8_t *p = (uint8_t *) str; 78 | uint8_t *end = (uint8_t *) str + str_size; 79 | 80 | while (p < end) { 81 | uint8_t c = *p++; 82 | if (c == 0) 83 | break; 84 | if ((c & 0x80) == 0) { 85 | len++; 86 | } else if ((c & 0xe0) == 0xc0) { 87 | len += 2; 88 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 89 | } else if ((c & 0xf0) == 0xe0) { 90 | len += 3; 91 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 92 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 93 | } else if ((c & 0xf8) == 0xf0) { 94 | len += 4; 95 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 96 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 97 | if (p >= end || (*p++ & 0xc0) != 0x80) return -1; 98 | } else { 99 | return -1; 100 | } 101 | } 102 | 103 | return len; 104 | } 105 | -------------------------------------------------------------------------------- /src/lib/value.c: -------------------------------------------------------------------------------- 1 | /* value.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "program.h" 8 | #include "value.h" 9 | 10 | static void free_func_def(struct fh_func_def *func_def) 11 | { 12 | if (func_def->consts) 13 | free(func_def->consts); 14 | if (func_def->code) 15 | free(func_def->code); 16 | if (func_def->upvals) 17 | free(func_def->upvals); 18 | if (func_def->code_src_loc) 19 | free(func_def->code_src_loc); 20 | free(func_def); 21 | } 22 | 23 | static void free_closure(struct fh_closure *closure) 24 | { 25 | free(closure); 26 | } 27 | 28 | static void free_upval(struct fh_upval *upval) 29 | { 30 | free(upval); 31 | } 32 | 33 | static void free_array(struct fh_array *arr) 34 | { 35 | if (arr->items) 36 | free(arr->items); 37 | free(arr); 38 | } 39 | 40 | static void free_map(struct fh_map *map) 41 | { 42 | if (map->entries) 43 | free(map->entries); 44 | free(map); 45 | } 46 | 47 | void fh_free_object(union fh_object *obj) 48 | { 49 | switch (obj->header.type) { 50 | case FH_VAL_NULL: 51 | case FH_VAL_BOOL: 52 | case FH_VAL_NUMBER: 53 | case FH_VAL_C_FUNC: 54 | fprintf(stderr, "**** ERROR: freeing object of NON-OBJECT type %d\n", obj->header.type); 55 | free(obj); 56 | return; 57 | 58 | case FH_VAL_STRING: free(obj); return; 59 | case FH_VAL_CLOSURE: free_closure(GET_OBJ_CLOSURE(obj)); return; 60 | case FH_VAL_UPVAL: free_upval(GET_OBJ_UPVAL(obj)); return; 61 | case FH_VAL_FUNC_DEF: free_func_def(GET_OBJ_FUNC_DEF(obj)); return; 62 | case FH_VAL_ARRAY: free_array(GET_OBJ_ARRAY(obj)); return; 63 | case FH_VAL_MAP: free_map(GET_OBJ_MAP(obj)); return; 64 | } 65 | 66 | fprintf(stderr, "**** ERROR: freeing object of INVALID type %d\n", obj->header.type); 67 | free(obj); 68 | } 69 | 70 | 71 | const char *fh_get_string(const struct fh_value *val) 72 | { 73 | if (val->type != FH_VAL_STRING) 74 | return NULL; 75 | return GET_OBJ_STRING_DATA(val->data.obj); 76 | } 77 | 78 | int fh_get_array_len(const struct fh_value *val) 79 | { 80 | if (val->type != FH_VAL_ARRAY) 81 | return -1; 82 | return GET_OBJ_ARRAY(val->data.obj)->len; 83 | } 84 | 85 | struct fh_value *fh_get_array_item(struct fh_value *val, uint32_t index) 86 | { 87 | if (val->type != FH_VAL_ARRAY) 88 | return NULL; 89 | 90 | struct fh_array *arr = GET_OBJ_ARRAY(val->data.obj); 91 | if (index >= arr->len) 92 | return NULL; 93 | return &arr->items[index]; 94 | } 95 | 96 | struct fh_value *fh_grow_array_object(struct fh_program *prog, struct fh_array *arr, uint32_t num_items) 97 | { 98 | if (arr->header.type != FH_VAL_ARRAY) 99 | return NULL; 100 | 101 | if ((size_t) arr->len + num_items + 15 < (size_t) arr->len 102 | || (size_t) arr->len + num_items + 15 > UINT32_MAX) 103 | return NULL; 104 | if (arr->len + num_items >= arr->cap) { 105 | size_t new_cap = ((size_t) arr->len + num_items + 15) / 16 * 16; 106 | void *new_items = realloc(arr->items, new_cap*sizeof(struct fh_value)); 107 | if (! new_items) { 108 | fh_set_error(prog, "out of memory"); 109 | return NULL; 110 | } 111 | arr->items = new_items; 112 | arr->cap = (uint32_t) new_cap; 113 | } 114 | struct fh_value *ret = &arr->items[arr->len]; 115 | for (uint32_t i = 0; i < num_items; i++) 116 | ret[i].type = FH_VAL_NULL; 117 | arr->len += num_items; 118 | return ret; 119 | } 120 | 121 | struct fh_value *fh_grow_array(struct fh_program *prog, struct fh_value *val, uint32_t num_items) 122 | { 123 | return fh_grow_array_object(prog, GET_OBJ_ARRAY(val->data.obj), num_items); 124 | } 125 | 126 | const char *fh_get_func_def_name(struct fh_func_def *func_def) 127 | { 128 | if (func_def->header.type != FH_VAL_FUNC_DEF || ! func_def->name) 129 | return NULL; 130 | return GET_OBJ_STRING_DATA(func_def->name); 131 | } 132 | 133 | /************************************************************************* 134 | * OBJECT CREATION 135 | * 136 | * The following functions create a new object and add it to the list 137 | * of program objects. 138 | *************************************************************************/ 139 | 140 | static void *fh_make_object(struct fh_program *prog, bool pinned, enum fh_value_type type, size_t size) 141 | { 142 | if (size < sizeof(struct fh_object_header)) { 143 | fh_set_error(prog, "object size too small"); 144 | return NULL; 145 | } 146 | 147 | if (prog->gc_frequency >= 0 && ++prog->n_created_objs_since_last_gc > prog->gc_frequency) 148 | fh_collect_garbage(prog); 149 | 150 | union fh_object *obj = malloc(size); 151 | if (! obj) { 152 | fh_set_error(prog, "out of memory"); 153 | return NULL; 154 | } 155 | if (pinned) { 156 | if (! p_object_stack_push(&prog->pinned_objs, &obj)) { 157 | free(obj); 158 | fh_set_error(prog, "out of memory"); 159 | return NULL; 160 | } 161 | } 162 | 163 | obj->header.next = prog->objects; 164 | prog->objects = obj; 165 | obj->header.type = type; 166 | obj->header.gc_bits = 0; 167 | return obj; 168 | } 169 | 170 | struct fh_upval *fh_make_upval(struct fh_program *prog, bool pinned) 171 | { 172 | struct fh_upval *uv = fh_make_object(prog, pinned, FH_VAL_UPVAL, sizeof(struct fh_upval)); 173 | if (! uv) 174 | return NULL; 175 | uv->gc_next_container = NULL; 176 | return uv; 177 | } 178 | 179 | struct fh_closure *fh_make_closure(struct fh_program *prog, bool pinned, struct fh_func_def *func_def) 180 | { 181 | struct fh_closure *c = fh_make_object(prog, pinned, FH_VAL_CLOSURE, sizeof(struct fh_closure) + func_def->n_upvals*sizeof(struct fh_upval *)); 182 | if (! c) 183 | return NULL; 184 | c->gc_next_container = NULL; 185 | c->func_def = func_def; 186 | c->n_upvals = func_def->n_upvals; 187 | return c; 188 | } 189 | 190 | struct fh_func_def *fh_make_func_def(struct fh_program *prog, bool pinned) 191 | { 192 | struct fh_func_def *func_def = fh_make_object(prog, pinned, FH_VAL_FUNC_DEF, sizeof(struct fh_func_def)); 193 | if (! func_def) 194 | return NULL; 195 | func_def->gc_next_container = NULL; 196 | return func_def; 197 | } 198 | 199 | struct fh_array *fh_make_array(struct fh_program *prog, bool pinned) 200 | { 201 | struct fh_array *arr = fh_make_object(prog, pinned, FH_VAL_ARRAY, sizeof(struct fh_array)); 202 | if (! arr) 203 | return NULL; 204 | arr->gc_next_container = NULL; 205 | arr->len = 0; 206 | arr->cap = 0; 207 | arr->items = NULL; 208 | return arr; 209 | } 210 | 211 | struct fh_map *fh_make_map(struct fh_program *prog, bool pinned) 212 | { 213 | struct fh_map *map = fh_make_object(prog, pinned, FH_VAL_MAP, sizeof(struct fh_map)); 214 | if (! map) 215 | return NULL; 216 | map->gc_next_container = NULL; 217 | map->len = 0; 218 | map->cap = 0; 219 | map->entries = NULL; 220 | return map; 221 | } 222 | 223 | struct fh_string *fh_make_string_n(struct fh_program *prog, bool pinned, const char *str, size_t str_len) 224 | { 225 | if (sizeof(struct fh_string) + str_len > UINT32_MAX) 226 | return NULL; 227 | struct fh_string *s = fh_make_object(prog, pinned, FH_VAL_STRING, sizeof(struct fh_string) + str_len); 228 | if (! s) 229 | return NULL; 230 | memcpy(GET_OBJ_STRING_DATA(s), str, str_len); 231 | s->size = (uint32_t) str_len; 232 | s->hash = fh_hash(str, str_len); 233 | return s; 234 | } 235 | 236 | struct fh_string *fh_make_string(struct fh_program *prog, bool pinned, const char *str) 237 | { 238 | return fh_make_string_n(prog, pinned, str, strlen(str)+1); 239 | } 240 | 241 | /************************************************************************* 242 | * C INTERFACE FUNCTIONS 243 | * 244 | * The following functions create a new value and, if the value is an 245 | * object, add the object to the C temp array to keep it anchored 246 | * while the C function is running. 247 | *************************************************************************/ 248 | 249 | struct fh_value fh_new_string(struct fh_program *prog, const char *str) 250 | { 251 | return fh_new_string_n(prog, str, strlen(str) + 1); 252 | } 253 | 254 | struct fh_value fh_new_string_n(struct fh_program *prog, const char *str, size_t str_len) 255 | { 256 | struct fh_value *val = value_stack_push(&prog->c_vals, NULL); 257 | if (! val) { 258 | fh_set_error(prog, "out of memory"); 259 | return prog->null_value; 260 | } 261 | struct fh_string *s = fh_make_string_n(prog, false, str, str_len); 262 | if (! s) { 263 | value_stack_pop(&prog->c_vals, NULL); 264 | return prog->null_value; 265 | } 266 | val->type = FH_VAL_STRING; 267 | val->data.obj = s; 268 | return *val; 269 | } 270 | 271 | struct fh_value fh_new_array(struct fh_program *prog) 272 | { 273 | struct fh_value *val = value_stack_push(&prog->c_vals, NULL); 274 | if (! val) { 275 | fh_set_error(prog, "out of memory"); 276 | return prog->null_value; 277 | } 278 | struct fh_array *arr = fh_make_array(prog, false); 279 | if (! arr) { 280 | value_stack_pop(&prog->c_vals, NULL); 281 | return prog->null_value; 282 | } 283 | val->type = FH_VAL_ARRAY; 284 | val->data.obj = arr; 285 | return *val; 286 | } 287 | 288 | struct fh_value fh_new_map(struct fh_program *prog) 289 | { 290 | struct fh_value *val = value_stack_push(&prog->c_vals, NULL); 291 | if (! val) { 292 | fh_set_error(prog, "out of memory"); 293 | return prog->null_value; 294 | } 295 | struct fh_map *map = fh_make_map(prog, false); 296 | if (! map) { 297 | value_stack_pop(&prog->c_vals, NULL); 298 | return prog->null_value; 299 | } 300 | val->type = FH_VAL_MAP; 301 | val->data.obj = map; 302 | return *val; 303 | } 304 | -------------------------------------------------------------------------------- /src/lib/value.h: -------------------------------------------------------------------------------- 1 | /* value.h */ 2 | 3 | #ifndef VALUE_H_FILE 4 | #define VALUE_H_FILE 5 | 6 | #include "fh_internal.h" 7 | #include "stack.h" 8 | 9 | #define GC_BIT_MARK (1<<0) 10 | #define GC_BIT_PIN (1<<1) 11 | 12 | #define GC_SET_BIT(o,b) ((o)->header.gc_bits|=(b)) 13 | #define GC_CLEAR_BIT(o,b) ((o)->header.gc_bits&=~(b)) 14 | #define GC_PIN_OBJ(o) GC_SET_BIT(o, GC_BIT_PIN) 15 | #define GC_UNPIN_OBJ(o) GC_CLEAR_BIT(o, GC_BIT_PIN) 16 | 17 | struct fh_object_header { 18 | union fh_object *next; 19 | uint8_t gc_bits; 20 | enum fh_value_type type; 21 | }; 22 | 23 | struct fh_string { 24 | struct fh_object_header header; 25 | uint32_t size; 26 | uint32_t hash; 27 | }; 28 | 29 | struct fh_array { 30 | struct fh_object_header header; 31 | union fh_object *gc_next_container; 32 | struct fh_value *items; 33 | uint32_t len; 34 | uint32_t cap; 35 | }; 36 | 37 | struct fh_map_entry { 38 | struct fh_value key; 39 | struct fh_value val; 40 | }; 41 | 42 | struct fh_map { 43 | struct fh_object_header header; 44 | union fh_object *gc_next_container; 45 | struct fh_map_entry *entries; 46 | uint32_t len; 47 | uint32_t cap; 48 | }; 49 | 50 | struct fh_func_def { 51 | struct fh_object_header header; 52 | union fh_object *gc_next_container; 53 | struct fh_string *name; 54 | int n_params; 55 | int n_regs; 56 | uint32_t *code; 57 | int code_size; 58 | struct fh_value *consts; 59 | int n_consts; 60 | struct fh_upval_def *upvals; 61 | int n_upvals; 62 | int code_src_loc_size; 63 | void *code_src_loc; 64 | }; 65 | 66 | struct fh_upval { 67 | struct fh_object_header header; 68 | union fh_object *gc_next_container; 69 | struct fh_value *val; 70 | union { 71 | struct fh_value storage; 72 | struct fh_upval *next; 73 | } data; 74 | }; 75 | 76 | struct fh_closure { 77 | struct fh_object_header header; 78 | union fh_object *gc_next_container; 79 | struct fh_func_def *func_def; 80 | int n_upvals; 81 | struct fh_upval *upvals[]; 82 | }; 83 | 84 | union fh_object { 85 | struct fh_object_header header; 86 | struct fh_string str; 87 | struct fh_func_def func_def; 88 | struct fh_upval upval; 89 | struct fh_closure closure; 90 | struct fh_array array; 91 | struct fh_map map; 92 | }; 93 | 94 | enum fh_upval_def_type { 95 | FH_UPVAL_TYPE_REG, 96 | FH_UPVAL_TYPE_UPVAL, 97 | }; 98 | 99 | struct fh_upval_def { 100 | enum fh_upval_def_type type; 101 | int num; 102 | }; 103 | 104 | #define VAL_IS_OBJECT(v) ((v)->type >= FH_FIRST_OBJECT_VAL) 105 | 106 | #define GET_OBJ_CLOSURE(o) ((struct fh_closure *) (o)) 107 | #define GET_OBJ_UPVAL(o) ((struct fh_upval *) (o)) 108 | #define GET_OBJ_FUNC_DEF(o) ((struct fh_func_def *) (o)) 109 | #define GET_OBJ_ARRAY(o) ((struct fh_array *) (o)) 110 | #define GET_OBJ_MAP(o) ((struct fh_map *) (o)) 111 | #define GET_OBJ_STRING(o) ((struct fh_string *) (o)) 112 | #define GET_OBJ_STRING_DATA(o) (((char *) (o)) + sizeof(struct fh_string)) 113 | 114 | #define GET_VAL_OBJ(v) ((union fh_object *) ((v)->data.obj)) 115 | #define GET_VAL_CLOSURE(v) (((v)->type == FH_VAL_CLOSURE ) ? ((struct fh_closure *) ((v)->data.obj)) : NULL) 116 | #define GET_VAL_FUNC_DEF(v) (((v)->type == FH_VAL_FUNC_DEF) ? ((struct fh_func_def *) ((v)->data.obj)) : NULL) 117 | #define GET_VAL_ARRAY(v) (((v)->type == FH_VAL_ARRAY ) ? ((struct fh_array *) ((v)->data.obj)) : NULL) 118 | #define GET_VAL_MAP(v) (((v)->type == FH_VAL_MAP ) ? ((struct fh_map *) ((v)->data.obj)) : NULL) 119 | #define GET_VAL_STRING(v) (((v)->type == FH_VAL_STRING ) ? ((struct fh_string *) ((v)->data.obj)) : NULL) 120 | #define GET_VAL_STRING_DATA(v) (((v)->type == FH_VAL_STRING ) ? GET_OBJ_STRING_DATA((v)->data.obj) : NULL) 121 | 122 | #define UPVAL_IS_OPEN(uv) ((uv)->val != (uv)->data.storage) 123 | 124 | // non-object types 125 | #define fh_make_null fh_new_null 126 | #define fh_make_bool fh_new_bool 127 | #define fh_make_number fh_new_number 128 | #define fh_make_c_func fh_new_c_func 129 | 130 | // object types 131 | struct fh_func_def *fh_make_func_def(struct fh_program *prog, bool pinned); 132 | struct fh_closure *fh_make_closure(struct fh_program *prog, bool pinned, struct fh_func_def *func_def); 133 | struct fh_upval *fh_make_upval(struct fh_program *prog, bool pinned); 134 | struct fh_array *fh_make_array(struct fh_program *prog, bool pinned); 135 | struct fh_map *fh_make_map(struct fh_program *prog, bool pinned); 136 | struct fh_string *fh_make_string(struct fh_program *prog, bool pinned, const char *str); 137 | struct fh_string *fh_make_string_n(struct fh_program *prog, bool pinned, const char *str, size_t str_len); 138 | 139 | // object functions 140 | void fh_free_object(union fh_object *obj); 141 | struct fh_value *fh_grow_array_object(struct fh_program *prog, struct fh_array *arr, uint32_t num_items); 142 | const char *fh_get_func_def_name(struct fh_func_def *func_def); 143 | int fh_alloc_map_object_len(struct fh_map *map, uint32_t len); 144 | int fh_next_map_object_key(struct fh_map *map, struct fh_value *key, struct fh_value *next_key); 145 | int fh_get_map_object_value(struct fh_map *map, struct fh_value *key, struct fh_value *val); 146 | int fh_add_map_object_entry(struct fh_program *prog, struct fh_map *map, struct fh_value *key, struct fh_value *val); 147 | int fh_delete_map_object_entry(struct fh_map *map, struct fh_value *key); 148 | 149 | DECLARE_STACK(value_stack, struct fh_value); 150 | 151 | #endif /* VALUE_H_FILE */ 152 | -------------------------------------------------------------------------------- /src/lib/vm.c: -------------------------------------------------------------------------------- 1 | /* vm.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "vm.h" 10 | #include "program.h" 11 | #include "bytecode.h" 12 | 13 | void fh_init_vm(struct fh_vm *vm, struct fh_program *prog) 14 | { 15 | vm->prog = prog; 16 | vm->stack = NULL; 17 | vm->stack_size = 0; 18 | vm->open_upvals = NULL; 19 | vm->last_error_loc = fh_make_src_loc(0,0,0); 20 | vm->last_error_addr = -1; 21 | vm->last_error_frame_index = -1; 22 | call_frame_stack_init(&vm->call_stack); 23 | } 24 | 25 | void fh_destroy_vm(struct fh_vm *vm) 26 | { 27 | if (vm->stack) 28 | free(vm->stack); 29 | call_frame_stack_free(&vm->call_stack); 30 | } 31 | 32 | static int vm_error(struct fh_vm *vm, char *fmt, ...) 33 | { 34 | va_list ap; 35 | va_start(ap, fmt); 36 | fh_set_verror(vm->prog, fmt, ap); 37 | va_end(ap); 38 | return -1; 39 | } 40 | 41 | static int ensure_stack_size(struct fh_vm *vm, int size) 42 | { 43 | if (vm->stack_size >= size) 44 | return 0; 45 | int new_size = (size + 1024 + 1) / 1024 * 1024; 46 | void *new_stack = realloc(vm->stack, new_size * sizeof(struct fh_value)); 47 | if (! new_stack) 48 | return vm_error(vm, "out of memory"); 49 | vm->stack = new_stack; 50 | vm->stack_size = new_size; 51 | return 0; 52 | } 53 | 54 | static struct fh_vm_call_frame *prepare_call(struct fh_vm *vm, struct fh_closure *closure, int ret_reg, int n_args) 55 | { 56 | struct fh_func_def *func_def = closure->func_def; 57 | 58 | if (ensure_stack_size(vm, ret_reg + 1 + func_def->n_regs) < 0) 59 | return NULL; 60 | if (n_args < func_def->n_params) 61 | memset(vm->stack + ret_reg + 1 + n_args, 0, (func_def->n_params - n_args) * sizeof(struct fh_value)); 62 | 63 | memset(vm->stack + ret_reg + 1 + func_def->n_params, 0, (func_def->n_regs - func_def->n_params) * sizeof(struct fh_value)); 64 | 65 | struct fh_vm_call_frame *frame = call_frame_stack_push(&vm->call_stack, NULL); 66 | if (! frame) { 67 | vm_error(vm, "out of memory"); 68 | return NULL; 69 | } 70 | frame->closure = closure; 71 | frame->base = ret_reg + 1; 72 | frame->ret_addr = NULL; 73 | return frame; 74 | } 75 | 76 | static struct fh_vm_call_frame *prepare_c_call(struct fh_vm *vm, int ret_reg, int n_args) 77 | { 78 | if (ensure_stack_size(vm, ret_reg + 1 + n_args) < 0) 79 | return NULL; 80 | 81 | struct fh_vm_call_frame *frame = call_frame_stack_push(&vm->call_stack, NULL); 82 | if (! frame) { 83 | vm_error(vm, "out of memory"); 84 | return NULL; 85 | } 86 | frame->closure = NULL; 87 | frame->base = ret_reg + 1; 88 | frame->ret_addr = NULL; 89 | return frame; 90 | } 91 | 92 | static void dump_val(char *label, struct fh_value *val) 93 | { 94 | printf("%s", label); 95 | fh_dump_value(val); 96 | printf("\n"); 97 | } 98 | 99 | static void dump_regs(struct fh_vm *vm) 100 | { 101 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 102 | struct fh_value *reg_base = vm->stack + frame->base; 103 | printf("--- base=%d, n_regs=%d\n", frame->base, frame->closure->func_def->n_regs); 104 | for (int i = 0; i < frame->closure->func_def->n_regs; i++) { 105 | printf("[%-3d] r%-2d = ", i+frame->base, i); 106 | dump_val("", ®_base[i]); 107 | } 108 | printf("----------------------------\n"); 109 | } 110 | 111 | int fh_call_vm_function(struct fh_vm *vm, struct fh_closure *closure, struct fh_value *args, int n_args, struct fh_value *ret) 112 | { 113 | if (n_args > closure->func_def->n_params) 114 | n_args = closure->func_def->n_params; 115 | 116 | struct fh_vm_call_frame *prev_frame = call_frame_stack_top(&vm->call_stack); 117 | int ret_reg = (prev_frame) ? prev_frame->base + prev_frame->closure->func_def->n_regs : 0; 118 | if (ensure_stack_size(vm, ret_reg + n_args + 1) < 0) 119 | return -1; 120 | memset(&vm->stack[ret_reg], 0, sizeof(struct fh_value)); 121 | if (args) 122 | memcpy(&vm->stack[ret_reg+1], args, n_args*sizeof(struct fh_value)); 123 | 124 | if (n_args < closure->func_def->n_regs) 125 | memset(&vm->stack[ret_reg+1+n_args], 0, (closure->func_def->n_regs-n_args)*sizeof(struct fh_value)); 126 | 127 | if (! prepare_call(vm, closure, ret_reg, n_args)) 128 | return -1; 129 | vm->pc = closure->func_def->code; 130 | if (fh_run_vm(vm) < 0) 131 | return -1; 132 | if (ret) 133 | *ret = vm->stack[ret_reg]; 134 | return 0; 135 | } 136 | 137 | static int call_c_func(struct fh_vm *vm, fh_c_func func, struct fh_value *ret, struct fh_value *args, int n_args) 138 | { 139 | int num_c_vals = value_stack_size(&vm->prog->c_vals); 140 | 141 | int r = func(vm->prog, ret, args, n_args); 142 | 143 | value_stack_set_size(&vm->prog->c_vals, num_c_vals); // release any objects created by the C function 144 | return r; 145 | } 146 | 147 | bool fh_val_is_true(struct fh_value *val) 148 | { 149 | if (val->type == FH_VAL_UPVAL) 150 | val = GET_OBJ_UPVAL(val)->val; 151 | switch (val->type) { 152 | case FH_VAL_NULL: return false; 153 | case FH_VAL_BOOL: return val->data.b; 154 | case FH_VAL_NUMBER: return val->data.num != 0.0; 155 | case FH_VAL_STRING: return GET_VAL_STRING_DATA(val)[0] != '\0'; 156 | case FH_VAL_ARRAY: return true; 157 | case FH_VAL_MAP: return true; 158 | case FH_VAL_CLOSURE: return true; 159 | case FH_VAL_FUNC_DEF: return true; 160 | case FH_VAL_C_FUNC: return true; 161 | case FH_VAL_UPVAL: return false; 162 | } 163 | return false; 164 | } 165 | 166 | bool fh_vals_are_equal(struct fh_value *v1, struct fh_value *v2) 167 | { 168 | if (v1->type == FH_VAL_UPVAL) 169 | v1 = GET_OBJ_UPVAL(v1)->val; 170 | if (v2->type == FH_VAL_UPVAL) 171 | v2 = GET_OBJ_UPVAL(v2)->val; 172 | 173 | if (v1->type != v2->type) 174 | return false; 175 | switch (v1->type) { 176 | case FH_VAL_NULL: return true; 177 | case FH_VAL_BOOL: return v1->data.b == v2->data.b; 178 | case FH_VAL_NUMBER: return v1->data.num == v2->data.num; 179 | case FH_VAL_C_FUNC: return v1->data.c_func == v2->data.c_func; 180 | case FH_VAL_ARRAY: return v1->data.obj == v2->data.obj; 181 | case FH_VAL_MAP: return v1->data.obj == v2->data.obj; 182 | case FH_VAL_CLOSURE: return v1->data.obj == v2->data.obj; 183 | case FH_VAL_FUNC_DEF: return v1->data.obj == v2->data.obj; 184 | case FH_VAL_UPVAL: return false; 185 | 186 | case FH_VAL_STRING: 187 | if (GET_VAL_STRING(v1)->hash != GET_VAL_STRING(v2)->hash) 188 | return false; 189 | return strcmp(GET_OBJ_STRING_DATA(v1->data.obj), GET_OBJ_STRING_DATA(v2->data.obj)) == 0; 190 | } 191 | return false; 192 | } 193 | 194 | static struct fh_upval *find_or_add_upval(struct fh_vm *vm, struct fh_value *val) 195 | { 196 | struct fh_upval **cur = &vm->open_upvals; 197 | while (*cur != NULL && (*cur)->val >= val) { 198 | if ((*cur)->val == val) 199 | return *cur; 200 | cur = &(*cur)->data.next; 201 | } 202 | struct fh_upval *uv = fh_make_upval(vm->prog, false); 203 | uv->val = val; 204 | uv->data.next = *cur; 205 | *cur = uv; 206 | return uv; 207 | } 208 | 209 | static void close_upval(struct fh_vm *vm) 210 | { 211 | struct fh_upval *uv = vm->open_upvals; 212 | //printf("CLOSING UPVAL %p (", (void *) uv); fh_dump_value(uv->val); printf(")\n"); 213 | vm->open_upvals = uv->data.next; 214 | uv->data.storage = *uv->val; 215 | uv->val = &uv->data.storage; 216 | } 217 | 218 | static void dump_state(struct fh_vm *vm) 219 | { 220 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 221 | printf("\n"); 222 | printf("****************************\n"); 223 | printf("***** HALTING ON ERROR *****\n"); 224 | printf("****************************\n"); 225 | printf("** current stack frame: "); 226 | if (frame) { 227 | if (frame->closure->func_def->name) 228 | printf("closure %p of %s\n", (void *) frame->closure, GET_OBJ_STRING_DATA(frame->closure->func_def->name)); 229 | else 230 | printf("closure %p of function %p\n", (void *) frame->closure, (void *) frame->closure->func_def); 231 | } else 232 | printf("no stack frame!\n"); 233 | dump_regs(vm); 234 | printf("** instruction that caused error:\n"); 235 | int addr = (frame) ? vm->pc - 1 - frame->closure->func_def->code : -1; 236 | fh_dump_bc_instr(vm->prog, addr, vm->pc[-1]); 237 | printf("----------------------------\n"); 238 | } 239 | 240 | static void save_error_loc(struct fh_vm *vm) 241 | { 242 | int frame_index = call_frame_stack_size(&vm->call_stack) - 1; 243 | struct fh_vm_call_frame *frame; 244 | do { 245 | frame = call_frame_stack_item(&vm->call_stack, frame_index); 246 | frame_index--; 247 | } while (frame && ! frame->closure); 248 | if (frame) { 249 | struct fh_func_def *func_def = frame->closure->func_def; 250 | vm->last_error_frame_index = frame_index; 251 | vm->last_error_addr = vm->pc - func_def->code; 252 | vm->last_error_loc = fh_get_addr_src_loc(func_def, vm->last_error_addr); 253 | } else { 254 | vm->last_error_frame_index = -1; 255 | vm->last_error_addr = -1; 256 | vm->last_error_loc = fh_make_src_loc(0,0,0); 257 | } 258 | } 259 | 260 | #define handle_op(op) case op: 261 | #define LOAD_REG_OR_CONST(index) (((index) <= MAX_FUNC_REGS) ? ®_base[index] : &const_base[(index)-MAX_FUNC_REGS-1]) 262 | #define LOAD_REG(index) (®_base[index]) 263 | #define LOAD_CONST(index) (&const_base[(index)-MAX_FUNC_REGS-1]) 264 | 265 | int fh_run_vm(struct fh_vm *vm) 266 | { 267 | struct fh_value *const_base; 268 | struct fh_value *reg_base; 269 | 270 | uint32_t *pc = vm->pc; 271 | 272 | changed_stack_frame: 273 | { 274 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 275 | const_base = frame->closure->func_def->consts; 276 | reg_base = vm->stack + frame->base; 277 | } 278 | while (1) { 279 | //dump_regs(vm); 280 | //fh_dump_bc_instr(vm->prog, -1, *pc); 281 | 282 | uint32_t instr = *pc++; 283 | struct fh_value *ra = ®_base[GET_INSTR_RA(instr)]; 284 | switch (GET_INSTR_OP(instr)) { 285 | handle_op(OPC_LDC) { 286 | *ra = const_base[GET_INSTR_RU(instr)]; 287 | break; 288 | } 289 | 290 | handle_op(OPC_LDNULL) { 291 | ra->type = FH_VAL_NULL; 292 | break; 293 | } 294 | 295 | handle_op(OPC_MOV) { 296 | *ra = *LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 297 | break; 298 | } 299 | 300 | handle_op(OPC_RET) { 301 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 302 | if (GET_INSTR_RA(instr)) 303 | vm->stack[frame->base-1] = *LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 304 | else 305 | vm->stack[frame->base-1].type = FH_VAL_NULL; 306 | 307 | // close function upvalues 308 | while (vm->open_upvals != NULL && vm->open_upvals->val >= vm->stack + frame->base) 309 | close_upval(vm); 310 | 311 | uint32_t *ret_addr = frame->ret_addr; 312 | call_frame_stack_pop(&vm->call_stack, NULL); 313 | if (call_frame_stack_size(&vm->call_stack) == 0 || ! ret_addr) { 314 | vm->pc = pc; 315 | return 0; 316 | } 317 | pc = ret_addr; 318 | goto changed_stack_frame; 319 | } 320 | 321 | handle_op(OPC_GETEL) { 322 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 323 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 324 | if (rb->type == FH_VAL_ARRAY) { 325 | if (rc->type != FH_VAL_NUMBER) { 326 | vm_error(vm, "invalid array access (non-numeric index)"); 327 | goto user_err; 328 | } 329 | struct fh_value *val = fh_get_array_item(rb, (int) rc->data.num); 330 | if (! val) { 331 | vm_error(vm, "invalid array index"); 332 | goto user_err; 333 | } 334 | *ra = *val; 335 | break; 336 | } else if (rb->type == FH_VAL_MAP) { 337 | if (fh_get_map_value(rb, rc, ra) < 0) { 338 | vm_error(vm, "key not in map"); 339 | goto user_err; 340 | } 341 | break; 342 | } 343 | vm_error(vm, "invalid element access (non-container object)"); 344 | goto user_err; 345 | } 346 | 347 | handle_op(OPC_SETEL) { 348 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 349 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 350 | if (ra->type == FH_VAL_ARRAY) { 351 | if (rb->type != FH_VAL_NUMBER) { 352 | vm_error(vm, "invalid array access (non-numeric index)"); 353 | goto user_err; 354 | } 355 | struct fh_value *val = fh_get_array_item(ra, (int) rb->data.num); 356 | if (! val) { 357 | vm_error(vm, "invalid array index"); 358 | goto user_err; 359 | } 360 | *val = *rc; 361 | break; 362 | } else if (ra->type == FH_VAL_MAP) { 363 | if (fh_add_map_entry(vm->prog, ra, rb, rc) < 0) 364 | goto err; 365 | break; 366 | } 367 | vm_error(vm, "invalid element access (non-container object)"); 368 | goto user_err; 369 | } 370 | 371 | handle_op(OPC_NEWARRAY) { 372 | int n_elems = GET_INSTR_RU(instr); 373 | 374 | struct fh_array *arr = fh_make_array(vm->prog, false); 375 | if (! arr) 376 | goto err; 377 | if (n_elems != 0) { 378 | GC_PIN_OBJ(arr); 379 | struct fh_value *first = fh_grow_array_object(vm->prog, arr, n_elems); 380 | if (! first) { 381 | GC_UNPIN_OBJ(arr); 382 | goto err; 383 | } 384 | GC_UNPIN_OBJ(arr); 385 | memcpy(first, ra + 1, n_elems*sizeof(struct fh_value)); 386 | } 387 | ra->type = FH_VAL_ARRAY; 388 | ra->data.obj = arr; 389 | break; 390 | } 391 | 392 | handle_op(OPC_NEWMAP) { 393 | int n_elems = GET_INSTR_RU(instr); 394 | 395 | struct fh_map *map = fh_make_map(vm->prog, false); 396 | if (! map) 397 | goto err; 398 | fh_alloc_map_object_len(map, n_elems/2); 399 | if (n_elems != 0) { 400 | GC_PIN_OBJ(map); 401 | for (int i = 0; i < n_elems/2; i++) { 402 | struct fh_value *key = &ra[2*i+1]; 403 | struct fh_value *val = &ra[2*i+2]; 404 | if (key->type == FH_VAL_NULL) { 405 | GC_UNPIN_OBJ(map); 406 | return vm_error(vm, "can't create array with null key"); 407 | } 408 | if (fh_add_map_object_entry(vm->prog, map, key, val) < 0) { 409 | GC_UNPIN_OBJ(map); 410 | goto err; 411 | } 412 | } 413 | GC_UNPIN_OBJ(map); 414 | } 415 | ra->type = FH_VAL_MAP; 416 | ra->data.obj = map; 417 | break; 418 | } 419 | 420 | handle_op(OPC_CLOSURE) { 421 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 422 | if (rb->type != FH_VAL_FUNC_DEF) { 423 | vm_error(vm, "invalid value for closure (not a func_def)"); 424 | goto err; 425 | } 426 | struct fh_func_def *func_def = GET_VAL_FUNC_DEF(rb); 427 | struct fh_closure *c = fh_make_closure(vm->prog, false, func_def); 428 | if (! c) 429 | goto err; 430 | GC_PIN_OBJ(c); 431 | struct fh_vm_call_frame *frame = NULL; 432 | for (int i = 0; i < func_def->n_upvals; i++) { 433 | if (func_def->upvals[i].type == FH_UPVAL_TYPE_UPVAL) { 434 | if (frame == NULL) 435 | frame = call_frame_stack_top(&vm->call_stack); 436 | c->upvals[i] = frame->closure->upvals[func_def->upvals[i].num]; 437 | } else { 438 | c->upvals[i] = find_or_add_upval(vm, ®_base[func_def->upvals[i].num]); 439 | GC_PIN_OBJ(c->upvals[i]); 440 | } 441 | } 442 | ra->type = FH_VAL_CLOSURE; 443 | ra->data.obj = c; 444 | for (int i = 0; i < func_def->n_upvals; i++) 445 | GC_UNPIN_OBJ(c->upvals[i]); 446 | GC_UNPIN_OBJ(c); 447 | break; 448 | } 449 | 450 | handle_op(OPC_GETUPVAL) { 451 | int b = GET_INSTR_RB(instr); 452 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 453 | *ra = *frame->closure->upvals[b]->val; 454 | break; 455 | } 456 | 457 | handle_op(OPC_SETUPVAL) { 458 | int a = GET_INSTR_RA(instr); 459 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 460 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 461 | *frame->closure->upvals[a]->val = *rb; 462 | break; 463 | } 464 | 465 | handle_op(OPC_ADD) { 466 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 467 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 468 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 469 | vm_error(vm, "arithmetic on non-numeric values"); 470 | goto user_err; 471 | } 472 | ra->type = FH_VAL_NUMBER; 473 | ra->data.num = rb->data.num + rc->data.num; 474 | break; 475 | } 476 | 477 | handle_op(OPC_SUB) { 478 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 479 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 480 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 481 | vm_error(vm, "arithmetic on non-numeric values"); 482 | goto user_err; 483 | } 484 | ra->type = FH_VAL_NUMBER; 485 | ra->data.num = rb->data.num - rc->data.num; 486 | break; 487 | } 488 | 489 | handle_op(OPC_MUL) { 490 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 491 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 492 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 493 | vm_error(vm, "arithmetic on non-numeric values"); 494 | goto user_err; 495 | } 496 | ra->type = FH_VAL_NUMBER; 497 | ra->data.num = rb->data.num * rc->data.num; 498 | break; 499 | } 500 | 501 | handle_op(OPC_DIV) { 502 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 503 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 504 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 505 | vm_error(vm, "arithmetic on non-numeric values"); 506 | goto user_err; 507 | } 508 | ra->type = FH_VAL_NUMBER; 509 | ra->data.num = rb->data.num / rc->data.num; 510 | break; 511 | } 512 | 513 | handle_op(OPC_MOD) { 514 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 515 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 516 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 517 | vm_error(vm, "arithmetic on non-numeric values"); 518 | goto user_err; 519 | } 520 | ra->type = FH_VAL_NUMBER; 521 | ra->data.num = fmod(rb->data.num, rc->data.num); 522 | break; 523 | } 524 | 525 | handle_op(OPC_NEG) { 526 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 527 | if (rb->type != FH_VAL_NUMBER) { 528 | vm_error(vm, "arithmetic on non-numeric value"); 529 | goto user_err; 530 | } 531 | ra->type = FH_VAL_NUMBER; 532 | ra->data.num = -rb->data.num; 533 | break; 534 | } 535 | 536 | handle_op(OPC_NOT) { 537 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 538 | *ra = fh_new_bool(! fh_val_is_true(rb)); 539 | break; 540 | } 541 | 542 | handle_op(OPC_CALL) { 543 | //dump_regs(vm); 544 | struct fh_vm_call_frame *frame = call_frame_stack_top(&vm->call_stack); 545 | if (ra->type == FH_VAL_CLOSURE) { 546 | uint32_t *func_addr = GET_OBJ_CLOSURE(ra->data.obj)->func_def->code; 547 | 548 | /* 549 | * WARNING: prepare_call() may move the stack, so don't trust reg_base 550 | * or ra after calling it -- jumping to changed_stack_frame fixes it. 551 | */ 552 | struct fh_vm_call_frame *new_frame = prepare_call(vm, GET_OBJ_CLOSURE(ra->data.obj), frame->base + GET_INSTR_RA(instr), GET_INSTR_RB(instr)); 553 | if (! new_frame) 554 | goto err; 555 | new_frame->ret_addr = pc; 556 | pc = func_addr; 557 | goto changed_stack_frame; 558 | } 559 | if (ra->type == FH_VAL_C_FUNC) { 560 | fh_c_func c_func = ra->data.c_func; 561 | 562 | /* 563 | * WARNING: above warning about prepare_call() also applies to prepare_c_call() 564 | */ 565 | struct fh_vm_call_frame *new_frame = prepare_c_call(vm, frame->base + GET_INSTR_RA(instr), GET_INSTR_RB(instr)); 566 | if (! new_frame) 567 | goto err; 568 | int ret = call_c_func(vm, c_func, vm->stack + new_frame->base - 1, vm->stack + new_frame->base, GET_INSTR_RB(instr)); 569 | call_frame_stack_pop(&vm->call_stack, NULL); 570 | if (ret < 0) 571 | goto user_err; 572 | goto changed_stack_frame; 573 | } 574 | vm_error(vm, "call to non-function value"); 575 | goto user_err; 576 | } 577 | 578 | handle_op(OPC_JMP) { 579 | int a = GET_INSTR_RA(instr); 580 | for (int i = 0; i < a; i++) 581 | close_upval(vm); 582 | pc += GET_INSTR_RS(instr); 583 | break; 584 | } 585 | 586 | handle_op(OPC_TEST) { 587 | int a = GET_INSTR_RA(instr); 588 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 589 | int test = fh_val_is_true(rb) ^ a; 590 | if (test) { 591 | pc++; 592 | break; 593 | } 594 | pc += GET_INSTR_RS(*pc) + 1; 595 | break; 596 | } 597 | 598 | handle_op(OPC_CMP_EQ) { 599 | int inv = GET_INSTR_RA(instr); 600 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 601 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 602 | int test = fh_vals_are_equal(rb, rc) ^ inv; 603 | if (test) 604 | pc++; 605 | break; 606 | } 607 | 608 | handle_op(OPC_CMP_LT) { 609 | int inv = GET_INSTR_RA(instr); 610 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 611 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 612 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 613 | vm_error(vm, "using < with non-numeric values"); 614 | goto user_err; 615 | } 616 | int test = (rb->data.num < rc->data.num) ^ inv; 617 | if (test) 618 | pc++; 619 | break; 620 | } 621 | 622 | handle_op(OPC_CMP_LE) { 623 | int inv = GET_INSTR_RA(instr); 624 | struct fh_value *rb = LOAD_REG_OR_CONST(GET_INSTR_RB(instr)); 625 | struct fh_value *rc = LOAD_REG_OR_CONST(GET_INSTR_RC(instr)); 626 | if (rb->type != FH_VAL_NUMBER || rc->type != FH_VAL_NUMBER) { 627 | vm_error(vm, "using <= with non-numeric values"); 628 | goto user_err; 629 | } 630 | int test = (rb->data.num <= rc->data.num) ^ inv; 631 | if (test) 632 | pc++; 633 | break; 634 | } 635 | 636 | default: 637 | vm_error(vm, "unhandled opcode"); 638 | goto err; 639 | } 640 | } 641 | 642 | err: 643 | vm->pc = pc; 644 | save_error_loc(vm); 645 | dump_state(vm); 646 | return -1; 647 | 648 | user_err: 649 | vm->pc = pc; 650 | save_error_loc(vm); 651 | //dump_state(vm); 652 | return -1; 653 | } 654 | -------------------------------------------------------------------------------- /src/lib/vm.h: -------------------------------------------------------------------------------- 1 | /* vm.h */ 2 | 3 | #ifndef VM_H_FILE 4 | #define VM_H_FILE 5 | 6 | #include "fh_internal.h" 7 | #include "stack.h" 8 | 9 | struct fh_vm_call_frame { 10 | struct fh_closure *closure; 11 | int base; 12 | uint32_t *ret_addr; 13 | }; 14 | 15 | DECLARE_STACK(call_frame_stack, struct fh_vm_call_frame); 16 | 17 | struct fh_program; 18 | 19 | struct fh_vm { 20 | struct fh_program *prog; 21 | struct fh_value *stack; 22 | int stack_size; 23 | struct fh_upval *open_upvals; 24 | struct call_frame_stack call_stack; 25 | uint32_t *pc; 26 | struct fh_src_loc last_error_loc; 27 | int last_error_addr; 28 | int last_error_frame_index; 29 | }; 30 | 31 | void fh_init_vm(struct fh_vm *vm, struct fh_program *prog); 32 | void fh_destroy_vm(struct fh_vm *vm); 33 | int fh_call_vm_function(struct fh_vm *vm, struct fh_closure *closure, struct fh_value *args, int n_args, struct fh_value *ret); 34 | int fh_run_vm(struct fh_vm *vm); 35 | 36 | #endif /* VM_H_FILE */ 37 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* main.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "functions.h" 9 | 10 | static int run_string(struct fh_program *prog, bool dump_bytecode, const char *string) 11 | { 12 | char *template = "function main(){%s;}"; 13 | char *code = malloc(strlen(template) - 2 + strlen(string) + 1); 14 | if (! code) { 15 | fh_set_error(prog, "out of memory for string"); 16 | return -1; 17 | } 18 | sprintf(code, template, string); 19 | 20 | struct fh_input *in = fh_open_input_string(code); 21 | if (! in) { 22 | free(code); 23 | fh_set_error(prog, "out of memory for string input"); 24 | return -1; 25 | } 26 | free(code); 27 | 28 | if (add_functions(prog) < 0) 29 | return -1; 30 | 31 | if (fh_compile_input(prog, in) < 0) 32 | return -1; 33 | 34 | if (dump_bytecode) 35 | fh_dump_bytecode(prog); 36 | 37 | struct fh_value script_ret; 38 | if (fh_call_function(prog, "main", NULL, 0, &script_ret) < 0) 39 | return -1; 40 | 41 | if (fh_is_number(&script_ret)) 42 | return (int) fh_get_number(&script_ret); 43 | return 0; 44 | } 45 | 46 | static int run_script_file(struct fh_program *prog, bool dump_bytecode, char *filename, char **args, int n_args) 47 | { 48 | if (add_functions(prog) < 0) 49 | return -1; 50 | 51 | if (fh_compile_file(prog, filename) < 0) 52 | return -1; 53 | 54 | if (dump_bytecode) 55 | fh_dump_bytecode(prog); 56 | 57 | struct fh_value script_args = fh_new_array(prog); 58 | if (fh_is_null(&script_args)) 59 | return -1; 60 | struct fh_value *items = fh_grow_array(prog, &script_args, n_args+1); 61 | if (! items) 62 | return -1; 63 | items[0] = fh_new_string(prog, filename); 64 | for (int i = 0; i < n_args; i++) 65 | items[i+1] = fh_new_string(prog, args[i]); 66 | 67 | struct fh_value script_ret; 68 | if (fh_call_function(prog, "main", &script_args, 1, &script_ret) < 0) 69 | return -1; 70 | 71 | if (fh_is_number(&script_ret)) 72 | return (int) fh_get_number(&script_ret); 73 | return 0; 74 | } 75 | 76 | static void print_usage(char *progname) 77 | { 78 | printf("USAGE: %s [options] [filename [args...]]\n", progname); 79 | printf("\n"); 80 | printf("options:\n"); 81 | printf("\n"); 82 | printf(" -e STRING execute STRING\n"); 83 | printf(" -d dump bytecode before execution\n"); 84 | printf(" -h display this help\n"); 85 | printf("\n"); 86 | printf("Source code: \n"); 87 | } 88 | 89 | int main(int argc, char **argv) 90 | { 91 | char *execute_code = NULL; 92 | char *filename = NULL; 93 | char **args = NULL; 94 | int num_args = 0; 95 | bool dump_bytecode = false; 96 | 97 | for (int i = 1; i < argc; i++) { 98 | if (argv[i][0] != '-') { 99 | filename = argv[i]; 100 | args = argv + i + 1; 101 | num_args = argc - i - 1; 102 | break; 103 | } 104 | switch (argv[i][1]) { 105 | case 'h': 106 | print_usage(argv[0]); 107 | return 0; 108 | 109 | case 'd': 110 | dump_bytecode = true; 111 | break; 112 | 113 | case 'e': 114 | execute_code = argv[++i]; 115 | if (! execute_code) { 116 | printf("%s: option '-e' requires an argument\n", argv[0]); 117 | return 1; 118 | } 119 | break; 120 | 121 | default: 122 | printf("%s: unknown option '%s'\n", argv[0], argv[i]); 123 | return 1; 124 | } 125 | } 126 | if (! filename && ! execute_code) { 127 | print_usage(argv[0]); 128 | return 0; 129 | } 130 | 131 | struct fh_program *prog = fh_new_program(); 132 | if (! prog) { 133 | printf("ERROR: out of memory for program\n"); 134 | return 1; 135 | } 136 | 137 | int ret; 138 | if (execute_code) 139 | ret = run_string(prog, dump_bytecode, execute_code); 140 | else 141 | ret = run_script_file(prog, dump_bytecode, filename, args, num_args); 142 | if (ret < 0) { 143 | printf("ERROR: %s\n", fh_get_error(prog)); 144 | ret = 1; 145 | } 146 | fh_free_program(prog); 147 | return ret; 148 | } 149 | -------------------------------------------------------------------------------- /tests/1.fh: -------------------------------------------------------------------------------- 1 | 2 | function f(x,y) 3 | { 4 | return 2*x + 3*y; 5 | } 6 | -------------------------------------------------------------------------------- /tests/2.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | print("Hello, world!", f(1, 2)); 5 | } 6 | -------------------------------------------------------------------------------- /tests/args.fh: -------------------------------------------------------------------------------- 1 | 2 | function main(arg1, arg2) 3 | { 4 | printf("arg1 = %s\n", arg1); 5 | printf("arg2 = %s\n", arg2); 6 | } 7 | -------------------------------------------------------------------------------- /tests/array.fh: -------------------------------------------------------------------------------- 1 | 2 | function print_array(arr) 3 | { 4 | var arr_len = len(arr); 5 | printf("array has %d elements:\n", arr_len); 6 | var i = 0; 7 | while (i < arr_len) { 8 | printf(" [%d] = %s\n", i, arr[i]); 9 | i = i + 1; 10 | } 11 | } 12 | 13 | function main(args) 14 | { 15 | var x = [0, 1, [2], 3]; 16 | 17 | delete(x, 0); 18 | append(x, "four", "五"); 19 | 20 | printf("BEFORE: "); 21 | print_array(x); 22 | 23 | x[0] = "hello, world!"; 24 | 25 | printf("AFTER: "); 26 | print_array(x); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tests/bad_byte.fh: -------------------------------------------------------------------------------- 1 | 2 | function main(args) 3 | { 4 | ¡; 5 | } 6 | -------------------------------------------------------------------------------- /tests/bench_call.fh: -------------------------------------------------------------------------------- 1 | 2 | # function call speed benchmark 3 | 4 | function f(x) 5 | { 6 | return x; 7 | } 8 | 9 | function main() 10 | { 11 | var i = 0; 12 | while (i < 200000000) { 13 | f(i); 14 | i=i+1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/bench_mandel.fh: -------------------------------------------------------------------------------- 1 | 2 | function calc_point(cx, cy, max_iter) 3 | { 4 | var i = 0; 5 | var x = 0; 6 | var y = 0; 7 | while (i < max_iter) { 8 | var t = x*x - y*y + cx; 9 | y = 2*x*y + cy; 10 | x = t; 11 | if (x*x + y*y > 4) 12 | break; 13 | i = i + 1; 14 | } 15 | return i; 16 | } 17 | 18 | function mandelbrot(x1,y1, x2,y2, size_x,size_y, max_iter) 19 | { 20 | var step_x = (x2-x1)/(size_x-1); 21 | var step_y = (y2-y1)/(size_y-1); 22 | 23 | var y = y1; 24 | while (y <= y2) { 25 | var x = x1; 26 | while (x <= x2) { 27 | var c = calc_point(x, y, max_iter); 28 | if (c == max_iter) 29 | "."; 30 | else 31 | c%10; 32 | x = x + step_x; 33 | } 34 | y = y + step_y; 35 | } 36 | } 37 | 38 | function main() 39 | { 40 | mandelbrot(-2, -2, 2, 2, 500, 500, 1500); 41 | } 42 | -------------------------------------------------------------------------------- /tests/block.fh: -------------------------------------------------------------------------------- 1 | 2 | function f() 3 | { 4 | var x = 0; 5 | { 6 | var k = 0; 7 | } 8 | print(k); # error: unknown variable 'k' 9 | print(x); 10 | } 11 | -------------------------------------------------------------------------------- /tests/closure.fh: -------------------------------------------------------------------------------- 1 | 2 | function make_counter(num) { 3 | return { 4 | "next" : function() { 5 | num = num + 1; 6 | }, 7 | 8 | "read" : function() { 9 | return num; 10 | }, 11 | }; 12 | } 13 | 14 | function main() { 15 | print("Hello, world!\n"); 16 | 17 | var c1 = make_counter(0); 18 | var c2 = make_counter(10); 19 | c1.next(); 20 | c2.next(); 21 | printf("%d, %d\n", c1.read(), c2.read()); # prints 1, 11 22 | 23 | c1.next(); 24 | if (c1.read() == 2 && c2.read() == 11) { 25 | printf("ok!\n"); 26 | } else { 27 | error("this should will not happen"); 28 | } 29 | 30 | var i = 1; 31 | while (i <= 10) { 32 | if (i == 6) 33 | break; 34 | printf("%d\n", i); 35 | i = i + 1; 36 | } 37 | 38 | return; 39 | error("this should will not happen"); 40 | } 41 | -------------------------------------------------------------------------------- /tests/compilation_error.fh: -------------------------------------------------------------------------------- 1 | 2 | function f() 3 | { 4 | x = 1; 5 | } 6 | -------------------------------------------------------------------------------- /tests/error.fh: -------------------------------------------------------------------------------- 1 | 2 | include "error_include.fh" 3 | 4 | function main() 5 | { 6 | make_error("hello!"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/error_include.fh: -------------------------------------------------------------------------------- 1 | 2 | function make_error(msg) 3 | { 4 | printf("will cause error '%s'\n", msg); 5 | error(msg); 6 | } 7 | -------------------------------------------------------------------------------- /tests/func.fh: -------------------------------------------------------------------------------- 1 | 2 | function call_with_goodbye(f) 3 | { 4 | function(x) { 5 | x("goodbye"); 6 | }(f); 7 | } 8 | 9 | function test() 10 | { 11 | var i = 0; 12 | while (i < 10) { 13 | function(x) { print(x, "\n"); }(i); 14 | i = i + 1; 15 | } 16 | gc(); # check release of all created closures 17 | } 18 | 19 | function main(args) 20 | { 21 | var f = function(x) { 22 | printf("%s from inner function\n", x); 23 | }; 24 | f("hello"); 25 | call_with_goodbye(f); 26 | test(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/gc.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | var i = 0; 5 | while (i < 10000000000) { 6 | var x = [1,2,3]; 7 | x[2] = x; 8 | i = i + 1; 9 | if (i%100000 == 0) print(i,"\n"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/half_func.fh: -------------------------------------------------------------------------------- 1 | 2 | function main(args) 3 | { 4 | var x = 42; 5 | printf("before: %d\n", x); 6 | -------------------------------------------------------------------------------- /tests/if.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | var x = 1; 5 | if (x == 1) 6 | printf("yes!\n"); 7 | else 8 | printf("oops...\n"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/include.fh: -------------------------------------------------------------------------------- 1 | 2 | include "half_func.fh" 3 | 4 | printf("after: %d\n", x); 5 | return x; 6 | } 7 | -------------------------------------------------------------------------------- /tests/logic.fh: -------------------------------------------------------------------------------- 1 | 2 | function fn_true() { printf("fn true\n"); return true; } 3 | function fn_false() { printf("fn false\n"); return false; } 4 | 5 | function x() {} 6 | 7 | function test_and() 8 | { 9 | var x = true; 10 | var y = false; 11 | if (x==true && y==false) 12 | printf("yep\n"); 13 | else 14 | printf("nope\n"); 15 | } 16 | 17 | function main() 18 | { 19 | test_and(); 20 | var x = true; 21 | x = !!x; 22 | printf("%s\n", x); 23 | var r = fn_false() && fn_true(); 24 | printf("r=%s\n", r); 25 | if (r) { 26 | print("yes\n"); 27 | } else { 28 | print("no\n"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/mandel_color.fh: -------------------------------------------------------------------------------- 1 | 2 | function calc_point(cx, cy, max_iter) 3 | { 4 | var i = 0; 5 | var x = 0; 6 | var y = 0; 7 | while (i < max_iter) { 8 | var t = x*x - y*y + cx; 9 | y = 2*x*y + cy; 10 | x = t; 11 | if (x*x + y*y > 4) 12 | break; 13 | i = i + 1; 14 | } 15 | return i; 16 | } 17 | 18 | function mandelbrot(x1,y1, x2,y2, size_x,size_y, max_iter) 19 | { 20 | var step_x = (x2-x1)/(size_x-1); 21 | var step_y = (y2-y1)/(size_y-1); 22 | 23 | var y = y1; 24 | while (y <= y2) { 25 | var x = x1; 26 | while (x <= x2) { 27 | var c = calc_point(x, y, max_iter); 28 | if (c == max_iter) 29 | printf("\e[37;40m "); 30 | else 31 | printf("\e[37;4%dm ", c%6+1); 32 | x = x + step_x; 33 | } 34 | y = y + step_y; 35 | printf("\e[0m\n"); 36 | } 37 | } 38 | 39 | function main() 40 | { 41 | var lines = get_term_lines(); 42 | if (lines <= 0) 43 | lines = 25; 44 | lines = lines - 1; 45 | if (lines % 2 == 0) 46 | lines = lines - 1; 47 | var cols = 2.2*lines; 48 | mandelbrot(-2, -2, 2, 2, cols, lines, 1500); 49 | } 50 | -------------------------------------------------------------------------------- /tests/mandel_lib.fh: -------------------------------------------------------------------------------- 1 | 2 | function calc_point(cx, cy, max_iter) 3 | { 4 | var i = 0; 5 | var x = 0; 6 | var y = 0; 7 | while (i < max_iter) { 8 | var t = x*x - y*y + cx; 9 | y = 2*x*y + cy; 10 | x = t; 11 | if (x*x + y*y > 4) 12 | break; 13 | i = i + 1; 14 | } 15 | return i; 16 | } 17 | 18 | function mandelbrot(x1,y1, x2,y2, size_x,size_y, max_iter, out) 19 | { 20 | var step_x = (x2-x1)/(size_x-1); 21 | var step_y = (y2-y1)/(size_y-1); 22 | 23 | var y = y1; 24 | var ny = 0; 25 | while (ny < size_y) { 26 | var x = x1; 27 | var nx = 0; 28 | while (nx < size_x) { 29 | var c = calc_point(x, y, max_iter); 30 | if (c == max_iter) 31 | out.point_inside(); 32 | else 33 | out.point_outside(c); 34 | x = x + step_x; 35 | nx = nx + 1; 36 | } 37 | y = y + step_y; 38 | ny = ny + 1; 39 | out.end_line(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/mandel_text_color.fh: -------------------------------------------------------------------------------- 1 | 2 | function calc_point(cx, cy, max_iter) 3 | { 4 | var i = 0; 5 | var x = 0; 6 | var y = 0; 7 | while (i < max_iter) { 8 | var t = x*x - y*y + cx; 9 | y = 2*x*y + cy; 10 | x = t; 11 | if (x*x + y*y > 4) 12 | break; 13 | i = i + 1; 14 | } 15 | return i; 16 | } 17 | 18 | function mandelbrot(x1,y1, x2,y2, size_x,size_y, max_iter) 19 | { 20 | var step_x = (x2-x1)/(size_x-1); 21 | var step_y = (y2-y1)/(size_y-1); 22 | 23 | var y = y1; 24 | while (y <= y2) { 25 | var x = x1; 26 | while (x <= x2) { 27 | var c = calc_point(x, y, max_iter); 28 | if (c == max_iter) 29 | printf("\e[37m."); 30 | else 31 | printf("\e[3%dm%d", c%6+1, c%10); 32 | x = x + step_x; 33 | } 34 | y = y + step_y; 35 | printf("\e[0m\n"); 36 | } 37 | } 38 | 39 | function main() 40 | { 41 | var lines = get_term_lines(); 42 | if (lines <= 0) 43 | lines = 25; 44 | lines = lines - 1; 45 | if (lines % 2 == 0) 46 | lines = lines - 1; 47 | var cols = 2.2*lines; 48 | mandelbrot(-2, -2, 2, 2, cols, lines, 1500); 49 | } 50 | -------------------------------------------------------------------------------- /tests/mandelbrot.fh: -------------------------------------------------------------------------------- 1 | 2 | function calc_point(cx, cy, max_iter) 3 | { 4 | var i = 0; 5 | var x = 0; 6 | var y = 0; 7 | while (i < max_iter) { 8 | var t = x*x - y*y + cx; 9 | y = 2*x*y + cy; 10 | x = t; 11 | if (x*x + y*y > 4) 12 | break; 13 | i = i + 1; 14 | } 15 | return i; 16 | } 17 | 18 | function mandelbrot(x1,y1, x2,y2, size_x,size_y, max_iter) 19 | { 20 | var step_x = (x2-x1)/(size_x-1); 21 | var step_y = (y2-y1)/(size_y-1); 22 | 23 | var y = y1; 24 | while (y <= y2) { 25 | var x = x1; 26 | while (x <= x2) { 27 | var c = calc_point(x, y, max_iter); 28 | if (c == max_iter) 29 | printf("."); 30 | else 31 | printf("%d", c%10); 32 | x = x + step_x; 33 | } 34 | y = y + step_y; 35 | printf("\n"); 36 | } 37 | } 38 | 39 | function main() 40 | { 41 | var lines = get_term_lines(); 42 | if (lines <= 0) 43 | lines = 25; 44 | lines = lines - 1; 45 | if (lines % 2 == 0) 46 | lines = lines - 1; 47 | var cols = 2.2*lines; 48 | mandelbrot(-2, -2, 2, 2, cols, lines, 1500); 49 | } 50 | -------------------------------------------------------------------------------- /tests/map.fh: -------------------------------------------------------------------------------- 1 | 2 | function print_map(map) 3 | { 4 | var map_len = len(map); 5 | printf("{\n"); 6 | var k = next_key(map, null); 7 | while (k != null) { 8 | printf(" %s : %s,\n", k, map[k]); 9 | k = next_key(map, k); 10 | } 11 | printf("}\n"); 12 | } 13 | 14 | function f() 15 | { 16 | } 17 | 18 | function main(args) 19 | { 20 | var x = { 21 | "hello" : "world", 22 | true : "yes", 23 | false : "no", 24 | 5 : "五", 25 | "please_print" : function(x) { printf("OK, I'll print '%s'\n", x); }, 26 | "calc" : 1+2, 27 | printf : "func printf", 28 | print : "func print", 29 | }; 30 | 31 | if (x["calc"] != 3) 32 | error("calc"); 33 | delete(x, "calc"); 34 | 35 | x[main] = "main"; 36 | 37 | x["please_print"](42); 38 | 39 | if (! contains_key(x, 5)) 40 | error("nope"); 41 | if (contains_key(x, "not in array")) 42 | error("nope"); 43 | 44 | printf("printf -> %s\n", x[printf]); 45 | printf("hello -> %s\n", x["hello"]); 46 | 47 | print_map(x); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /tests/opt.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | "banana"; 5 | function(){}; 6 | } 7 | -------------------------------------------------------------------------------- /tests/parse_error.fh: -------------------------------------------------------------------------------- 1 | 2 | function f() 3 | { 4 | x = 1 + + 2; 5 | } 6 | -------------------------------------------------------------------------------- /tests/prop.fh: -------------------------------------------------------------------------------- 1 | 2 | function print_map(map) 3 | { 4 | var map_len = len(map); 5 | printf("{\n"); 6 | var k = next_key(map, null); 7 | while (k != null) { 8 | printf(" %s : %s,\n", k, map[k]); 9 | k = next_key(map, k); 10 | } 11 | printf("}\n"); 12 | } 13 | 14 | function main() 15 | { 16 | var printer = function(x) { printf("%s\n", x); }; 17 | var x = { 18 | "print" : printer, 19 | "test" : { 20 | "print" : printer, 21 | }, 22 | "getmap" : function() { return { "print" : printer }; }, 23 | }; 24 | print_map(x); 25 | print_map(x.test); 26 | x.print("test"); 27 | x.test.print(42); 28 | x.getmap().print("hello"); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/run_error.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | printf("%s\n"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/simple.fh: -------------------------------------------------------------------------------- 1 | 2 | function simple() 3 | { 4 | return 1+2; 5 | } 6 | 7 | function f(x,y) { return y; } 8 | function g(x,y,z) { return z; } 9 | 10 | function test() 11 | { 12 | var x = 0; 13 | var y = 1; 14 | var z = f(g(1,x,3), y); 15 | return z; 16 | } 17 | 18 | function main(f) 19 | { 20 | var x = 100; 21 | var y = 101; 22 | var z = 102; 23 | #var w = 103; 24 | 25 | var r = 2*x + 3*z; 26 | #r = x*y + z*w; 27 | 28 | r = test(x, y, r); 29 | r = test(0); 30 | 31 | return r; 32 | } 33 | 34 | function test_if(x) 35 | { 36 | if (0) 37 | x = 1; 38 | else 39 | x = 2; 40 | return x; 41 | } 42 | 43 | function test_while() 44 | { 45 | var x = 0; 46 | while (x < 10) { 47 | test(x); 48 | x = x + 1; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/simple2.fh: -------------------------------------------------------------------------------- 1 | 2 | function f(a, b) 3 | { 4 | return 1+a*b; 5 | } 6 | 7 | function main(x,y) 8 | { 9 | print(x); 10 | return 2*y + f(x, 3); 11 | } 12 | -------------------------------------------------------------------------------- /tests/test.fh: -------------------------------------------------------------------------------- 1 | 2 | function test(x) 3 | { 4 | printf("In function test(), x=%s\n", x); 5 | } 6 | 7 | function main() { 8 | print("Hello, World!\n"); 9 | 10 | var i = 0; 11 | while (i < 10) { 12 | if (i == 5) { 13 | printf("skipping 5\n"); 14 | i = i + 1; 15 | continue; 16 | } 17 | []; # create garbage 18 | printf("i=%d\n", i); 19 | i = i + 1; 20 | } 21 | 22 | gc(); 23 | test("hi"); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/test2.fh: -------------------------------------------------------------------------------- 1 | 2 | function print_array(a) 3 | { 4 | var i = 0; 5 | print("[\n"); 6 | while (i < len(a)) { 7 | printf(" %s,\n", a[i]); 8 | i = i + 1; 9 | } 10 | print("]\n"); 11 | } 12 | 13 | function f() { return 0; } 14 | 15 | function main() 16 | { 17 | var r = [0,0,0]; 18 | { 19 | var i = 0+f(3,4); 20 | while (i < 3) { 21 | var a = 0; 22 | var b; 23 | r[i] = function() { return 0; }; 24 | i = i + 1; 25 | } 26 | i = i + 2; 27 | } 28 | print_array(r); 29 | } 30 | -------------------------------------------------------------------------------- /tests/test_array.fh: -------------------------------------------------------------------------------- 1 | 2 | function print_array(arr) 3 | { 4 | var arr_len = len(arr); 5 | printf("array has %d elements:\n", arr_len); 6 | var i = 0; 7 | while (i < arr_len) { 8 | printf(" [%d] = %s\n", i, arr[i]); 9 | i = i + 1; 10 | } 11 | } 12 | 13 | function main(args) 14 | { 15 | var x = [1,[2],3,]; 16 | append(x, "four", "五"); 17 | 18 | if (len(x) != 5) error("nope"); 19 | 20 | x[0] = "hello, world!"; 21 | 22 | if (len(x) != 5) error("nope"); 23 | if (x[0] != "hello, world!") error("nope"); 24 | 25 | print("ok\n"); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests/test_closure.fh: -------------------------------------------------------------------------------- 1 | 2 | function add3(a) 3 | { 4 | return function(b) { 5 | return function(c) { 6 | return a + b + c; 7 | }; 8 | }; 9 | } 10 | 11 | function make_counter(c) 12 | { 13 | return [ 14 | function() { return c; }, 15 | function(n) { c = c + n; } 16 | ]; 17 | } 18 | 19 | function main() 20 | { 21 | var c1 = make_counter(0); 22 | var c2 = make_counter(100); 23 | 24 | c1[1](1); 25 | c1[1](1); 26 | if (c1[0]() != 2) error("nope"); 27 | 28 | c2[1](15); 29 | if (c2[0]() != 115) error("nope"); 30 | 31 | c1[1](1); 32 | if (c1[0]() != 3) error("nope"); 33 | 34 | if (add3(1)(2)(3) != 6) error("nope"); 35 | 36 | print("ok\n"); 37 | } 38 | -------------------------------------------------------------------------------- /tests/test_equals.fh: -------------------------------------------------------------------------------- 1 | 2 | function assert_eq(x, y) 3 | { 4 | if (! (x == y)) 5 | error("nope"); 6 | if (x != y) 7 | error("nope"); 8 | } 9 | 10 | function assert_neq(x, y) 11 | { 12 | if (x == y) 13 | error("nope"); 14 | if (! (x != y)) 15 | error("nope"); 16 | } 17 | 18 | function hello() 19 | { 20 | return "hello"; 21 | } 22 | 23 | function main(args) 24 | { 25 | var x; 26 | 27 | x = null; 28 | assert_eq(x, x); 29 | assert_neq(x, false); 30 | assert_neq(x, true); 31 | assert_neq(x, 0); 32 | assert_neq(x, []); 33 | assert_neq(x, function(){}); 34 | 35 | x = false; 36 | assert_eq(x, x); 37 | assert_neq(x, true); 38 | assert_neq(x, null); 39 | assert_neq(x, 0); 40 | assert_neq(x, []); 41 | assert_neq(x, function(){}); 42 | 43 | x = true; 44 | assert_eq(x, x); 45 | assert_neq(x, false); 46 | assert_neq(x, null); 47 | assert_neq(x, 0); 48 | assert_neq(x, []); 49 | assert_neq(x, function(){}); 50 | 51 | x = 42; 52 | assert_eq(x, x); 53 | assert_eq(x, 42); 54 | assert_neq(x, null); 55 | assert_neq(x, 0); 56 | assert_neq(x, []); 57 | assert_neq(x, function(){}); 58 | 59 | x = "hello"; 60 | assert_eq(x, x); 61 | assert_eq(x, "hello"); 62 | assert_eq(x, hello()); # force it to be a different string object 63 | assert_neq(x, null); 64 | assert_neq(x, 0); 65 | assert_neq(x, []); 66 | assert_neq(x, function(){}); 67 | 68 | x = [1]; 69 | assert_eq(x, x); 70 | assert_neq(x, [1]); 71 | assert_neq(x, null); 72 | assert_neq(x, 0); 73 | assert_neq(x, []); 74 | assert_neq(x, function(){}); 75 | 76 | x = function(){}; 77 | assert_eq(x, x); 78 | assert_neq(x, null); 79 | assert_neq(x, 0); 80 | assert_neq(x, []); 81 | assert_neq(x, function(){}); 82 | 83 | print("ok\n"); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_mandel.fh: -------------------------------------------------------------------------------- 1 | 2 | include "mandel_lib.fh" 3 | 4 | function main(args) 5 | { 6 | var plane = []; 7 | var cur_line = []; 8 | var out = { 9 | "point_inside" : function() { 10 | append(cur_line, 0); 11 | }, 12 | 13 | "point_outside" : function(c) { 14 | append(cur_line, c+1); 15 | }, 16 | 17 | "end_line" : function() { 18 | append(plane, cur_line); 19 | cur_line = []; 20 | }, 21 | }; 22 | 23 | mandelbrot(-2, -2, 2, 2, 101, 101, 1500, out); 24 | 25 | if (len(plane) != 101 || len(plane[0]) != 101) 26 | error("nope"); 27 | 28 | if (plane[ 0][ 0] != 1) error("nope"); 29 | if (plane[100][100] != 1) error("nope"); 30 | if (plane[ 50][ 2] < 1) error("nope"); 31 | if (plane[ 50][100] != 1) error("nope"); 32 | print("ok\n"); 33 | } 34 | -------------------------------------------------------------------------------- /tests/test_map.fh: -------------------------------------------------------------------------------- 1 | 2 | function main(args) 3 | { 4 | var x = { 5 | "hello" : "world", 6 | true : "yes", 7 | false : "no", 8 | 4 : "four" 9 | }; 10 | 11 | if (len(x) != 4) error("nope"); 12 | 13 | x[5] = "五"; 14 | 15 | if (len(x) != 5) error("nope"); 16 | if (x[5] != "五") error("nope"); 17 | if (x[true] != "yes") error("nope"); 18 | 19 | print("ok\n"); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /tests/test_prop.fh: -------------------------------------------------------------------------------- 1 | 2 | function main(args) 3 | { 4 | var x = { 5 | "x" : "x", 6 | "y" : { 7 | "z" : "z", 8 | "f" : function(x) { return x; }, 9 | }, 10 | "g" : function(x) { return x + 1; }, 11 | }; 12 | 13 | if (x.x != "x") error("nope"); 14 | if (x.y.z != "z") error("nope"); 15 | if (x.y["z"] != "z") error("nope"); 16 | if (x["y"].z != "z") error("nope"); 17 | if (x["y"]["z"] != "z") error("nope"); 18 | if (x.g(1) != 2) error("nope"); 19 | if (x.y.f(42) != 42) error("nope"); 20 | 21 | print("ok\n"); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/tokenizer_error.fh: -------------------------------------------------------------------------------- 1 | 2 | function f() 3 | { 4 | var x = " 5 | } 6 | -------------------------------------------------------------------------------- /tests/upvalue.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | var i = 0; 5 | while (i < 10) { 6 | i = i + 1; 7 | var a; 8 | { var x; } 9 | if (i == 5) continue; 10 | var x; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/use_mandel_lib.fh: -------------------------------------------------------------------------------- 1 | 2 | include "mandel_lib.fh" 3 | 4 | function get_text_output() 5 | { 6 | return { 7 | "point_inside" : function() { 8 | printf("."); 9 | }, 10 | 11 | "point_outside" : function(c) { 12 | printf("%d", c%10); 13 | }, 14 | 15 | "end_line" : function() { 16 | printf("\n"); 17 | }, 18 | }; 19 | } 20 | 21 | function get_color_output() 22 | { 23 | return { 24 | "point_inside" : function() { 25 | printf("\e[37;40m "); 26 | }, 27 | 28 | "point_outside" : function(c) { 29 | printf("\e[37;4%dm ", c%6+1); 30 | }, 31 | 32 | "end_line" : function() { 33 | printf("\e[0m\n"); 34 | }, 35 | }; 36 | } 37 | 38 | function main(args) 39 | { 40 | var lines = get_term_lines(); 41 | if (lines <= 0) 42 | lines = 25; 43 | lines = lines - 1; 44 | if (lines % 2 == 0) 45 | lines = lines - 1; 46 | var cols = 2.2*lines; 47 | mandelbrot(-2, -2, 2, 2, cols, lines, 1500, get_color_output()); 48 | } 49 | -------------------------------------------------------------------------------- /tests/while.fh: -------------------------------------------------------------------------------- 1 | 2 | function main() 3 | { 4 | var i = 0; 5 | while (i < 10) { 6 | if (i == 5) { 7 | i = i + 1; 8 | continue; 9 | } 10 | print(i, "\n"); 11 | i = i + 1; 12 | } 13 | } 14 | --------------------------------------------------------------------------------