2021-02-07 15:40:00 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Thakee Nathees
|
|
|
|
* Licensed under: MIT License
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vm.h"
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
#include "core.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
#define HAS_ERROR() (vm->error != NULL)
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Initially allocated call frame capacity. Will grow dynamically.
|
|
|
|
#define INITIAL_CALL_FRAMES 4
|
|
|
|
|
|
|
|
// Minimum size of the stack.
|
|
|
|
#define MIN_STACK_SIZE 16
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Track the total allocated memory of the VM to trigger the GC.
|
|
|
|
// if vmRealloc is called for freeing the old_size would be 0 since
|
|
|
|
// deallocated bytes are traced by garbage collector.
|
|
|
|
self->bytes_allocated += new_size - old_size;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// TODO: If vm->bytes_allocated > some_value -> GC();
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
if (new_size == 0) {
|
|
|
|
free(memory);
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
return realloc(memory, new_size);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
void vmInit(MSVM* self, MSConfiguration* config) {
|
2021-02-12 01:35:43 +08:00
|
|
|
memset(self, 0, sizeof(MSVM));
|
|
|
|
self->config = *config;
|
|
|
|
|
|
|
|
// TODO: no need to initialize if already done by another vm.
|
|
|
|
initializeCore(self);
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void vmPushTempRef(MSVM* self, Object* obj) {
|
2021-02-12 01:35:43 +08:00
|
|
|
ASSERT(obj != NULL, "Cannot reference to NULL.");
|
|
|
|
if (self->temp_reference_count < MAX_TEMP_REFERENCE,
|
|
|
|
"Too many temp references");
|
|
|
|
self->temp_reference[self->temp_reference_count++] = obj;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
void vmPopTempRef(MSVM* self) {
|
2021-02-12 01:35:43 +08:00
|
|
|
ASSERT(self->temp_reference_count > 0, "Temporary reference is empty to pop.");
|
|
|
|
self->temp_reference_count--;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
/******************************************************************************
|
|
|
|
* RUNTIME *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// TODO: A function for quick debug. REMOVE.
|
|
|
|
void _printStackTop(MSVM* vm) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (vm->sp != vm->stack) {
|
|
|
|
Var v = *(vm->sp - 1);
|
2021-02-13 01:40:19 +08:00
|
|
|
printf("%s\n", toString(vm, v, false)->data);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
static void ensureStackSize(MSVM* vm, int size) {
|
|
|
|
if (vm->stack_size > size) return;
|
|
|
|
TODO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void pushCallFrame(MSVM* vm, Function* fn) {
|
|
|
|
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
|
|
|
|
|
|
|
|
// Grow the stack frame if needed.
|
|
|
|
if (vm->frame_count + 1 > vm->frame_capacity) {
|
|
|
|
int new_capacity = vm->frame_capacity * 2;
|
|
|
|
vm->frames = (CallFrame*)vmRealloc(vm, vm->frames,
|
|
|
|
sizeof(CallFrame) * vm->frame_capacity,
|
|
|
|
sizeof(CallFrame) * new_capacity);
|
|
|
|
vm->frame_capacity = new_capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grow the stack if needed.
|
|
|
|
int stack_size = (int)(vm->sp - vm->stack);
|
|
|
|
int needed = stack_size + fn->fn->stack_size;
|
|
|
|
ensureStackSize(vm, needed);
|
|
|
|
|
|
|
|
CallFrame* frame = &vm->frames[vm->frame_count++];
|
|
|
|
frame->rbp = vm->rbp + 1; // vm->rbp is the return value.
|
|
|
|
frame->fn = fn;
|
|
|
|
frame->ip = fn->fn->opcodes.data;
|
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
|
2021-02-12 01:35:43 +08:00
|
|
|
vm->error = newString(vm, "TODO:", 5);
|
|
|
|
TODO;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void vmReportError(MSVM* vm) {
|
2021-02-12 01:35:43 +08:00
|
|
|
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error.");
|
|
|
|
ASSERT(false, "TODO: create debug.h");
|
|
|
|
}
|
|
|
|
|
|
|
|
MSVM* msNewVM(MSConfiguration* config) {
|
|
|
|
MSVM* vm = (MSVM*)malloc(sizeof(MSVM));
|
|
|
|
vmInit(vm, config);
|
|
|
|
return vm;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MSInterpretResult msInterpret(MSVM* vm, const char* file) {
|
2021-02-12 01:35:43 +08:00
|
|
|
Script* script = compileSource(vm, file);
|
|
|
|
if (script == NULL) return RESULT_COMPILE_ERROR;
|
|
|
|
|
|
|
|
// TODO: Check if scripts size is enough.
|
|
|
|
vm->scripts[vm->script_count++] = script;
|
|
|
|
return vmRunScript(vm, script);
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
register uint8_t* ip; //< Current instruction pointer.
|
|
|
|
register Var* rbp; //< Stack base pointer register.
|
|
|
|
register CallFrame* frame; //< Current call frame.
|
2021-02-13 01:40:19 +08:00
|
|
|
register Script* script; //< Currently executing script.
|
2021-02-11 01:23:48 +08:00
|
|
|
|
|
|
|
#define PUSH(value) (*vm->sp++ = (value))
|
|
|
|
#define POP() (*(--vm->sp))
|
|
|
|
#define DROP() (--vm->sp)
|
|
|
|
#define PEEK() (vm->sp - 1)
|
|
|
|
#define READ_BYTE() (*ip++)
|
|
|
|
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
|
|
|
|
|
|
|
|
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
2021-02-12 01:35:43 +08:00
|
|
|
#define CHECK_ERROR() \
|
|
|
|
do { \
|
|
|
|
if (HAS_ERROR()) { \
|
|
|
|
vmReportError(vm); \
|
|
|
|
return RESULT_RUNTIME_ERROR; \
|
|
|
|
} \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
#define RUNTIME_ERROR(fmt, ...) \
|
|
|
|
do { \
|
|
|
|
msSetRuntimeError(vm, fmt, __VA_ARGS__); \
|
|
|
|
vmReportError(vm); \
|
|
|
|
return RESULT_RUNTIME_ERROR; \
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
// Store the current frame to vm's call frame before pushing a new frame.
|
|
|
|
// Frames rbp will set once it created and will never change.
|
|
|
|
#define STORE_FRAME() frame->ip = ip
|
|
|
|
|
|
|
|
// Update the call frame and ip once vm's call frame pushed or popped.
|
|
|
|
// fuction call, return or done running imported script.
|
|
|
|
#define LOAD_FRAME() \
|
|
|
|
do { \
|
|
|
|
frame = &vm->frames[vm->frame_count-1]; \
|
|
|
|
ip = frame->ip; \
|
|
|
|
rbp = frame->rbp; \
|
|
|
|
script = frame->fn->owner; \
|
|
|
|
} while (false)
|
2021-02-11 01:23:48 +08:00
|
|
|
|
|
|
|
#ifdef OPCODE
|
|
|
|
#error "OPCODE" should not be deifined here.
|
|
|
|
#endif
|
|
|
|
|
2021-02-12 14:53:52 +08:00
|
|
|
#define DEBUG_INSTRUCTION() //_printStackTop(vm)
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
#define SWITCH(code) \
|
|
|
|
L_vm_main_loop: \
|
|
|
|
DEBUG_INSTRUCTION(); \
|
|
|
|
switch (code = (Opcode)READ_BYTE())
|
2021-02-11 01:23:48 +08:00
|
|
|
#define OPCODE(code) case OP_##code
|
|
|
|
#define DISPATCH() goto L_vm_main_loop
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Allocate stack.
|
|
|
|
int stack_size = utilPowerOf2Ceil(_script->body->fn->stack_size + 1);
|
|
|
|
if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
|
|
|
|
vm->stack_size = stack_size;
|
|
|
|
vm->stack = ALLOCATE_ARRAY(vm, Var, vm->stack_size);
|
|
|
|
vm->sp = vm->stack;
|
|
|
|
vm->rbp = vm->stack;
|
|
|
|
PUSH(VAR_NULL); // Return value of the script body.
|
|
|
|
|
|
|
|
// Allocate call frames.
|
|
|
|
vm->frame_capacity = INITIAL_CALL_FRAMES;
|
|
|
|
vm->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->frame_capacity);
|
|
|
|
vm->frame_count = 1;
|
|
|
|
|
|
|
|
// Initialize VM's first frame.
|
|
|
|
vm->frames[0].ip = _script->body->fn->opcodes.data;
|
|
|
|
vm->frames[0].fn = _script->body;
|
|
|
|
vm->frames[0].rbp = vm->rbp + 1; // +1 to skip script's null return value.
|
|
|
|
|
|
|
|
LOAD_FRAME();
|
|
|
|
|
|
|
|
Opcode instruction;
|
|
|
|
SWITCH(instruction) {
|
|
|
|
|
|
|
|
OPCODE(CONSTANT):
|
|
|
|
PUSH(script->literals.data[READ_SHORT()]);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_NULL):
|
|
|
|
PUSH(VAR_NULL);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_TRUE):
|
|
|
|
PUSH(VAR_TRUE);
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(PUSH_FALSE):
|
|
|
|
PUSH(VAR_FALSE);
|
|
|
|
DISPATCH();
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
OPCODE(PUSH_LIST):
|
|
|
|
{
|
|
|
|
List* list = newList(vm, (uint32_t)READ_SHORT());
|
|
|
|
PUSH(VAR_OBJ(&list->_super));
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(LIST_APPEND):
|
|
|
|
{
|
|
|
|
Var elem = POP();
|
|
|
|
Var list = *(vm->sp - 1);
|
|
|
|
ASSERT(IS_OBJ(list) && AS_OBJ(list)->type == OBJ_LIST, OOPS);
|
|
|
|
varBufferWrite(&((List*)AS_OBJ(list))->elements, vm, elem);
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(PUSH_LOCAL_0):
|
|
|
|
OPCODE(PUSH_LOCAL_1):
|
|
|
|
OPCODE(PUSH_LOCAL_2):
|
|
|
|
OPCODE(PUSH_LOCAL_3):
|
|
|
|
OPCODE(PUSH_LOCAL_4):
|
|
|
|
OPCODE(PUSH_LOCAL_5):
|
|
|
|
OPCODE(PUSH_LOCAL_6):
|
|
|
|
OPCODE(PUSH_LOCAL_7):
|
|
|
|
OPCODE(PUSH_LOCAL_8):
|
|
|
|
{
|
|
|
|
int index = (int)(instruction - OP_PUSH_LOCAL_0);
|
|
|
|
PUSH(rbp[index]);
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
OPCODE(PUSH_LOCAL_N):
|
|
|
|
{
|
|
|
|
int index = READ_SHORT();
|
|
|
|
PUSH(rbp[index]);
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(STORE_LOCAL_0):
|
|
|
|
OPCODE(STORE_LOCAL_1):
|
|
|
|
OPCODE(STORE_LOCAL_2):
|
|
|
|
OPCODE(STORE_LOCAL_3):
|
|
|
|
OPCODE(STORE_LOCAL_4):
|
|
|
|
OPCODE(STORE_LOCAL_5):
|
|
|
|
OPCODE(STORE_LOCAL_6):
|
|
|
|
OPCODE(STORE_LOCAL_7):
|
|
|
|
OPCODE(STORE_LOCAL_8):
|
2021-02-12 14:53:52 +08:00
|
|
|
{
|
|
|
|
int index = (int)(instruction - OP_STORE_LOCAL_0);
|
|
|
|
rbp[index] = POP();
|
|
|
|
DISPATCH();
|
|
|
|
}
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(STORE_LOCAL_N):
|
2021-02-12 14:53:52 +08:00
|
|
|
{
|
|
|
|
int index = READ_SHORT();
|
|
|
|
rbp[index] = POP();
|
|
|
|
DISPATCH();
|
|
|
|
}
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
OPCODE(PUSH_GLOBAL):
|
|
|
|
{
|
|
|
|
int index = READ_SHORT();
|
2021-02-13 01:40:19 +08:00
|
|
|
ASSERT(index < script->globals.count, OOPS);
|
2021-02-12 01:35:43 +08:00
|
|
|
PUSH(script->globals.data[index]);
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(STORE_GLOBAL):
|
|
|
|
{
|
|
|
|
int index = READ_SHORT();
|
2021-02-13 01:40:19 +08:00
|
|
|
ASSERT(index < script->globals.count, OOPS);
|
2021-02-12 01:35:43 +08:00
|
|
|
script->globals.data[index] = POP();
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(PUSH_FN):
|
|
|
|
{
|
|
|
|
int index = READ_SHORT();
|
2021-02-13 01:40:19 +08:00
|
|
|
ASSERT(index < script->functions.count, OOPS);
|
2021-02-12 01:35:43 +08:00
|
|
|
Function* fn = script->functions.data[index];
|
|
|
|
PUSH(VAR_OBJ(&fn->_super));
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(PUSH_BUILTIN_FN):
|
|
|
|
{
|
|
|
|
Function* fn = getBuiltinFunction(READ_SHORT());
|
|
|
|
PUSH(VAR_OBJ(&fn->_super));
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(POP):
|
|
|
|
DROP();
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(CALL):
|
|
|
|
{
|
|
|
|
int argc = READ_SHORT();
|
|
|
|
Var* callable = vm->sp - argc - 1;
|
|
|
|
|
|
|
|
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
|
|
|
|
|
|
|
|
Function* fn = (Function*)AS_OBJ(*callable);
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
// -1 argument means multiple number of args.
|
|
|
|
if (fn->arity != -1 && fn->arity != argc) {
|
2021-02-12 01:35:43 +08:00
|
|
|
RUNTIME_ERROR("Expected excatly %d argument(s).", fn->arity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now the function will never needed in the stack and it'll
|
|
|
|
// initialized with VAR_NULL as return value.
|
|
|
|
*callable = VAR_NULL;
|
|
|
|
|
2021-02-12 14:53:52 +08:00
|
|
|
// Next call frame starts here. (including return value).
|
|
|
|
vm->rbp = callable;
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
if (fn->is_native) {
|
|
|
|
fn->native(vm);
|
2021-02-12 14:53:52 +08:00
|
|
|
// Pop function arguments except for the return value.
|
|
|
|
vm->sp = vm->rbp + 1;
|
2021-02-12 01:35:43 +08:00
|
|
|
CHECK_ERROR();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
STORE_FRAME();
|
|
|
|
pushCallFrame(vm, fn);
|
|
|
|
LOAD_FRAME();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
RUNTIME_ERROR("Expected a function in call.");
|
|
|
|
}
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
OPCODE(ITER) :
|
|
|
|
{
|
|
|
|
Var* iter_value = (vm->sp - 1);
|
|
|
|
Var* iterator = (vm->sp - 2);
|
|
|
|
Var* container = (vm->sp - 3);
|
|
|
|
int jump_offset = READ_SHORT();
|
|
|
|
if (!varIterate(vm, *container, iterator, iter_value)) {
|
|
|
|
DROP(); //< Iter value.
|
|
|
|
DROP(); //< Iterator.
|
|
|
|
DROP(); //< Container.
|
|
|
|
ip += jump_offset;
|
|
|
|
}
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(JUMP): {
|
|
|
|
int offset = READ_SHORT();
|
|
|
|
ip += offset;
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
OPCODE(LOOP): {
|
|
|
|
int offset = READ_SHORT();
|
|
|
|
ip -= offset;
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
OPCODE(JUMP_IF):
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(JUMP_IF_NOT):
|
2021-02-12 14:53:52 +08:00
|
|
|
TODO;
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(RETURN):
|
2021-02-12 14:53:52 +08:00
|
|
|
{
|
|
|
|
Var ret = POP();
|
|
|
|
vm->frame_count--;
|
|
|
|
|
|
|
|
// If no more call frames. We're done.
|
|
|
|
if (vm->frame_count == 0) {
|
|
|
|
vm->sp = vm->stack;
|
|
|
|
PUSH(ret);
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the return value.
|
|
|
|
*(frame->rbp - 1) = ret;
|
|
|
|
|
|
|
|
// Pop the locals and update stack pointer.
|
|
|
|
vm->sp = frame->rbp;
|
|
|
|
|
|
|
|
LOAD_FRAME();
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(GET_ATTRIB):
|
|
|
|
OPCODE(SET_ATTRIB):
|
|
|
|
OPCODE(GET_SUBSCRIPT):
|
|
|
|
OPCODE(SET_SUBSCRIPT):
|
2021-02-13 01:40:19 +08:00
|
|
|
TODO;
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(NEGATIVE):
|
2021-02-13 01:40:19 +08:00
|
|
|
{
|
|
|
|
Var num = POP();
|
|
|
|
if (!IS_NUM(num)) {
|
|
|
|
RUNTIME_ERROR("Cannot negate a non numeric value.");
|
|
|
|
}
|
|
|
|
PUSH(VAR_NUM(-AS_NUM(num)));
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(NOT):
|
|
|
|
OPCODE(BIT_NOT):
|
|
|
|
TODO;
|
|
|
|
|
|
|
|
OPCODE(ADD):
|
|
|
|
PUSH(varAdd(vm, POP(), POP()));
|
2021-02-13 01:40:19 +08:00
|
|
|
CHECK_ERROR();
|
2021-02-12 01:35:43 +08:00
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(SUBTRACT):
|
|
|
|
PUSH(varSubtract(vm, POP(), POP()));
|
2021-02-13 01:40:19 +08:00
|
|
|
CHECK_ERROR();
|
2021-02-12 01:35:43 +08:00
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(MULTIPLY):
|
|
|
|
PUSH(varMultiply(vm, POP(), POP()));
|
2021-02-13 01:40:19 +08:00
|
|
|
CHECK_ERROR();
|
2021-02-12 01:35:43 +08:00
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(DIVIDE):
|
|
|
|
PUSH(varDivide(vm, POP(), POP()));
|
2021-02-13 01:40:19 +08:00
|
|
|
CHECK_ERROR();
|
2021-02-12 01:35:43 +08:00
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
OPCODE(MOD):
|
|
|
|
OPCODE(BIT_AND):
|
|
|
|
OPCODE(BIT_OR):
|
|
|
|
OPCODE(BIT_XOR):
|
|
|
|
OPCODE(BIT_LSHIFT):
|
|
|
|
OPCODE(BIT_RSHIFT):
|
|
|
|
OPCODE(AND):
|
|
|
|
OPCODE(OR):
|
|
|
|
OPCODE(EQEQ):
|
|
|
|
OPCODE(NOTEQ):
|
|
|
|
OPCODE(LT):
|
|
|
|
OPCODE(LTEQ):
|
|
|
|
OPCODE(GT):
|
|
|
|
OPCODE(GTEQ):
|
2021-02-12 14:53:52 +08:00
|
|
|
TODO;
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(RANGE):
|
2021-02-12 14:53:52 +08:00
|
|
|
{
|
|
|
|
Var to = POP();
|
|
|
|
Var from = POP();
|
|
|
|
if (!IS_NUM(from) || !IS_NUM(to)) {
|
|
|
|
RUNTIME_ERROR("Range arguments must be number.");
|
|
|
|
}
|
|
|
|
PUSH(VAR_OBJ(newRange(vm, AS_NUM(from), AS_NUM(to))));
|
|
|
|
DISPATCH();
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
OPCODE(IN):
|
|
|
|
OPCODE(END):
|
2021-02-12 14:53:52 +08:00
|
|
|
TODO;
|
2021-02-12 01:35:43 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return RESULT_SUCCESS;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|