#define STRING1(...) #__VA_ARGS__
#define STRING2(...) STRING1(__VA_ARGS__)
-#define EMIT(...) do { debug(STRING2(__VA_ARGS__) "\n"); sbVmCompiler_write_code(cm, (u8[]) { __VA_ARGS__ }, sizeof((u8[]) { __VA_ARGS__ })); } while (0)
+#define EMIT(...) do { \
+ if (cm->debugmode) debug(STRING2(__VA_ARGS__) "\n"); \
+ sbVmCompiler_write_code(cm, (u8[]) { __VA_ARGS__ }, sizeof((u8[]) { __VA_ARGS__ })); \
+} while (0)
#define EARG(x) (emit_arg(cm, x))
struct labelpos {
void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt);
void compile_expr(sbVmCompiler *cm, sbIrExpr *expr);
-void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir) {
- sbVmCompiler cm = sbVmCompiler_create(4096, 4096);
+void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir, flag debugmode) {
+ sbVmCompiler cm = sbVmCompiler_create(4096, 4096, debugmode);
int nchunks = ir->chunks.size / sizeof(sbIrChunk*);
for (int i = 0; i < nchunks; i++) {
/* --- */
void emit_arg(sbVmCompiler *cm, i64 actual_number) {
- debug(" arg %lld\n", (long long)actual_number);
+ if (cm->debugmode) debug(" arg %lld\n", (long long)actual_number);
+
u8 buf[9];
u64 number = actual_number;
if (0 <= actual_number && actual_number < 253) {
void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) {
- debug("\n--block %d--\n", chunk->id);
+ if (cm->debugmode) debug("\n--block %d--\n", chunk->id);
int nstmts = chunk->stmts.size / sizeof(sbIrStmt*);
if (chunk->id > 0) {
struct labelpos lp = ((struct labelpos*)cm->label_positions.data)[i];
u8 location_bytes[4];
u32 position = lp.label->block_position;
- debug("now we know that the jump at %d should go to %d\n", lp.offset - 2, position);
+ if (cm->debugmode) debug("now we know that the jump at %d should go to %d\n", lp.offset - 2, position);
location_bytes[0] = (position >> 24) & 0xFF;
location_bytes[1] = (position >> 16) & 0xFF;
location_bytes[2] = (position >> 8) & 0xFF;
void compile_list(sbVmCompiler *cm, sbIrExpr *expr);
void compile_bind_list(sbVmCompiler *cm, sbIrBindList *list);
void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) {
- debug("%3zu ", sbVmCompiler_get_position(cm));
+ if (cm->debugmode) debug("%3zu ", sbVmCompiler_get_position(cm));
switch (stmt->type) {
case IR_S_EXPR:
compile_expr(cm, stmt->expr);
compile_expr(cm, expr->send.target);
EMIT(BC_SEND);
break;
+ case IR_E_CONTEXT:
+ EMIT(BC_LD_CTX);
+ EARG(expr->symbol);
+ break;
case IR_E_VAR:
if (expr->var->is_upvalue) {
EMIT(BC_LD_UPVAL);
* have to essentially convert the tree to "reverse polish
* notation." */
-void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir);
+void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir, flag debugmode);
sbBuffer_deinitialize(&ir->chunks);
sbBuffer_deinitialize(&ir->varmapping);
+ sbBuffer_deinitialize(&ir->context_vars);
BUFFER_ITER(ir->buffers, sbBuffer*, buf) {
sbBuffer_deinitialize(*buf);
}
sbIrVariable *v = existing_var(ck, name, name_len, &index);
if (v == NO_VAR) {
- /* TODO do a different thing here */
- chunk_error(ck, "unknown variable name! '%s'\n", name);
- }
-
- if (index >= ck->lowest_var_id) {
+ /* not a lexical variable */
+ return NULL;
+ } else if (index >= ck->lowest_var_id) {
/* normal variable in inner function scope */
return v;
} else {
/* we can introduce new variables into a scope using LET */
static sbIrVariable *create_var(hIrChunk ck, const char *name, usize name_len) {
- sbIrVariable *new_var = sbArena_alloc(&ck->program->arena, sizeof(sbIrVariable));
-
sbIrVariable *already_existing = existing_var(ck, name, name_len, NULL);
if (already_existing != NO_VAR) {
// TODO this should probably just be a warning
return NO_VAR;
}
+ sbIrVariable *new_var = sbArena_alloc(&ck->program->arena, sizeof(sbIrVariable));
+
usize nvars = ck->program->varmapping.size / sizeof(varmapentry);
*new_var = (sbIrVariable) {0};
});
}
+static sbIrExpr *expr_context(hIrChunk ck, hSymbol symbol) {
+ return new_expr(ck, &(sbIrExpr) {
+ .type = IR_E_CONTEXT,
+ .symbol = symbol,
+ });
+}
+
static sbIrExpr *expr_value(hIrChunk ck, hV *value) {
return new_expr(ck, &(sbIrExpr) {
.type = IR_E_VALUE,
* RETURN a
* linearizes to:
* RETURN a
- *
+ *
* OK, this one's pretty simple.
*/
if (node->seq.left == NO_NODE) {
static sbIrVariable *compile_ast_var(hIrChunk ck, sbAst node) {
if (node->type == AST_NODE_NAME) {
+ /* TODO I don't want to use strlen. symbols should know their length */
return var_name(ck, sbSymbol_name(node->symb), strlen(sbSymbol_name(node->symb)));
} else {
/* TODO make errors not bad */
} else if (node->type == AST_VAL_NIL) {
return expr_value(ck, &HVNIL);
} else if (node->type == AST_NODE_NAME) {
- /* TODO: I don't want to use strlen. Can we remember the lengths of symbols? */
sbIrVariable *var = compile_ast_var(ck, node);
- if (var->introduced) {
+ if (!var) {
+ /* it is not a variable that was introduced anywhere. assume it is a context value */
+ return expr_context(ck, node->symb);
+ } else if (var->introduced) {
return expr_var(ck, var);
} else {
chunk_error(ck, "Variable '%s' has not been declared here!\n", sbSymbol_name(node->symb));
IR_E_VALUE,
IR_E_OP,
IR_E_VAR,
+ IR_E_CONTEXT,
IR_E_FUNC,
IR_E_CALL,
IR_E_SEND,
BY_DEF,
BY_LET,
BY_PARAM,
+ BY_CONTEXT,
} sbIrNameIntroduceType;
typedef struct sbIrLabel {
union {
hV value;
sbIrVariable *var;
+ hSymbol symbol;
struct {
struct sbIrChunk *chunk;
sbBuffer bound;
sbBuffer varmapping;
sbBuffer chunks;
sbBuffer buffers;
+ sbBuffer context_vars;
i32 error_count;
} sbIrProgram;
#define HVNOTHING ((hV) {0})
#define HVFUNC(i, c) ((hV) { .type = i, .closure = c })
#define HVFUNC2(i, c) ((hV) { .type = i | FLAG_SQUIGGLY, .closure = c })
+#define HVBUILTIN(b) ((hV) { .type = IT_BUILTIN, .builtin = b })
+
+struct sbVm;
typedef u64 hHash;
typedef u64 hString;
typedef u64 hList;
typedef u64 hRef;
typedef u64 hClosure;
+typedef void (*sbBuiltinFunc)(struct sbVm*, usize);
enum intrinsic_type {
IT_NOTHING, // sentinel for "no value here"
hRef ref;
u64 boolean;
double float_val;
+ sbBuiltinFunc builtin;
u64 data;
};
} hV;
#include "common.h"
-#include "lib/table.h"
#include "data/symbol.h"
#include "vm/exec.h"
-extern void sbList_create_methods();
-extern void sbString_create_methods();
-extern void sbInteger_create_methods();
+#include "lib/table.h"
+#include "lib/method.h"
+#include "lib/module.h"
void sbLib_sys_init() {
+ sbLibTable_initialize(&g_global_module, 16, FALSE);
+
+ sbLib_loadmodule_global();
+
sbLibTable_initialize(&g_list_methods, 16, TRUE);
sbLibTable_initialize(&g_string_methods, 16, TRUE);
sbLibTable_initialize(&g_integer_methods, 16, TRUE);
sbLibTable_deinitialize(&g_list_methods);
sbLibTable_deinitialize(&g_string_methods);
sbLibTable_deinitialize(&g_integer_methods);
+
+ sbLibTable_deinitialize(&g_global_module);
}
void sbLib_resolve_method(hVm vm) {
PANIC("invalid method name for type %lld: %s", (long long)target->type, sbSymbol_name(method_name_val->symbol));
}
}
+
+void sbLib_resolve_global(hVm vm) {
+ hV *target = sbVm_pop(vm);
+ if (target->type != IT_SYMBOL) {
+ CHECK("global lookup must be symbol!");
+ }
+ hV *v = sbLibTable_find_value(&g_global_module, target->symbol);
+ if (v) {
+ sbVm_push_immediate(vm, v);
+ } else {
+ PANIC("name '%s' is not defined", sbSymbol_name(target->symbol));
+ }
+}
void sbLib_resolve_method(hVm vm);
+void sbLib_resolve_global(hVm vm);
+
void sbLib_sys_init();
void sbLib_sys_deinit();
--- /dev/null
+extern sbLibTable g_list_methods;
+extern sbLibTable g_string_methods;
+extern sbLibTable g_integer_methods;
+
+void sbList_create_methods();
+void sbString_create_methods();
+void sbInteger_create_methods();
#include "data/string.h"
#include "vm/exec.h"
-void list_each_cfunc(hVm vm, flag init);
-void list_map_cfunc(hVm vm, flag init);
-void list_filter_cfunc(hVm vm, flag init);
-void list_any_cfunc(hVm vm, flag init);
-void list_all_cfunc(hVm vm, flag init);
-
sbLibTable g_integer_methods;
static void to_string(hVm vm, hV *target, usize num_params) {
#include "data/string.h"
#include "vm/exec.h"
-void list_each_cfunc(hVm vm, flag init);
-void list_map_cfunc(hVm vm, flag init);
-void list_filter_cfunc(hVm vm, flag init);
-void list_any_cfunc(hVm vm, flag init);
-void list_all_cfunc(hVm vm, flag init);
+sbCFuncStatus list_each_cfunc(hVm vm, flag init);
+sbCFuncStatus list_map_cfunc(hVm vm, flag init);
+sbCFuncStatus list_filter_cfunc(hVm vm, flag init);
+sbCFuncStatus list_any_cfunc(hVm vm, flag init);
+sbCFuncStatus list_all_cfunc(hVm vm, flag init);
sbLibTable g_list_methods;
/* --- */
-void list_each_cfunc(hVm vm, flag init) {
+sbCFuncStatus list_each_cfunc(hVm vm, flag init) {
if (init) {
/* store three state variables: the list we're iterating, the index into it, and the callback function */
sbVm_request_var_space(vm, 3);
sbVm_push(vm, &iter_values[current_index]);
sbVm_push_immediate(vm, &HVINT(1));
sbVm_call_func(vm, &vm->fp->locals[2]);
+ return CFUNC_NEXT;
} else {
/* if index was past the end, callback function won't be called, and we'll exit. return nil */
sbVm_push_immediate(vm, &HVNIL);
+ return CFUNC_END;
}
}
-void list_map_cfunc(hVm vm, flag init) {
+sbCFuncStatus list_map_cfunc(hVm vm, flag init) {
if (init) {
/* state: list being mapped, index, callback, result */
sbVm_request_var_space(vm, 4);
sbVm_push(vm, &iter_values[current_index]);
sbVm_push_immediate(vm, &HVINT(1));
sbVm_call_func(vm, &vm->fp->locals[2]);
+ return CFUNC_NEXT;
} else {
/* return result list */
sbVm_push_immediate(vm, &vm->fp->locals[3]);
+ return CFUNC_END;
}
}
-void list_filter_cfunc(hVm vm, flag init) {
+sbCFuncStatus list_filter_cfunc(hVm vm, flag init) {
if (init) {
/* state: list being filtered, index, callback, result */
sbVm_request_var_space(vm, 4);
sbVm_push(vm, &iter_values[current_index]);
sbVm_push_immediate(vm, &HVINT(1));
sbVm_call_func(vm, &vm->fp->locals[2]);
+ return CFUNC_NEXT;
} else {
/* return result list */
sbVm_push_immediate(vm, &vm->fp->locals[3]);
+ return CFUNC_END;
}
}
-void list_any_cfunc(hVm vm, flag init) {
+sbCFuncStatus list_any_cfunc(hVm vm, flag init) {
if (init) {
/* state: list being filtered, index, callback */
sbVm_request_var_space(vm, 3);
if (!sbV_c_falsy(mapped)) {
/* one was true! return true */
sbVm_push_immediate(vm, &HVBOOL(TRUE));
- return;
+ return CFUNC_END;
}
}
sbVm_push(vm, &iter_values[current_index]);
sbVm_push_immediate(vm, &HVINT(1));
sbVm_call_func(vm, &vm->fp->locals[2]);
+ return CFUNC_NEXT;
} else {
/* no result found */
sbVm_push_immediate(vm, &HVBOOL(FALSE));
+ return CFUNC_END;
}
}
-void list_all_cfunc(hVm vm, flag init) {
+sbCFuncStatus list_all_cfunc(hVm vm, flag init) {
if (init) {
/* state: list being filtered, index, callback */
sbVm_request_var_space(vm, 3);
if (sbV_c_falsy(mapped)) {
/* one was false; return false */
sbVm_push_immediate(vm, &HVBOOL(FALSE));
- return;
+ return CFUNC_END;
}
}
sbVm_push(vm, &iter_values[current_index]);
sbVm_push_immediate(vm, &HVINT(1));
sbVm_call_func(vm, &vm->fp->locals[2]);
+ return CFUNC_NEXT;
} else {
/* all passed */
sbVm_push_immediate(vm, &HVBOOL(TRUE));
+ return CFUNC_END;
}
}
sbVm_push_immediate(vm, &HVLIST(l));
}
+static void to_string(hVm vm, hV *target, usize num_params) {
+ if (num_params != 0) {
+ PANIC("to_string takes no parameters");
+ }
+ sbVm_pop(vm); /* remove method name */
+
+ /* to_string for a string just returns itself */
+ sbVm_push_immediate(vm, target);
+}
+
void sbString_create_methods(void) {
REGISTER_METHOD(&g_string_methods, "split", split);
+ REGISTER_METHOD(&g_string_methods, "to_string", to_string);
}
--- /dev/null
+extern sbLibTable g_global_module;
+
+void sbLib_loadmodule_global();
--- /dev/null
+#include "common.h"
+
+#include "lib/table.h"
+#include "data/list.h"
+#include "data/string.h"
+#include "data/symbol.h"
+#include "vm/exec.h"
+
+sbCFuncStatus print_cfunc(hVm vm, flag init);
+sbCFuncStatus println_cfunc(hVm vm, flag init);
+
+sbLibTable g_global_module;
+
+static void print(hVm vm, usize argc) {
+ sbVm_push_immediate(vm, &HVINT(argc));
+ sbVm_call_c_func(vm, print_cfunc);
+}
+
+static void println(hVm vm, usize argc) {
+ sbVm_push_immediate(vm, &HVINT(argc));
+ sbVm_call_c_func(vm, println_cfunc);
+}
+
+void sbLib_loadmodule_global() {
+ REGISTER_VALUE(&g_global_module, "print", &HVBUILTIN(print));
+ REGISTER_VALUE(&g_global_module, "println", &HVBUILTIN(println));
+}
+
+/* --- */
+
+sbCFuncStatus print_cfunc(hVm vm, flag init) {
+ hV *args_left;
+
+ if (!init) {
+ /* get value of previous to_string */
+ hV *value = sbVm_pop(vm);
+ /* TODO: We should probably have some kind of 'implicit convert to string'
+ * thing that checks that it's really a string and throws if not, that we
+ * can call from multiple places */
+ if (value->type != IT_STRING) {
+ PANIC("to_string needs to return a string");
+ }
+
+ char scratch[8];
+ const char *strdata = sbString_get_value(value->string, scratch, NULL);
+ printf("%s", strdata);
+ args_left = sbVm_pop(vm);
+ } else {
+ args_left = sbVm_pop(vm);
+ if (args_left->type != IT_INTEGER) {
+ CHECK("argc to function needs to be integer!");
+ }
+ }
+
+ /* TODO: Built-in symbols like 'to_string' should be saved globally, so we don't have to do this
+ * hashing at runtime. */
+ if (args_left->integer > 0) {
+ args_left->integer --;
+ sbVm_push_immediate(vm, args_left); /* ... param args_left */
+ sbVm_swap(vm); /* ... args_left param */
+ sbVm_push_immediate(vm, &HVSYM(sbSymbol_from_bytes("to_string", 9))); /* ... args_left param :to_string */
+ sbVm_swap(vm); /* ... args_left :to_string param */
+ sbVm_push_immediate(vm, &HVINT(1)); /* ... args_left :to_string param 1 */
+ sbVm_swap(vm); /* ... args_left :to_string 1 param */
+ sbLib_resolve_method(vm); /* call method */
+ return CFUNC_NEXT;
+ } else {
+ sbVm_push_immediate(vm, &HVNIL);
+ return CFUNC_END;
+ }
+}
+
+sbCFuncStatus println_cfunc(hVm vm, flag init) {
+ if (init) {
+ sbVm_call_c_func(vm, print_cfunc);
+ return CFUNC_NEXT;
+ } else {
+ printf("\n");
+ return CFUNC_END;
+ }
+}
typedef sbLibTable *hLibTable;
-extern sbLibTable g_list_methods;
-extern sbLibTable g_string_methods;
-extern sbLibTable g_integer_methods;
-
void sbLibTable_initialize(hLibTable t, usize capacity, flag method_table);
void sbLibTable_deinitialize(hLibTable t);
sbVmProgram pm;
sbVmProgram_initialize(&pm, 65536);
- sbEmit_compile_program(&pm, &ir);
+ sbEmit_compile_program(&pm, &ir, debugmode);
sbIrProgram_deinitialize(&ir);
sbVm_execute(&vm, &pm);
- printf("Stack result: ");
- for (hV **p = (hV**)vm.vstack; p < (hV**)vm.vsp; p++) {
- printf("%16llx %16llx ", (long long)(*p)->type, (long long)(*p)->data);
+ if (vm.debugmode) {
+ printf("Stack result: ");
+ for (hV **p = (hV**)vm.vstack; p < (hV**)vm.vsp; p++) {
+ printf("%16llx %16llx ", (long long)(*p)->type, (long long)(*p)->data);
+ }
+ printf("\n");
}
- printf("\n");
sbVmProgram_deinitialize(&pm);
sbVm_deinitialize(&vm);
#define INITIAL_PROGRAM_BLOCK_SIZE 32
-sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size) {
+sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size, flag debugmode) {
sbVmCompiler cm = {0};
+ cm.debugmode = debugmode;
sbBuffer_initialize(&cm.bytecode, initial_bytecode_size);
sbBuffer_initialize(&cm.constants, initial_constant_size);
sbBuffer_initialize(&cm.label_positions, 1024);
/* dynamic sized block that we can more easily add
* things to while compiling */
typedef struct sbVmCompiler {
+ flag debugmode;
sbBuffer bytecode;
sbBuffer constants;
sbBuffer label_positions;
typedef usize sbBlockId;
-sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size);
+sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size, flag debugmode);
void sbVmCompiler_reset(sbVmCompiler *pb);
hV *pop_stack(hVm vm);
hV *npop_stack(hVm vm, usize count);
hV *peek_stack(hVm vm, isize offset);
+void swap_stack_top(hVm vm);
void print_stack(hVm vm);
void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode) {
return peek_stack(vm, where);
}
+void sbVm_swap(hVm vm) {
+ swap_stack_top(vm);
+}
+
void sbVm_call_func(hVm vm, hV *func) {
call_block(vm, func->type, func->closure);
}
vm->fp = (sbVmStackFrame*)vm->rsp;
vm->rsp += sizeof(sbVmStackFrame);
- func(vm, TRUE);
+ sbCFuncStatus result = func(vm, TRUE);
- if (vm->fp->is_c_func) {
+ if (result == CFUNC_END) {
/* if c_func didn't call another callback, return from it */
+ printf("returning\n");
return_from_block(vm);
}
}
debug("\n");
}
+void call_builtin(hVm vm, hV *to_call) {
+ if (to_call->type != IT_BUILTIN) {
+ CHECK("call_builtin can only call builtins!");
+ }
+
+ hV *argc = sbVm_pop(vm);
+ if (argc->type != IT_INTEGER) {
+ CHECK("argc to builtin needs to be integer!");
+ }
+
+ to_call->builtin(vm, argc->integer);
+}
+
void call_block(hVm vm, usize block_id, hClosure closure) {
sbVmBlock *blk = &vm->program->blocks[block_id];
vm->fp = frame->last_fp;
vm->rsp = frame->last_rp;
vm->ip = frame->return_addr;
-
- if (vm->fp->is_c_func) {
- /* if we returned into a c-function call frame, go back to the c-function */
- vm->fp->c_func(vm, FALSE);
-
- if (vm->fp->is_c_func) {
- /* c-function didn't make a callback, so is done */
- return_from_block(vm);
- }
- }
}
}
/* execute one instruction! wow! */
void execute_instruction(hVm vm) {
+ if (vm->fp->is_c_func) {
+ /* if still in c func, do that instead of instruction */
+ sbCFuncStatus result = vm->fp->c_func(vm, FALSE);
+
+ if (result == CFUNC_END) {
+ /* c-function is done */
+ return_from_block(vm);
+ }
+
+ return;
+ }
+
sbOpcode op = get_opcode(vm);
if (vm->debugmode) debug("op %02X ", op);
u64 param;
push_stack_immediate(vm, &vm->fp->block_func.block->constants[param]);
break;
case BC_LD_CTX:
- PANIC("todo");
+ param = get_param(vm);
+ push_stack_immediate(vm, &HVSYM((hSymbol)param));
+ sbLib_resolve_global(vm);
+ break;
case BC_LD_VAR:
param = get_param(vm);
push_stack(vm, &vm->fp->locals[param]);
case BC_CALL:
v = pop_stack(vm);
if (v->type == IT_BUILTIN) {
+ call_builtin(vm, v);
} else if (v->type <= 0) {
/* We need to figure out exception support or some such.
* User error should not panic. */
/* Also these stacks grow upwards in memory, from low to high addresses. */
-typedef void (*sbRuntimeCFunc)(hVm, flag init);
+typedef enum {
+ CFUNC_END,
+ CFUNC_NEXT,
+} sbCFuncStatus;
+
+typedef sbCFuncStatus (*sbRuntimeCFunc)(hVm, flag init);
typedef enum sbVmStatus {
VM_STAT_UNKNOWN,
hV *sbVm_peek(hVm vm, usize where);
+void sbVm_swap(hVm vm);
+
void sbVm_call_func(hVm vm, hV *func);
void sbVm_call_c_func(hVm vm, sbRuntimeCFunc func);