diff --git a/src/ast.rs b/src/ast.rs index 7420502..818ac47 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -140,8 +140,9 @@ pub mod ast { c.cpush32(n as u32); } _ => { - c.cpushop(VmOpcode::OP_PUSH64); - c.cpush64(n); + unimplemented!() + //c.cpushop(VmOpcode::OP_PUSH64); + //c.cpush64(n); } } emit_end!(c, _smap_begin); @@ -191,8 +192,8 @@ pub mod ast { c.cpushop(VmOpcode::OP_PUSH8); c.cpush8(self.exprs.len() as u8); } else { - c.cpushop(VmOpcode::OP_PUSH64); - c.cpush64(self.exprs.len() as u64); + c.cpushop(VmOpcode::OP_PUSH32); + c.cpush32(self.exprs.len() as u32); } c.cpushop(VmOpcode::OP_ARRAY_LOAD); emit_end!(c, _smap_begin); @@ -315,8 +316,8 @@ pub mod ast { c.cpushop(VmOpcode::OP_PUSH8); c.cpush8(self.stmts.len() as u8); } else { - c.cpushop(VmOpcode::OP_PUSH64); - c.cpush64(self.stmts.len() as u64); + c.cpushop(VmOpcode::OP_PUSH32); + c.cpush32(self.stmts.len() as u32); } c.cpushop(VmOpcode::OP_DICT_LOAD); emit_end!(c, _smap_begin); diff --git a/src/hanayo/array.rs b/src/hanayo/array.rs index 197b442..c4f56d6 100644 --- a/src/hanayo/array.rs +++ b/src/hanayo/array.rs @@ -1,7 +1,7 @@ //! Provides Array record for handling arrays use std::cmp::Ordering; -use crate::vmbindings::cnativeval::{NativeValue, NativeValueType}; +use crate::vmbindings::cnativeval::{NativeValue}; use crate::vmbindings::value::Value; use crate::vmbindings::vm::Vm; @@ -23,13 +23,13 @@ pub extern "C" fn constructor(cvm: *mut Vm, nargs: u16) { #[hana_function()] fn length(array: Value::Array) -> Value { - Value::Int(array.as_ref().len() as i64) + Value::Int(array.as_ref().len() as i32) } #[hana_function()] fn insert_(array: Value::Array, pos: Value::Int, elem: Value::Any) -> Value { array.as_mut().insert(pos as usize, elem.wrap()); - Value::Int(array.as_ref().len() as i64) + Value::Int(array.as_ref().len() as i32) } #[hana_function()] @@ -37,7 +37,7 @@ fn delete_(array: Value::Array, from_pos: Value::Int, nelems: Value::Int) -> Val array .as_mut() .drain((from_pos as usize)..((nelems as usize) + 1)); - Value::Int(array.as_ref().len() as i64) + Value::Int(array.as_ref().len() as i32) } // stack manipulation @@ -53,34 +53,21 @@ fn pop(array: Value::Array) -> Value { } extern "C" { - fn value_gt(result: *mut NativeValue, left: NativeValue, right: NativeValue); - fn value_lt(result: *mut NativeValue, left: NativeValue, right: NativeValue); + fn value_gt(left: NativeValue, right: NativeValue) -> NativeValue; + fn value_lt(left: NativeValue, right: NativeValue) -> NativeValue; } // sorting fn value_cmp(left: &NativeValue, right: &NativeValue) -> Ordering { let left = left.clone(); let right = right.clone(); - let mut val = NativeValue { - data: 0, - r#type: NativeValueType::TYPE_NIL, - }; - - unsafe { - value_gt(&mut val, left, right); - } - if val.data == 1 { - return Ordering::Greater; - } - - unsafe { - value_lt(&mut val, left, right); + if unsafe { value_gt(left, right) }.unwrap().int() == 1 { + Ordering::Greater + } else if unsafe { value_lt(left, right) }.unwrap().int() == 1 { + Ordering::Less + } else { + Ordering::Equal } - if val.data == 1 { - return Ordering::Less; - } - - Ordering::Equal } #[hana_function()] @@ -151,21 +138,14 @@ fn reduce(array: Value::Array, fun: Value::Any, acc_: Value::Any) -> Value { // search extern "C" { - fn value_eq(result: *mut NativeValue, left: NativeValue, right: NativeValue); + fn value_eq(left: NativeValue, right: NativeValue) -> NativeValue; } #[hana_function()] fn index(array: Value::Array, elem: Value::Any) -> Value { let array = array.as_ref(); for i in 0..(array.len() - 1) { - let mut val = NativeValue { - data: 0, - r#type: NativeValueType::TYPE_NIL, - }; - unsafe { - value_eq(&mut val, array[i], elem.wrap()); - } - if val.data == 1 { - return Value::Int(i as i64); + if unsafe { value_eq(array[i], elem.wrap()) }.unwrap().int() == 1 { + return Value::Int(i as i32); } } Value::Int(-1) diff --git a/src/hanayo/file.rs b/src/hanayo/file.rs index 9285cc8..c00bcfe 100644 --- a/src/hanayo/file.rs +++ b/src/hanayo/file.rs @@ -88,7 +88,7 @@ fn write(file: Value::Record, buf: Value::Str) -> Value { let file = file.as_mut(); if let Some(field) = file.native_field.as_mut() { let file = field.downcast_mut::().unwrap(); - Value::Int(file.write_all(buf.as_ref().as_bytes()).is_ok() as i64) + Value::Int(file.write_all(buf.as_ref().as_bytes()).is_ok() as i32) } else { Value::Int(0) } @@ -100,8 +100,8 @@ fn seek(file: Value::Record, pos: Value::Int) -> Value { let file = file.as_mut(); if let Some(field) = file.native_field.as_mut() { let file = field.downcast_mut::().unwrap(); - if let Ok(result) = file.seek(SeekFrom::Current(pos)) { - Value::Int(result as i64) + if let Ok(result) = file.seek(SeekFrom::Current(pos as i64)) { + Value::Int(result as i32) } else { Value::Int(-1) } @@ -116,7 +116,7 @@ fn seek_from_start(file: Value::Record, pos: Value::Int) -> Value { if let Some(field) = file.native_field.as_mut() { let file = field.downcast_mut::().unwrap(); if let Ok(result) = file.seek(SeekFrom::Start(pos as u64)) { - Value::Int(result as i64) + Value::Int(result as i32) } else { Value::Int(-1) } @@ -130,8 +130,8 @@ fn seek_from_end(file: Value::Record, pos: Value::Int) -> Value { let file = file.as_mut(); if let Some(field) = file.native_field.as_mut() { let file = field.downcast_mut::().unwrap(); - if let Ok(result) = file.seek(SeekFrom::End(pos)) { - Value::Int(result as i64) + if let Ok(result) = file.seek(SeekFrom::End(pos as i64)) { + Value::Int(result as i32) } else { Value::Int(-1) } diff --git a/src/hanayo/int.rs b/src/hanayo/int.rs index 1277473..d3136d0 100644 --- a/src/hanayo/int.rs +++ b/src/hanayo/int.rs @@ -9,8 +9,8 @@ use std::str::FromStr; fn constructor(val: Value::Any) -> Value { match val { Value::Int(n) => Value::Int(n), - Value::Float(n) => Value::Int(n as i64), - Value::Str(s) => match i64::from_str(s.as_ref()) { + Value::Float(n) => Value::Int(n as i32), + Value::Str(s) => match i32::from_str(s.as_ref()) { Ok(n) => Value::Int(n), Err(_) => { hana_raise!(vm, { diff --git a/src/hanayo/proc.rs b/src/hanayo/proc.rs index f13b99d..48ca2c5 100644 --- a/src/hanayo/proc.rs +++ b/src/hanayo/proc.rs @@ -105,7 +105,7 @@ fn wait(process: Value::Record) -> Value { match p.wait() { Ok(e) => { if let Some(code) = e.code() { - Value::Int(code as i64) + Value::Int(code as i32) } else { Value::Int(0) } diff --git a/src/hanayo/string.rs b/src/hanayo/string.rs index 5a27f80..53f4aa0 100644 --- a/src/hanayo/string.rs +++ b/src/hanayo/string.rs @@ -24,21 +24,21 @@ pub extern "C" fn constructor(cvm: *mut Vm, nargs: u16) { // length #[hana_function()] fn length(s: Value::Str) -> Value { - Value::Int(s.as_ref().graphemes(true).count() as i64) + Value::Int(s.as_ref().graphemes(true).count() as i32) } #[hana_function()] fn bytesize(s: Value::Str) -> Value { - Value::Int(s.as_ref().len() as i64) + Value::Int(s.as_ref().len() as i32) } // check #[hana_function()] fn startswith(s: Value::Str, left: Value::Str) -> Value { - Value::Int(s.as_ref().starts_with(left.as_ref()) as i64) + Value::Int(s.as_ref().starts_with(left.as_ref()) as i32) } #[hana_function()] fn endswith(s: Value::Str, left: Value::Str) -> Value { - Value::Int(s.as_ref().ends_with(left.as_ref()) as i64) + Value::Int(s.as_ref().ends_with(left.as_ref()) as i32) } // basic manip @@ -141,7 +141,7 @@ fn index(s: Value::Str, needle: Value::Str) -> Value { }) .next() { - (idx_grapheme - 1) as i64 + (idx_grapheme - 1) as i32 } else { -1 } @@ -164,7 +164,7 @@ fn chars(s: Value::Str) -> Value { fn ord(s: Value::Str) -> Value { let s = s.as_ref(); if let Some(ch) = s.chars().next() { - Value::Int(ch as i64) + Value::Int(ch as i32) } else { Value::Int(0) } diff --git a/src/hanayo/time.rs b/src/hanayo/time.rs index d3dd351..67cfd3b 100644 --- a/src/hanayo/time.rs +++ b/src/hanayo/time.rs @@ -42,25 +42,25 @@ fn since(left: Value::Record, right: Value::Record) -> Value { fn secs(time: Value::Record) -> Value { let tref = time.as_ref().native_field.as_ref().unwrap(); let time = tref.downcast_ref::().unwrap(); - Value::Int(time.as_secs() as i64) + Value::Int(time.as_secs() as i32) } #[hana_function()] fn millis(time: Value::Record) -> Value { let tref = time.as_ref().native_field.as_ref().unwrap(); let time = tref.downcast_ref::().unwrap(); - Value::Int(time.as_millis() as i64) + Value::Int(time.as_millis() as i32) } #[hana_function()] fn micros(time: Value::Record) -> Value { let tref = time.as_ref().native_field.as_ref().unwrap(); let time = tref.downcast_ref::().unwrap(); - Value::Int(time.as_micros() as i64) + Value::Int(time.as_micros() as i32) } #[hana_function()] fn nanos(time: Value::Record) -> Value { let tref = time.as_ref().native_field.as_ref().unwrap(); let time = tref.downcast_ref::().unwrap(); - Value::Int(time.as_nanos() as i64) + Value::Int(time.as_nanos() as i32) } // other diff --git a/src/vm/array.h b/src/vm/array.h index cea623b..a32cece 100644 --- a/src/vm/array.h +++ b/src/vm/array.h @@ -25,11 +25,6 @@ void *rrealloc(void *ptr, size_t nelems, size_t size, size_t new_size); .length = n, \ } -#define array_free(array) \ - do { \ - free(array.data); \ - } while (0) - #define array_push(array, element) \ do { \ if (array.length == array.capacity) { \ diff --git a/src/vm/value.c b/src/vm/value.c index 0b4739d..375cf4e 100644 --- a/src/vm/value.c +++ b/src/vm/value.c @@ -1,5 +1,6 @@ #include #include +#include #include "string_.h" #include "value.h" #include "vm.h" @@ -7,160 +8,165 @@ #include "array_obj.h" #include "function.h" -void value_int(struct value *val, int64_t data) { - val->type = TYPE_INT; - val->as.integer = data; -} -void value_float(struct value *val, double data) { - val->type = TYPE_FLOAT; - val->as.floatp = data; +int value_get_type(struct value val) { + if(val.as.bits.reserved_nan == RESERVED_NAN && val.as.bits.tag_bits > 0) { + return val.as.bits.tag_bits; + } else { + return TYPE_FLOAT; + } } + // non-primitives -void value_str(struct value *val, const char *data, const struct vm *vm) { - val->type = TYPE_STR; - val->as.str = string_malloc(data, vm); +struct value value_int(int32_t n) { + struct value val = {0}; + val.as.bits.reserved_nan = RESERVED_NAN; + val.as.bits.tag_bits = TYPE_INT; + val.as.lower32 = n; + return val; } -void value_function(struct value *val, uint32_t ip, uint16_t nargs, struct env *env, const struct vm *vm) { - val->type = TYPE_FN; - val->as.ifn = function_malloc(ip, nargs, env, vm); +struct value value_float(double n) { + struct value val; + val.as.floatp = n; + // TODO: fix nan + //assert(!isnan(n)); + return val; } -void value_dict(struct value *val, const struct vm *vm) { - val->type = TYPE_DICT; - val->as.dict = dict_malloc(vm); +struct value value_str(const char *data, const struct vm *vm) { + return value_pointer(TYPE_STR, string_malloc(data, vm)); } -void value_dict_n(struct value *val, size_t n, const struct vm *vm) { - val->type = TYPE_DICT; - val->as.dict = dict_malloc_n(vm, n); +struct value value_function(uint32_t ip, uint16_t nargs, struct env *env, const struct vm *vm) { + return value_pointer(TYPE_FN, function_malloc(ip, nargs, env, vm)); } -void value_array(struct value *val, const struct vm *vm) { - val->type = TYPE_ARRAY; - val->as.array = array_obj_malloc(vm); +struct value value_dict(const struct vm *vm) { + return value_pointer(TYPE_DICT, dict_malloc(vm)); } -void value_array_n(struct value *val, size_t n, const struct vm *vm) { - val->type = TYPE_ARRAY; - val->as.array = array_obj_malloc_n(n, vm); +struct value value_dict_n(size_t n, const struct vm *vm) { + return value_pointer(TYPE_DICT, dict_malloc_n(vm, n)); +} +struct value value_array(const struct vm *vm) { + return value_pointer(TYPE_ARRAY, array_obj_malloc(vm)); +} +struct value value_array_n(size_t n, const struct vm *vm) { + return value_pointer(TYPE_ARRAY, array_obj_malloc_n(n, vm)); } -// arith -#define arith_op(name, op, custom) \ -void value_ ## name (struct value *result, const struct value left, const struct value right, const struct vm *vm) { \ - switch(left.type) { \ - case TYPE_INT: { \ - switch(right.type) { \ - case TYPE_FLOAT: { \ - value_float(result, (double)left.as.integer op right.as.floatp); break; } \ - case TYPE_INT: { \ - value_int(result, left.as.integer op right.as.integer); \ - break; }\ - default: \ - result->type = TYPE_INTERPRETER_ERROR; } \ - break; \ - } \ - case TYPE_FLOAT: { \ - switch(right.type) { \ - case TYPE_INT: { \ - value_float(result, left.as.floatp op (double)right.as.integer); break; } \ - case TYPE_FLOAT: { \ - value_float(result, left.as.floatp op right.as.floatp); break; } \ - default: \ - result->type = TYPE_INTERPRETER_ERROR; } \ - break; \ - } custom \ - default: result->type = TYPE_INTERPRETER_ERROR; }\ +struct value value_interpreter_error() { + return value_pointer(TYPE_INTERPRETER_ERROR, 0); } + +// arith +#define arith_op(name, op, custom) \ + struct value value_##name(const struct value left, const struct value right, const struct vm *vm) { \ + switch (value_get_type(left)) { \ + case TYPE_INT: { \ + switch (value_get_type(right)) { \ + case TYPE_FLOAT: \ + return value_float((double)value_get_int(left) op right.as.floatp); \ + case TYPE_INT: \ + return value_int(value_get_int(left) op value_get_int(right)); \ + default: \ + return value_interpreter_error(); \ + } \ + } \ + case TYPE_FLOAT: { \ + switch (value_get_type(right)) { \ + case TYPE_INT: \ + return value_int(left.as.floatp op(double) value_get_int(right)); \ + case TYPE_FLOAT: \ + return value_float(left.as.floatp op right.as.floatp); \ + default: \ + return value_interpreter_error(); \ + } \ + } \ + custom \ + } \ + return value_interpreter_error(); \ + } arith_op(add, +, case TYPE_STR: { - if(right.type != TYPE_STR) { - result->type = TYPE_INTERPRETER_ERROR; - return; + if(value_get_type(right) != TYPE_STR) { + return value_interpreter_error(); + } + return value_pointer(TYPE_STR, string_append(value_get_pointer(TYPE_STR, left), value_get_pointer(TYPE_STR, right), vm)); } - result->type = TYPE_STR; - result->as.str = string_append(left.as.str, right.as.str, vm); - break; } ) arith_op(sub, -,) arith_op(mul, *, case TYPE_STR: { - if(right.type == TYPE_INT) { - if(right.as.integer == 0) { - value_str(result, "", vm); - } else { - result->type = TYPE_STR; - result->as.str = string_repeat(left.as.str, right.as.integer, vm); + if(value_get_type(right) == TYPE_INT) { + if(value_get_int(right) == 0) { + return value_interpreter_error(); } + return value_pointer(TYPE_STR, string_repeat(value_get_pointer(TYPE_STR, left), value_get_int(right), vm)); } - else - result->type = TYPE_INTERPRETER_ERROR; - break; } + return value_interpreter_error(); + } case TYPE_ARRAY: { - if(right.type == TYPE_INT) { - result->type = TYPE_ARRAY; - result->as.array = array_obj_repeat(left.as.array, (size_t)right.as.integer, vm); + if(value_get_type(right) == TYPE_INT) { + return value_pointer(TYPE_ARRAY, array_obj_repeat(value_get_pointer(TYPE_ARRAY, left), (size_t)value_get_int(right), vm)); } - else - result->type = TYPE_INTERPRETER_ERROR; - break; } + return value_interpreter_error(); + } ) -void value_div(struct value *result, const struct value left, const struct value right, const struct vm *_) { - switch(left.type) { +struct value value_div(const struct value left, const struct value right, const struct vm *_) { + switch(value_get_type(left)) { case TYPE_INT: { - switch(right.type) { - case TYPE_FLOAT: { - value_float(result, (double)left.as.integer / right.as.floatp); break; } - case TYPE_INT: { - value_float(result, (double)left.as.integer / (double)right.as.integer); - break; } - default: - result->type = TYPE_INTERPRETER_ERROR; } - break; + switch(value_get_type(right)) { + case TYPE_FLOAT: + return value_float((double)value_get_int(left) / right.as.floatp); + case TYPE_INT: + return value_float((double)value_get_int(left) / (double)value_get_int(right)); + } + return value_interpreter_error(); } case TYPE_FLOAT: { - switch(right.type) { - case TYPE_INT: { - value_float(result, left.as.floatp / (double)right.as.integer); break; } - case TYPE_FLOAT: { - value_float(result, left.as.floatp / right.as.floatp); break; } - default: - result->type = TYPE_INTERPRETER_ERROR; } - break; + switch(value_get_type(right)) { + case TYPE_INT: + return value_float(left.as.floatp / (double)value_get_int(right)); + case TYPE_FLOAT: + return value_float(left.as.floatp / right.as.floatp); + } + return value_interpreter_error(); } - default: result->type = TYPE_INTERPRETER_ERROR; } + default: return value_interpreter_error(); } } -void value_mod(struct value *result, const struct value left, const struct value right, const struct vm *_) { - if(left.type == TYPE_INT && right.type == TYPE_INT) { - value_int(result, left.as.integer % right.as.integer); +struct value value_mod(const struct value left, const struct value right, const struct vm *_) { + if(value_get_type(left) == TYPE_INT && value_get_type(right) == TYPE_INT) { + return value_int(value_get_int(left) % value_get_int(right)); } else - result->type = TYPE_INTERPRETER_ERROR; + return value_interpreter_error(); } -void value_bitwise_and(struct value *result, const struct value left, const struct value right, const struct vm *_) { - if (left.type == TYPE_INT && right.type == TYPE_INT) { - value_int(result, left.as.integer & right.as.integer); +struct value value_bitwise_and(const struct value left, const struct value right, const struct vm *_) { + if (value_get_type(left) == TYPE_INT && value_get_type(right) == TYPE_INT) { + return value_int(value_get_int(left) & value_get_int(right)); } else - result->type = TYPE_INTERPRETER_ERROR; + return value_interpreter_error(); } -void value_bitwise_or(struct value *result, const struct value left, const struct value right, const struct vm *_) { - if (left.type == TYPE_INT && right.type == TYPE_INT) { - value_int(result, left.as.integer | right.as.integer); +struct value value_bitwise_or(const struct value left, const struct value right, const struct vm *_) { + if (value_get_type(left) == TYPE_INT && value_get_type(right) == TYPE_INT) { + return value_int(value_get_int(left) | value_get_int(right)); } else - result->type = TYPE_INTERPRETER_ERROR; + return value_interpreter_error(); } -void value_bitwise_xor(struct value *result, const struct value left, const struct value right, const struct vm *_) { - if (left.type == TYPE_INT && right.type == TYPE_INT) { - value_int(result, left.as.integer ^ right.as.integer); +struct value value_bitwise_xor(const struct value left, const struct value right, const struct vm *_) { + if (value_get_type(left) == TYPE_INT && value_get_type(right) == TYPE_INT) { + return value_int(value_get_int(left) ^ value_get_int(right)); } else - result->type = TYPE_INTERPRETER_ERROR; + return value_interpreter_error(); } // in place // returns 1 if it CAN do it in place int value_iadd(struct value left, const struct value right) { - switch (left.type) { + switch (value_get_type(left)) { case TYPE_STR: { - switch(right.type) { - case TYPE_STR: { - string_append_in_place(left.as.str, right.as.str); - return 1; } } + switch (value_get_type(right)) { + case TYPE_STR: { + string_append_in_place(value_get_pointer(TYPE_STR, left), value_get_pointer(TYPE_STR, right)); + return 1; + } + } return 0; } } @@ -168,12 +174,14 @@ int value_iadd(struct value left, const struct value right) { } int value_imul(struct value left, const struct value right) { - switch (left.type) { + switch (value_get_type(left)) { case TYPE_STR: { - switch(right.type) { - case TYPE_INT: { - string_repeat_in_place(left.as.str, right.as.integer); - return 1; } } + switch (value_get_type(right)) { + case TYPE_INT: { + string_repeat_in_place(value_get_pointer(TYPE_STR, left), value_get_int(right)); + return 1; + } + } return 0; } } @@ -183,29 +191,25 @@ int value_imul(struct value left, const struct value right) { // comparison #define strcmp_op(cond) \ case TYPE_STR: \ - value_int(result, right.type == TYPE_STR && string_cmp(left.as.str, right.as.str) cond); \ + return value_int(value_get_type(right) == TYPE_STR && string_cmp(value_get_pointer(TYPE_STR, left), value_get_pointer(TYPE_STR, right)) cond); \ break; arith_op(eq, ==, strcmp_op(== 0) case TYPE_NATIVE_FN: case TYPE_FN: case TYPE_DICT: - value_int(result, left.as.integer == right.as.integer); - break; + return value_int(value_get_int(left) == value_get_int(right)); case TYPE_NIL: - value_int(result, right.type == TYPE_NIL); - break; + return value_int(value_get_type(right) == TYPE_NIL); ) arith_op(neq, !=, strcmp_op(!= 0) case TYPE_NATIVE_FN: case TYPE_FN: case TYPE_DICT: - value_int(result, left.as.integer != right.as.integer); - break; + return value_int(value_get_int(left) != value_get_int(right)); case TYPE_NIL: - value_int(result, right.type != TYPE_NIL); - break; + return value_int(value_get_type(right) != TYPE_NIL); ) arith_op(lt, <, strcmp_op(< 0) @@ -222,27 +226,29 @@ arith_op(geq, >=, // boolean bool value_is_true(const struct value val) { - switch(val.type) { - case TYPE_INT: return val.as.integer > 0; + switch(value_get_type(val)) { + case TYPE_INT: return value_get_int(val) > 0; case TYPE_FLOAT: return val.as.floatp > 0; - case TYPE_STR: return !string_is_empty(val.as.str); + case TYPE_STR: return !string_is_empty(value_get_pointer(TYPE_STR, val)); default: return 0; } } struct dict *value_get_prototype(const struct vm *vm, const struct value val) { - if(val.type == TYPE_STR) { - return vm->dstr; - } else if(val.type == TYPE_INT) { - return vm->dint; - } else if(val.type == TYPE_FLOAT) { - return vm->dfloat; - } else if(val.type == TYPE_ARRAY) { - return vm->darray; - } else if(val.type == TYPE_DICT) { - const struct value *p = dict_get(val.as.dict, "prototype"); - if (p == NULL) return NULL; - return p->as.dict; + switch (value_get_type(val)) { + case TYPE_STR: + return vm->dstr; + case TYPE_INT: + return vm->dint; + case TYPE_FLOAT: + return vm->dfloat; + case TYPE_ARRAY: + return vm->darray; + case TYPE_DICT: { + const struct value *p = dict_get(value_get_pointer(TYPE_DICT, val), "prototype"); + if (p == NULL) return NULL; + return value_get_pointer(TYPE_DICT, *p); + } } return NULL; } diff --git a/src/vm/value.h b/src/vm/value.h index 51972cd..574dcee 100644 --- a/src/vm/value.h +++ b/src/vm/value.h @@ -1,7 +1,4 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif #include #include #include "dict.h" @@ -12,66 +9,146 @@ struct hmap; struct array_obj; struct string; -#define TYPE_NIL 0 -#define TYPE_INT 1 -#define TYPE_FLOAT 2 -#define TYPE_NATIVE_FN 3 -#define TYPE_FN 4 -#define TYPE_STR 5 -#define TYPE_DICT 6 -#define TYPE_ARRAY 7 -#define TYPE_INTERPRETER_ERROR 127 -#define TYPE_INTERPRETER_ITERATOR 128 +#define RESERVED_NAN 0x7ff +#define TYPE_FLOAT 0 +#define TYPE_INT 1 +#define TYPE_NATIVE_FN 2 +#define TYPE_FN 3 +#define TYPE_STR 4 +#define TYPE_DICT 5 +#define TYPE_ARRAY 6 +#define TYPE_INTERPRETER_ERROR 7 +#define TYPE_INTERPRETER_ITERATOR 8 +#define TYPE_NIL 9 + +/* + 1111111111110000 + 01111111 11111010 00000000 00000000 00000000 00000000 00000000 00000000 + seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm + ^ ^^^^ tagging bits (T) + ^ ^ initial mantissa bit (whether it's a signalling/quiet NaN) + ^ signed bit (ignored) + + we'll assume the architecture is using IEEE 754 double precision floating + point, and the OS allocates userland memory in the lower half of memory. + NaN values inside doubles allow us to store a 52-bit payload inside the mantissa + we can store 48-bit pointers/32-bit integers in the lower 48-bit region + and 4 additional higher bits for tagging + + possible values for T: + - int + - native_fn + - fn + - str + - dict + - array + - [interpreter data] + +*/ -typedef void (*value_fn)(struct vm *vm, uint16_t nargs); struct __attribute__((packed)) value { union { - int64_t integer; double floatp; - struct string *str; - value_fn fn; - struct function *ifn; - struct dict *dict; - array_obj *array; + uint64_t bin; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + struct __attribute__((packed)) { + uint64_t payload : 48; + uint8_t tag_bits : 4; // must be larger than 0 + uint16_t reserved_nan : 12; // must be positive NaN (0x7ff0) + } bits; + struct __attribute__((packed)) { + uint32_t lower32; + uint32_t upper32; + }; +#else +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + struct __attribute__((packed)) { + uint16_t reserved_nan : 12; // must be positive NaN (0x7ff0) + uint8_t tag_bits : 4; // must be larger than 0 + uint64_t payload : 48; + } bits; + struct __attribute__((packed)) { + uint32_t upper32; + uint32_t lower32; + }; +#else +#error "does not support this system" +#endif +#endif } as; - uint8_t type; }; -void value_int(struct value*, int64_t); -void value_float(struct value*, double); -void value_str(struct value*, const char*, const struct vm*); +typedef void (*value_fn)(struct vm *vm, uint16_t nargs); + struct env; -void value_function(struct value *, uint32_t ip, uint16_t nargs, struct env *env, const struct vm *); -void value_dict(struct value *, const struct vm *); -void value_dict_n(struct value *, size_t n, const struct vm *); -void value_array(struct value *, const struct vm *); -void value_array_n(struct value *, size_t n, const struct vm *); + +struct value value_int(int32_t); +struct value value_float(double); +struct value value_str(const char*, const struct vm*); +struct value value_function(uint32_t ip, uint16_t nargs, struct env *env, const struct vm *); +struct value value_dict(const struct vm *); +struct value value_dict_n(size_t n, const struct vm *); +struct value value_array(const struct vm *); +struct value value_array_n(size_t n, const struct vm *); +struct value value_interpreter_error(); +int value_get_type(struct value val); void value_print(struct value); -void value_add(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_sub(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_mul(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_div(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_mod(struct value *result, const struct value left, const struct value right, const struct vm *); +struct value value_add(const struct value left, const struct value right, const struct vm *); +struct value value_sub(const struct value left, const struct value right, const struct vm *); +struct value value_mul(const struct value left, const struct value right, const struct vm *); +struct value value_div(const struct value left, const struct value right, const struct vm *); +struct value value_mod(const struct value left, const struct value right, const struct vm *); -void value_bitwise_and(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_bitwise_or(struct value *result, const struct value left, const struct value right, const struct vm *); -void value_bitwise_xor(struct value *result, const struct value left, const struct value right, const struct vm *); +struct value value_bitwise_and(const struct value left, const struct value right, const struct vm *); +struct value value_bitwise_or (const struct value left, const struct value right, const struct vm *); +struct value value_bitwise_xor(const struct value left, const struct value right, const struct vm *); int value_iadd(struct value left, const struct value right); int value_imul(struct value left, const struct value right); -void value_lt(struct value *result, const struct value left, const struct value right, const struct vm*); -void value_leq(struct value *result, const struct value left, const struct value right, const struct vm*); -void value_gt(struct value *result, const struct value left, const struct value right, const struct vm*); -void value_geq(struct value *result, const struct value left, const struct value right, const struct vm*); -void value_eq(struct value *result, const struct value left, const struct value right, const struct vm*); -void value_neq(struct value *result, const struct value left, const struct value right, const struct vm*); +struct value value_lt (const struct value left, const struct value right, const struct vm*); +struct value value_leq(const struct value left, const struct value right, const struct vm*); +struct value value_gt (const struct value left, const struct value right, const struct vm*); +struct value value_geq(const struct value left, const struct value right, const struct vm*); +struct value value_eq (const struct value left, const struct value right, const struct vm*); +struct value value_neq(const struct value left, const struct value right, const struct vm*); bool value_is_true(const struct value); struct dict *value_get_prototype(const struct vm *vm, const struct value val); -#ifdef __cplusplus -} +// TODO move this somewhere else +#ifdef DEBUG +#define debug_assert(x) if(!(x)){ *((void*)0); } +#else +#define debug_assert(x) #endif +static inline struct value value_pointer(uint8_t tag, void *ptr) { + //uint64_t low_bits = (uint64_t)ptr & 0xffffffffffff; + //debug_assert(low_bits == (uint64_t)ptr); + debug_assert(tag < 16 && tag > 0); // we can only store 4 bits + return (struct value){ + .as.bits.reserved_nan = RESERVED_NAN, + .as.bits.tag_bits = tag, + .as.bits.payload = (uint64_t)(uintptr_t)(ptr)}; +} +static inline uint16_t value_get_tag(struct value val) { + debug_assert(val.as.bits.reserved_nan == RESERVED_NAN && val.as.bits.tag_bits > 0); + return val.as.bits.tag_bits; +} +// TODO: remove redundant checks from opcodes +static inline void *value_get_pointer(uint8_t tag, struct value val) { + debug_assert(tag != TYPE_INT); + debug_assert(val.as.bits.reserved_nan == RESERVED_NAN && val.as.bits.tag_bits == tag); + return (void *)((uintptr_t)val.as.bits.payload); +} +static inline int32_t value_get_int(struct value val) { + debug_assert(val.as.bits.reserved_nan == RESERVED_NAN && val.as.bits.tag_bits == TYPE_INT); + return (int32_t)val.as.lower32; +} +static inline double value_get_float(struct value val) { + debug_assert(!isnan(val.as.floatp)); + return val.as.floatp; +} +#undef debug_assert \ No newline at end of file diff --git a/src/vm/vm.c b/src/vm/vm.c index aac5ae1..bdd8be4 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -23,8 +23,19 @@ #define FATAL(...) fprintf(stderr, __VA_ARGS__) void vm_execute(struct vm *vm) { -#define ERROR(code, unwind) do { vm->error = code; vm->ip -= unwind; return; } while(0) -#define ERROR_EXPECT(code, unwind, expect) do { vm->error = code; vm->ip -= unwind; vm->error_expected = expect; return; } while(0) +#define ERROR(code, unwind) \ + do { \ + vm->error = code; \ + vm->ip -= (uint32_t)(unwind); \ + return; \ + } while (0) +#define ERROR_EXPECT(code, unwind, expect) \ + do { \ + vm->error = code; \ + vm->ip -= (uint32_t)(unwind); \ + vm->error_expected = (uint32_t)expect; \ + return; \ + } while (0) #define doop(op) do_ ## op #define X(op) [op] = && doop(op) #ifdef NOLOG @@ -95,37 +106,28 @@ void vm_execute(struct vm *vm) { // stack manip // push uint family on to the stack -#define push_int_op(optype, _type, _data) \ - doop(optype): { \ - vm->ip++; \ - const _type data = _data; \ - vm->ip += sizeof(_type); \ +#define push_int_op(optype, _type, _data) \ + doop(optype) : { \ + vm->ip++; \ + const _type data = _data; \ + vm->ip += (uint32_t)sizeof(_type); \ LOG(sizeof(_type) == 8 ? "PUSH %ld\n" : "PUSH %d\n", data); \ -\ - struct value val = { \ - .type = TYPE_INT,\ - .as.integer = data }; \ - array_push(vm->stack, val); \ - dispatch(); \ + array_push(vm->stack, value_int((int32_t)data)); \ + dispatch(); \ } push_int_op(OP_PUSH8, uint8_t, vm->code.data[vm->ip+0]) - push_int_op(OP_PUSH16, uint16_t, (uint16_t)vm->code.data[vm->ip+0] << 8 | - (uint16_t)vm->code.data[vm->ip+1]) + push_int_op(OP_PUSH16, uint16_t, (uint16_t)(vm->code.data[vm->ip+0] << 8 | + vm->code.data[vm->ip+1])) - push_int_op(OP_PUSH32, uint32_t, (uint32_t)vm->code.data[vm->ip+0] << 24 | - (uint32_t)vm->code.data[vm->ip+1] << 16 | - (uint32_t)vm->code.data[vm->ip+2] << 8 | - (uint32_t)vm->code.data[vm->ip+3]) + push_int_op(OP_PUSH32, uint32_t, (uint32_t)(vm->code.data[vm->ip+0] << 24 | + vm->code.data[vm->ip+1] << 16 | + vm->code.data[vm->ip+2] << 8 | + vm->code.data[vm->ip+3])) - push_int_op(OP_PUSH64, uint64_t, (uint64_t)vm->code.data[vm->ip+0] << 56 | - (uint64_t)vm->code.data[vm->ip+1] << 48 | - (uint64_t)vm->code.data[vm->ip+2] << 40 | - (uint64_t)vm->code.data[vm->ip+3] << 32 | - (uint64_t)vm->code.data[vm->ip+4] << 24 | - (uint64_t)vm->code.data[vm->ip+5] << 16 | - (uint64_t)vm->code.data[vm->ip+6] << 8 | - (uint64_t)vm->code.data[vm->ip+7]) + doop(OP_PUSH64): { + assert(0); + } // push 32/64-bit float on to the stack doop(OP_PUSHF64): { @@ -142,10 +144,9 @@ void vm_execute(struct vm *vm) { u.u[5] = vm->code.data[vm->ip + 5]; u.u[6] = vm->code.data[vm->ip + 6]; u.u[7] = vm->code.data[vm->ip + 7]; - vm->ip += sizeof(u); + vm->ip += (uint32_t)sizeof(u); LOG("PUSH_F64 %f\n", u.d); - array_push(vm->stack, (struct value){0}); - value_float(&array_top(vm->stack), u.d); + array_push(vm->stack, value_float(u.d)); dispatch(); } @@ -153,10 +154,9 @@ void vm_execute(struct vm *vm) { doop(OP_PUSHSTR): { vm->ip++; char *str = (char *)&vm->code.data[vm->ip]; // must be null terminated - vm->ip += strlen(str)+1; + vm->ip += (uint32_t)strlen(str) + 1; LOG("PUSH %s\n", str); - array_push(vm->stack, (struct value){0}); - value_str(&array_top(vm->stack), str, vm); + array_push(vm->stack, value_str(str, vm)); dispatch(); } @@ -164,7 +164,7 @@ void vm_execute(struct vm *vm) { doop(OP_PUSH_NIL): { vm->ip++; LOG("PUSH NIL\n"); - array_push(vm->stack, (struct value){0}); + array_push(vm->stack, value_pointer(TYPE_NIL, 0)); dispatch(); } @@ -181,19 +181,24 @@ void vm_execute(struct vm *vm) { vm->ip++; struct value val = array_top(vm->stack); array_pop(vm->stack); - int truth = value_is_true(val); - value_int(&val, !truth); - array_push(vm->stack, val); + array_push(vm->stack, value_int(!value_is_true(val))); dispatch(); } // pops top of the stack, performs unary negation and pushes the result doop(OP_NEGATE): { vm->ip++; - struct value *val = &array_top(vm->stack); - if(val->type == TYPE_INT) { - val->as.integer = -val->as.integer; - } else if(val->type == TYPE_FLOAT) { - val->as.floatp = -val->as.floatp; + struct value val = array_top(vm->stack); + array_pop(vm->stack); + switch (value_get_type(val)) { + case TYPE_INT: { + array_push(vm->stack, value_int(-value_get_int(val))); + break; + } + case TYPE_FLOAT: { + array_push(vm->stack, value_float(-value_get_float(val))); + break; + } + default: assert(0); } dispatch(); } @@ -209,9 +214,8 @@ void vm_execute(struct vm *vm) { struct value right = vm->stack.data[vm->stack.length - 1]; \ struct value left = vm->stack.data[vm->stack.length - 2]; \ \ - struct value result = {0}; \ - fn(&result, left, right, vm); \ - if (result.type == TYPE_INTERPRETER_ERROR) { \ + struct value result = fn(left, right, vm); \ + if (value_get_type(result) == TYPE_INTERPRETER_ERROR) { \ ERROR(ERROR_##optype, 1); \ } \ vm->stack.length -= 2; \ @@ -231,30 +235,29 @@ void vm_execute(struct vm *vm) { // in place arithmetic: // does regular arith, returns lhs on stack and jumps out of fallback if CAN do it in place (for primitives) // else just does the fallback (copying and setting variable manually) -#define binop_inplace(optype, errortype, fn, fallback) \ - doop(optype) : { \ - vm->ip++; \ - LOG(#optype "\n"); \ - debug_assert(vm->stack.length >= 2); \ - const uint8_t pos = (uint8_t)vm->code.data[vm->ip]; \ - \ - struct value right = vm->stack.data[vm->stack.length - 1]; \ - struct value left = vm->stack.data[vm->stack.length - 2]; \ - if (fn(left, right)) { \ - LOG("did in place!\n"); \ - vm->stack.length--; \ - vm->ip += pos; \ - dispatch(); \ - } \ - struct value result = {0}; \ - fallback(&result, left, right, vm); \ - if (result.type == TYPE_INTERPRETER_ERROR) { \ - ERROR(errortype, 1); \ - } \ - vm->stack.length -= 2; \ - array_push(vm->stack, result); \ - vm->ip++; \ - dispatch(); \ +#define binop_inplace(optype, errortype, fn, fallback) \ + doop(optype) : { \ + vm->ip++; \ + LOG(#optype "\n"); \ + debug_assert(vm->stack.length >= 2); \ + const uint8_t pos = (uint8_t)vm->code.data[vm->ip]; \ + \ + struct value right = vm->stack.data[vm->stack.length - 1]; \ + struct value left = vm->stack.data[vm->stack.length - 2]; \ + if (fn(left, right)) { \ + LOG("did in place!\n"); \ + vm->stack.length--; \ + vm->ip += pos; \ + dispatch(); \ + } \ + struct value result = fallback(left, right, vm); \ + if (value_get_type(result) == TYPE_INTERPRETER_ERROR) { \ + ERROR(errortype, 1); \ + } \ + vm->stack.length -= 2; \ + array_push(vm->stack, result); \ + vm->ip++; \ + dispatch(); \ } binop_inplace(OP_IADD, ERROR_OP_ADD, value_iadd, value_add) @@ -279,17 +282,14 @@ void vm_execute(struct vm *vm) { struct value left = array_top(vm->stack); array_pop(vm->stack); - if (right.type == TYPE_DICT) { - const struct dict *rhs = right.as.dict; - struct value retval = { - .as.integer = 0, - .type = TYPE_INT - }; - if ((rhs == vm->drec && left.type == TYPE_DICT) || + if (value_get_type(right) == TYPE_DICT) { + const struct dict *rhs = value_get_pointer(TYPE_DICT, right); + if ((rhs == vm->drec && value_get_type(left) == TYPE_DICT) || (value_get_prototype(vm, left) == rhs)) { - retval.as.integer = 1; + array_push(vm->stack, value_int(1)); + } else { + array_push(vm->stack, value_int(0)); } - array_push(vm->stack, retval); } else { ERROR(ERROR_EXPECTED_RECORD_OF_EXPR, 1); } @@ -302,9 +302,9 @@ void vm_execute(struct vm *vm) { // the environment is initialized with a copy of the current environment's variables doop(OP_ENV_NEW): { vm->ip++; - const uint16_t n = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(n); + const uint16_t n = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(n); LOG("RESERVE %d\n", n); env_init(vm->localenv, n, vm); dispatch(); @@ -314,47 +314,47 @@ void vm_execute(struct vm *vm) { // sets the value of current environment's slot to the top of the stack doop(OP_SET_LOCAL): { vm->ip++; - const uint16_t key = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(key); - LOG("SET LOCAL %d\n", key); - env_set(vm->localenv, key, array_top(vm->stack)); + const uint16_t n = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(n); + LOG("SET LOCAL %d\n", n); + env_set(vm->localenv, n, array_top(vm->stack)); dispatch(); } // this is for recursive function doop(OP_SET_LOCAL_FUNCTION_DEF): { vm->ip++; - const uint16_t key = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(key); + const uint16_t n = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(n); struct value val = array_top(vm->stack); - env_set(vm->localenv, key, val); - function_set_bound_var(val.as.ifn, key, val); + env_set(vm->localenv, n, val); + function_set_bound_var(value_get_pointer(TYPE_FN, val), n, val); dispatch(); } // pushes a copy of the value of current environment's slot doop(OP_GET_LOCAL): { vm->ip++; - const uint16_t key = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(key); - LOG("GET LOCAL %d\n", key); + const uint16_t n = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(n); + LOG("GET LOCAL %d\n", n); array_push(vm->stack, (struct value){0}); - array_top(vm->stack) = env_get(vm->localenv, key); + array_top(vm->stack) = env_get(vm->localenv, n); dispatch(); } doop(OP_GET_LOCAL_UP): { vm->ip++; - const uint16_t key = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(key); - uint16_t relascope = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(relascope); - LOG("GET LOCAL UP %d %d\n", key, relascope); + const uint16_t n = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(n); + const uint16_t relascope = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(relascope); + LOG("GET LOCAL UP %d %d\n", n, relascope); array_push(vm->stack, (struct value){0}); - array_top(vm->stack) = env_get_up(vm->localenv, relascope, key); + array_top(vm->stack) = env_get_up(vm->localenv, relascope, n); dispatch(); } @@ -362,7 +362,7 @@ void vm_execute(struct vm *vm) { doop(OP_SET_GLOBAL): { vm->ip++; char *key = (char *)&vm->code.data[vm->ip]; // must be null terminated - vm->ip += strlen(key)+1; + vm->ip += (uint32_t)strlen(key) + 1; LOG("SET GLOBAL %s %p\n", key, vm->globalenv); hmap_set(vm->globalenv, key, array_top(vm->stack)); dispatch(); @@ -371,7 +371,7 @@ void vm_execute(struct vm *vm) { doop(OP_GET_GLOBAL): { vm->ip++; char *key = (char *)&vm->code.data[vm->ip]; // must be null terminated - vm->ip += strlen(key)+1; + vm->ip += (uint32_t)strlen(key) + 1; LOG("GET GLOBAL %s\n", key); const struct value *val = hmap_get(vm->globalenv, key); if(val == NULL) { @@ -387,14 +387,14 @@ void vm_execute(struct vm *vm) { doop(OP_DEF_FUNCTION_PUSH): { // [opcode][end address] vm->ip++; - const uint16_t nargs = (uint16_t)vm->code.data[vm->ip + 0] << 8 | - (uint16_t)vm->code.data[vm->ip + 1]; - vm->ip += sizeof(nargs); - const uint16_t pos = (uint16_t)vm->code.data[vm->ip + 0] << 8 | - (uint16_t)vm->code.data[vm->ip + 1]; + const uint16_t nargs = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); + vm->ip += (uint32_t)sizeof(nargs); + const uint16_t pos = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); LOG("DEF_FUNCTION_PUSH %d %d\n", pos, nargs); - array_push(vm->stack, (struct value){0}); - value_function(&array_top(vm->stack), vm->ip + sizeof(pos), nargs, vm->localenv, vm); + array_push(vm->stack, value_function(vm->ip + (uint32_t)sizeof(pos), nargs, vm->localenv, vm)); + vm->ip += pos; dispatch(); } @@ -402,18 +402,18 @@ void vm_execute(struct vm *vm) { // flow control doop(OP_JMP): { // jmp [32-bit position] vm->ip++; - const int16_t pos = (uint16_t)vm->code.data[vm->ip + 0] << 8 | - (uint16_t)vm->code.data[vm->ip + 1]; + const int16_t pos = (int16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); vm->ip += pos; LOG("JMP %d\n", pos); dispatch(); } doop(OP_JMP_LONG): { // jmp [32-bit position] vm->ip++; - const uint32_t pos = (uint32_t)vm->code.data[vm->ip + 0] << 24 | - (uint32_t)vm->code.data[vm->ip + 1] << 16 | - (uint32_t)vm->code.data[vm->ip + 2] << 8 | - (uint32_t)vm->code.data[vm->ip + 3]; + const uint32_t pos = (uint32_t)(vm->code.data[vm->ip + 0] << 24 | + vm->code.data[vm->ip + 1] << 16 | + vm->code.data[vm->ip + 2] << 8 | + vm->code.data[vm->ip + 3]); vm->ip = pos; LOG("JMP LONG %d\n", pos); dispatch(); @@ -421,25 +421,25 @@ void vm_execute(struct vm *vm) { doop(OP_JCOND): doop(OP_JCOND_NO_POP): { // jmp if not true [32-bit position] const enum vm_opcode op = vm->code.data[vm->ip++]; - const int16_t pos = (uint16_t)vm->code.data[vm->ip+0] << 8 | - (uint16_t)vm->code.data[vm->ip+1]; + const int16_t pos = (int16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); struct value val = array_top(vm->stack); if(op == OP_JCOND) array_pop(vm->stack); LOG(op == OP_JCOND ? "JCOND %d\n" : "JCOND_NO_POP %d\n", pos); if(value_is_true(val)) vm->ip += pos; - else vm->ip += sizeof(pos); + else vm->ip += (uint32_t)sizeof(pos); dispatch(); } doop(OP_JNCOND): doop(OP_JNCOND_NO_POP): { // jump if true [32-bit position] const enum vm_opcode op = vm->code.data[vm->ip++]; - const int16_t pos = (uint16_t)vm->code.data[vm->ip+0] << 8 | - (uint16_t)vm->code.data[vm->ip+1]; + const int16_t pos = (int16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); struct value val = array_top(vm->stack); if(op == OP_JNCOND) array_pop(vm->stack); LOG(op == OP_JNCOND ? "JNCOND %d\n" : "JNCOND_NO_POP %d\n", pos); if(!value_is_true(val)) vm->ip += pos; - else vm->ip += sizeof(pos); + else vm->ip += (uint32_t)sizeof(pos); dispatch(); } // pops a function/record constructor on top of the stack, @@ -454,54 +454,63 @@ void vm_execute(struct vm *vm) { vm->error != ERROR_NO_ERROR) \ return; \ } while (0) -#define JMP_INTERPRETED_FN_(POP, UNWIND, END_IF_NATIVE) \ - do { \ - if (val.type == TYPE_DICT) { \ - POP \ - const struct value *ctor = dict_get(val.as.dict, "constructor"); \ - if (ctor == NULL) { \ - ERROR(ERROR_RECORD_NO_CONSTRUCTOR, UNWIND); \ - } \ - if (ctor->type == TYPE_NATIVE_FN) { \ - LOG("NATIVE CONSTRUCTOR %d\n", nargs); \ - CALL_NATIVE(ctor->as.fn); \ - do { \ - END_IF_NATIVE \ - } while (0); \ - } else if (ctor->type != TYPE_FN) { \ - ERROR(ERROR_CONSTRUCTOR_NOT_FUNCTION, UNWIND); \ - } \ - ifn = ctor->as.ifn; \ - if (nargs + 1 != ifn->nargs) { \ - ERROR_EXPECT(ERROR_MISMATCH_ARGUMENTS, ifn->nargs, UNWIND); \ - } \ - struct value new_val; \ - value_dict(&new_val, vm); \ - dict_set(new_val.as.dict, "prototype", val); \ - array_push(vm->stack, new_val); \ - } else { \ - POP \ - ifn = val.as.ifn; \ - if (nargs != ifn->nargs) { \ - ERROR_EXPECT(ERROR_MISMATCH_ARGUMENTS, ifn->nargs, UNWIND); \ - } \ - } \ +#define JMP_INTERPRETED_FN_(POP, UNWIND, END_IF_NATIVE) \ + do { \ + if (value_get_type(val) == TYPE_DICT) { \ + do { \ + POP \ + } while (0); \ + const struct value *pctor = dict_get(value_get_pointer(TYPE_DICT, val), "constructor"); \ + if (pctor == NULL) { \ + ERROR(ERROR_RECORD_NO_CONSTRUCTOR, UNWIND); \ + } \ + const struct value ctor = *pctor; \ + switch (value_get_type(ctor)) { \ + case TYPE_NATIVE_FN: { \ + CALL_NATIVE(((value_fn)value_get_pointer(TYPE_NATIVE_FN, ctor))); \ + do { \ + END_IF_NATIVE \ + } while (0); \ + break; \ + } \ + case TYPE_FN: { \ + ifn = value_get_pointer(TYPE_FN, ctor); \ + if (nargs + 1 != ifn->nargs) { \ + ERROR_EXPECT(ERROR_MISMATCH_ARGUMENTS, ifn->nargs, UNWIND); \ + } \ + struct value new_val = value_dict(vm); \ + dict_set(value_get_pointer(TYPE_DICT, new_val), "prototype", val); \ + array_push(vm->stack, new_val); \ + break; \ + } \ + default: \ + ERROR(ERROR_CONSTRUCTOR_NOT_FUNCTION, UNWIND); \ + } \ + } else { \ + do { \ + POP \ + } while (0); \ + ifn = value_get_pointer(TYPE_FN, val); \ + if (nargs != ifn->nargs) { \ + ERROR_EXPECT(ERROR_MISMATCH_ARGUMENTS, ifn->nargs, UNWIND); \ + } \ + } \ } while (0) #define JMP_INTERPRETED_FN(UNWIND, END_IF_NATIVE) JMP_INTERPRETED_FN_(array_pop(vm->stack);, UNWIND, END_IF_NATIVE) #define JMP_INTERPRETED_FN_NO_POP(UNWIND, END_IF_NATIVE) JMP_INTERPRETED_FN_(do{}while(0);, UNWIND, END_IF_NATIVE) - doop(OP_CALL): { + doop(OP_CALL) : { // argument: [arg2][arg1] vm->ip++; struct value val = array_top(vm->stack); - const uint16_t nargs = vm->code.data[vm->ip+0] << 8 | - vm->code.data[vm->ip+1]; - vm->ip += sizeof(nargs); + const uint16_t nargs = (uint16_t)(vm->code.data[vm->ip+0] << 8 | + vm->code.data[vm->ip+1]); + vm->ip += (uint32_t)sizeof(nargs); debug_assert(vm->stack.length >= nargs); LOG("call %d\n", nargs); - switch(val.type) { + switch(value_get_type(val)) { case TYPE_NATIVE_FN: { array_pop(vm->stack); - CALL_NATIVE(val.as.fn); + CALL_NATIVE(((value_fn)(value_get_pointer(TYPE_NATIVE_FN, val)))); break; } case TYPE_FN: case TYPE_DICT: { @@ -539,36 +548,29 @@ void vm_execute(struct vm *vm) { // dictionaries doop(OP_DICT_NEW): { vm->ip++; - LOG("DICT_NEW\n"); - array_push(vm->stack, (struct value){0}); - value_dict(&array_top(vm->stack), vm); - dispatch(); + assert(0); } doop(OP_MEMBER_GET): doop(OP_MEMBER_GET_NO_POP): { const enum vm_opcode op = vm->code.data[vm->ip]; vm->ip++; const char *key = (const char *)&vm->code.data[vm->ip]; // must be null terminated - vm->ip += strlen(key)+1; + vm->ip += (uint32_t)strlen(key) + 1; LOG(op == OP_MEMBER_GET ? "MEMBER_GET %s\n" : "MEMBER_GET_NO_POP %s\n", key); - struct value val = array_top(vm->stack); + const struct value val = array_top(vm->stack); struct dict *dict = NULL; - if(val.type != TYPE_DICT) { + if(value_get_type(val) != TYPE_DICT) { if((dict = value_get_prototype(vm, val)) == NULL) { ERROR(ERROR_CANNOT_ACCESS_NON_RECORD, 1 + strlen(key)+1); } - if(strcmp(key, "prototype") == 0) { - struct value *val = &array_top(vm->stack); - val->type = TYPE_DICT; - val->as.dict = dict; + if (strcmp(key, "prototype") == 0) { + if (op == OP_MEMBER_GET) array_pop(vm->stack); + array_push(vm->stack, value_pointer(TYPE_DICT, dict)); dispatch(); } } else { - if(val.type != TYPE_DICT) { - ERROR(ERROR_CANNOT_ACCESS_NON_RECORD, 1 + strlen(key)+1); - } - dict = val.as.dict; + dict = value_get_pointer(TYPE_DICT, val); if(op == OP_MEMBER_GET) array_pop(vm->stack); } @@ -578,23 +580,22 @@ void vm_execute(struct vm *vm) { } else { ERROR(ERROR_UNKNOWN_KEY, strlen(key)+1); } - dispatch(); } doop(OP_MEMBER_SET): { // stack: [value][dict] vm->ip++; - char *key = (char *)(vm->code.data+vm->ip); // must be null terminated - vm->ip += strlen(key)+1; + const char *key = (char *)(vm->code.data + vm->ip); // must be null terminated + vm->ip += (uint32_t)strlen(key) + 1; LOG("MEMBER_SET %s\n", key); - struct value dval = array_top(vm->stack); - if(dval.type != TYPE_DICT) { + const struct value dval = array_top(vm->stack); + if(value_get_type(dval) != TYPE_DICT) { ERROR(ERROR_CANNOT_ACCESS_NON_RECORD, 1 + strlen(key)+1); } array_pop(vm->stack); struct value val = array_top(vm->stack); - dict_set(dval.as.dict, key, val); + dict_set(value_get_pointer(TYPE_DICT, dval), key, val); dispatch(); } doop(OP_DICT_LOAD): { @@ -602,23 +603,23 @@ void vm_execute(struct vm *vm) { vm->ip++; struct value val = array_top(vm->stack); - int64_t length = val.as.integer; + size_t length = (size_t)value_get_int(val); array_pop(vm->stack); - debug_assert(val.type == TYPE_INT); + debug_assert(value_get_type(val) == TYPE_INT); LOG("DICT_LOAD %ld\n", length); - struct value dval; - value_dict_n(&dval, length, vm); + struct value dval = value_dict_n(length, vm); + struct dict *dict = value_get_pointer(TYPE_DICT, dval); while(length--) { - debug_assert(key.type == TYPE_STR); + debug_assert(value_get_type(key) == TYPE_STR); // key struct value key = array_top(vm->stack); array_pop(vm->stack); // val struct value val = array_top(vm->stack); array_pop(vm->stack); - dict_set_str(dval.as.dict, key.as.str, val); + dict_set_str(dict, value_get_pointer(TYPE_STR, key), val); } array_push(vm->stack, dval); @@ -637,39 +638,45 @@ void vm_execute(struct vm *vm) { const struct value dval = array_top(vm->stack); if (op == OP_INDEX_GET) array_pop(vm->stack); - if(dval.type == TYPE_ARRAY) { - if(index.type != TYPE_INT) { - ERROR(ERROR_KEY_NON_INT, 1); - } - const int64_t i = (int64_t)index.as.integer; - if(!(i >= 0 && i < (int64_t)dval.as.array->length)) { - ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, 1, dval.as.array->length); - } - array_push(vm->stack, dval.as.array->data[i]); - } else if(dval.type == TYPE_STR) { - if(index.type != TYPE_INT) { - ERROR(ERROR_KEY_NON_INT, 1); - } - const int64_t i = index.as.integer; - struct value val; - val.type = TYPE_STR; - val.as.str = string_at(dval.as.str, i, vm); - if (val.as.str == NULL) { - ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, 1, string_len(dval.as.str)); + switch (value_get_type(dval)) { + case TYPE_ARRAY: { + array_obj *array = value_get_pointer(TYPE_ARRAY, dval); + if (value_get_type(index) != TYPE_INT) { + ERROR(ERROR_KEY_NON_INT, 1); + } + const int32_t i = (int32_t)value_get_int(index); + if (!(i >= 0 && i < (int32_t)array->length)) { + ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, 1, array->length); + } + array_push(vm->stack, array->data[i]); + break; } - array_push(vm->stack, val); - } else if(dval.type == TYPE_DICT) { - if(index.type != TYPE_STR) { - ERROR(ERROR_RECORD_KEY_NON_STRING, 1); + case TYPE_STR: { + if (value_get_type(index) != TYPE_INT) { + ERROR(ERROR_KEY_NON_INT, 1); + } + const int32_t i = (int32_t)value_get_int(index); + struct string *s = string_at(value_get_pointer(TYPE_STR, dval), i, vm); + if (s == NULL) { + ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, 1, string_len(value_get_pointer(TYPE_STR, dval))); + } + array_push(vm->stack, value_pointer(TYPE_STR, s)); + break; } - const struct value *val = dict_get_str(dval.as.dict, index.as.str); - if(val != NULL) { - array_push(vm->stack, *val); - } else { - ERROR(ERROR_UNKNOWN_KEY, 1); + case TYPE_DICT: { + if (value_get_type(index) != TYPE_STR) { + ERROR(ERROR_RECORD_KEY_NON_STRING, 1); + } + const struct value *val = dict_get_str(value_get_pointer(TYPE_DICT, dval), value_get_pointer(TYPE_STR, index)); + if (val != NULL) { + array_push(vm->stack, *val); + } else { + ERROR(ERROR_UNKNOWN_KEY, 1); + } + break; } - } else { - ERROR(ERROR_CANNOT_ACCESS_NON_RECORD, 1); + default: + ERROR(ERROR_CANNOT_ACCESS_NON_RECORD, 1); } dispatch(); } @@ -677,54 +684,57 @@ void vm_execute(struct vm *vm) { vm->ip++; LOG("INDEX_SET\n"); - struct value index = array_top(vm->stack); + const struct value index = array_top(vm->stack); array_pop(vm->stack); - struct value dval = array_top(vm->stack); + const struct value dval = array_top(vm->stack); array_pop(vm->stack); - struct value val = array_top(vm->stack); - - if(dval.type == TYPE_ARRAY) { - if(index.type != TYPE_INT) { - ERROR(ERROR_KEY_NON_INT, 1); - } - const int64_t i = index.as.integer; - if (!(i >= 0 && i < (int64_t)dval.as.array->length)) { - ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, dval.as.array->length, 1); + const struct value val = array_top(vm->stack); + switch (value_get_type(dval)) { + case TYPE_ARRAY: { + if (value_get_type(index) != TYPE_INT) { + ERROR(ERROR_KEY_NON_INT, 1); + } + const int64_t i = value_get_int(index); + array_obj *array = value_get_pointer(TYPE_ARRAY, dval); + if (!(i >= 0 && i < (int64_t)array->length)) { + ERROR_EXPECT(ERROR_UNBOUNDED_ACCESS, array->length, 1); + } + array->data[i] = val; + break; } - dval.as.array->data[i] = val; - } else if(dval.type == TYPE_DICT) { - if(index.type != TYPE_STR) { - ERROR(ERROR_RECORD_KEY_NON_STRING, 1); + case TYPE_DICT: { + if (value_get_type(index) != TYPE_STR) { + ERROR(ERROR_RECORD_KEY_NON_STRING, 1); + } + dict_set_str(value_get_pointer(TYPE_DICT, dval), value_get_pointer(TYPE_STR, index), val); + break; } - dict_set_str(dval.as.dict, index.as.str, val); - } else { - ERROR(ERROR_EXPECTED_RECORD_ARRAY, 1); + default: + ERROR(ERROR_EXPECTED_RECORD_ARRAY, 1); } dispatch(); } - doop(OP_ARRAY_LOAD): { + doop(OP_ARRAY_LOAD) : { vm->ip++; - struct value val = array_top(vm->stack); - int64_t length = val.as.integer; + size_t length = (size_t)value_get_int(array_top(vm->stack)); array_pop(vm->stack); - debug_assert(val.type == TYPE_INT); LOG("ARRAY_LOAD %ld\n", length); - struct value aval; if(length == 0) { - value_array(&aval, vm); + array_push(vm->stack, value_array(vm)); } else { - value_array_n(&aval, length, vm); - aval.as.array->length = length; + struct value aval = value_array_n(length, vm); + array_obj *array = value_get_pointer(TYPE_ARRAY, aval); + array->length = length; while(length--) { - aval.as.array->data[length] = array_top(vm->stack); + array->data[length] = array_top(vm->stack); array_pop(vm->stack); } + array_push(vm->stack, aval); } - array_push(vm->stack, aval); dispatch(); } @@ -736,17 +746,17 @@ void vm_execute(struct vm *vm) { struct exframe *frame = vm_enter_exframe(vm); struct value error = {0}; - while((error = array_top(vm->stack)).type != TYPE_NIL) { + while(value_get_type(error = array_top(vm->stack)) != TYPE_NIL) { // error type - if(error.type != TYPE_DICT) { + if(value_get_type(error) != TYPE_DICT) { ERROR(ERROR_CASE_EXPECTS_DICT, 1); } array_pop(vm->stack); // val struct value fn = array_top(vm->stack); - debug_assert(fn.type == TYPE_FN); + debug_assert(value_get_type(fn) == TYPE_FN); array_pop(vm->stack); - exframe_set_handler(frame, error.as.dict, fn.as.ifn); + exframe_set_handler(frame, value_get_pointer(TYPE_DICT, error), value_get_pointer(TYPE_FN, fn)); } array_pop(vm->stack); // pop nil @@ -770,8 +780,8 @@ void vm_execute(struct vm *vm) { } doop(OP_EXFRAME_RET): { vm->ip++; - const uint16_t pos = (uint16_t)vm->code.data[vm->ip + 0] << 8 | - (uint16_t)vm->code.data[vm->ip + 1]; + const uint16_t pos = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); vm->ip += pos; LOG("EXFRAME_RET %d\n", pos); vm_leave_exframe(vm); @@ -782,36 +792,38 @@ void vm_execute(struct vm *vm) { doop(OP_RETCALL): { vm->ip++; struct value val = array_top(vm->stack); - const uint16_t nargs = (uint16_t)vm->code.data[vm->ip+0] << 8 | - (uint16_t)vm->code.data[vm->ip+1]; - vm->ip += sizeof(nargs); + const uint16_t nargs = (uint16_t)(vm->code.data[vm->ip+0] << 8 | + vm->code.data[vm->ip+1]); + vm->ip += (uint32_t)sizeof(nargs); debug_assert(vm->stack.length >= nargs); LOG("retcall %d\n", nargs); - switch(val.type) { - case TYPE_NATIVE_FN: { - array_pop(vm->stack); - CALL_NATIVE(val.as.fn); - if (vm_leave_env(vm)) { - LOG("return from vm_call\n"); - return; - } - break; } - case TYPE_FN: - case TYPE_DICT: { - struct function *ifn; - JMP_INTERPRETED_FN(1 + sizeof(nargs), + switch (value_get_type(val)) { + case TYPE_NATIVE_FN: { + array_pop(vm->stack); + CALL_NATIVE(((value_fn)value_get_pointer(TYPE_NATIVE_FN, val))); if (vm_leave_env(vm)) { LOG("return from vm_call\n"); return; - } else { - dispatch(); } - ); - // caller - vm_enter_env_tail(vm, ifn); - break; } - default: { - ERROR(ERROR_EXPECTED_CALLABLE, 1 + sizeof(nargs)); } + break; + } + case TYPE_FN: + case TYPE_DICT: { + struct function *ifn; + JMP_INTERPRETED_FN(1 + sizeof(nargs), + if (vm_leave_env(vm)) { + LOG("return from vm_call\n"); + return; + } else { + dispatch(); + }); + // caller + vm_enter_env_tail(vm, ifn); + break; + } + default: { + ERROR(ERROR_EXPECTED_CALLABLE, 1 + sizeof(nargs)); + } } dispatch(); } @@ -823,9 +835,9 @@ void vm_execute(struct vm *vm) { if (pval == NULL) ERROR(ERROR_EXPECTED_CALLABLE, 1 + sizeof(pos)); \ const struct value val = *pval; \ const uint16_t nargs = 1; \ - switch (val.type) { \ + switch (value_get_type(val)) { \ case TYPE_NATIVE_FN: { \ - CALL_NATIVE(val.as.fn); \ + CALL_NATIVE(((value_fn)(value_get_pointer(TYPE_NATIVE_FN, val)))); \ break; \ } \ case TYPE_FN: \ @@ -849,100 +861,95 @@ void vm_execute(struct vm *vm) { } \ } while (0); vm->ip++; - const uint16_t pos = (uint16_t)vm->code.data[vm->ip + 0] << 8 | - (uint16_t)vm->code.data[vm->ip + 1]; + const uint16_t pos = (uint16_t)(vm->code.data[vm->ip + 0] << 8 | + vm->code.data[vm->ip + 1]); LOG("FOR_IN %d\n", pos); debug_assert(vm->stack.length > 0); - struct value *top = &array_top(vm->stack); - switch(top->type) { - // setup - case TYPE_STR: { - array_obj *chars = string_chars(top->as.str, vm); - array_pop(vm->stack); - struct value val = {0}; - val = (struct value){ - .as.array = chars, - .type = TYPE_ARRAY - }; - array_push(vm->stack, val); - val = (struct value){ - .as.integer = 1, - .type = TYPE_INTERPRETER_ITERATOR - }; - array_push(vm->stack, val); - array_push(vm->stack, chars->data[0]); - break; } - case TYPE_ARRAY: { - if (top->as.array->length == 0) { // skip empty - vm->ip += pos; + struct value top = array_top(vm->stack); + switch (value_get_type(top)) { + // setup + case TYPE_STR: { + array_obj *chars = string_chars(value_get_pointer(TYPE_STR, top), vm); array_pop(vm->stack); - dispatch(); + array_push(vm->stack, value_pointer(TYPE_ARRAY, chars)); + array_push(vm->stack, value_pointer(TYPE_INTERPRETER_ITERATOR, (void *)1)); + array_push(vm->stack, chars->data[0]); + break; } - const struct value top_copy = *top; - struct value val = { - .as.integer = 1, - .type = TYPE_INTERPRETER_ITERATOR - }; - array_push(vm->stack, val); - array_push(vm->stack, top_copy.as.array->data[0]); - break; } - case TYPE_DICT: { - struct dict *dict = top->as.dict; - const struct value *pval = dict_get(dict, "next"); - struct value val = { - .as.integer = 0, - .type = TYPE_INTERPRETER_ITERATOR - }; - array_push(vm->stack, val); - vm->ip += sizeof(pos); - array_push(vm->stack, *top); // pass arg - CALL_DICT_ITERATOR_FN - break; } - // interation - case TYPE_INTERPRETER_ITERATOR: { - debug_assert(vm->stack.length >= 2); - const struct value iterator = vm->stack.data[vm->stack.length-2]; - switch (iterator.type) { - case TYPE_NIL: { + case TYPE_ARRAY: { + array_obj *array = value_get_pointer(TYPE_ARRAY, top); + if (array->length == 0) { // skip empty vm->ip += pos; + array_pop(vm->stack); dispatch(); } - case TYPE_ARRAY: { - const array_obj *array = iterator.as.array; - const size_t idx = (size_t)top->as.integer; - if (idx == array->length) { /* end of it */ - array_pop(vm->stack); /* iterator */ - array_pop(vm->stack); /* array */ + array_push(vm->stack, value_pointer(TYPE_INTERPRETER_ITERATOR, (void *)1)); + array_push(vm->stack, array->data[0]); + break; + } + case TYPE_DICT: { + struct dict *dict = value_get_pointer(TYPE_DICT, top); + const struct value *pval = dict_get(dict, "next"); + array_push(vm->stack, value_pointer(TYPE_INTERPRETER_ITERATOR, (void *)0)); + vm->ip += (uint32_t)sizeof(pos); + array_push(vm->stack, top); // pass arg + CALL_DICT_ITERATOR_FN + break; + } + // interation + case TYPE_INTERPRETER_ITERATOR: { + debug_assert(vm->stack.length >= 2); + const struct value iterator = vm->stack.data[vm->stack.length - 2]; + switch (value_get_type(iterator)) { + case TYPE_NIL: { + LOG("END\n"); vm->ip += pos; dispatch(); - } else { // continuation - top->as.integer++; - array_push(vm->stack, array->data[idx]); } - break; - } - case TYPE_DICT: { - const struct dict *dict = iterator.as.dict; - if (dict_get(dict, "stopped") != NULL) { - array_pop(vm->stack); /* iterator */ - array_pop(vm->stack); /* record */ - vm->ip += pos; - dispatch(); + case TYPE_ARRAY: { + const array_obj *array = value_get_pointer(TYPE_ARRAY, iterator); + const size_t idx = (size_t)value_get_int(top); + if (idx == array->length) { /* end of it */ + LOG("END\n"); + array_pop(vm->stack); /* iterator */ + array_pop(vm->stack); /* array */ + vm->ip += pos; + dispatch(); + } else { // continuation + LOG("CONTINUE\n"); + array_pop(vm->stack); /* old iterator */ + array_push(vm->stack, value_pointer(TYPE_INTERPRETER_ITERATOR, (void *)(idx + 1))); + array_push(vm->stack, array->data[idx]); + } + break; } - - const struct value *pval = dict_get(dict, "next"); - vm->ip += sizeof(pos); - array_push(vm->stack, iterator); // arg - CALL_DICT_ITERATOR_FN - break; + case TYPE_DICT: { + LOG("CONTINUE\n"); + const struct dict *dict = value_get_pointer(TYPE_DICT, iterator); + if (dict_get(dict, "stopped") != NULL) { + array_pop(vm->stack); /* iterator */ + array_pop(vm->stack); /* record */ + vm->ip += pos; + dispatch(); + } + + const struct value *pval = dict_get(dict, "next"); + vm->ip += (uint32_t)sizeof(pos); + array_push(vm->stack, iterator); // arg + CALL_DICT_ITERATOR_FN + break; + } + default: + ERROR(ERROR_EXPECTED_ITERABLE, 1 + sizeof(pos)); } - default: ERROR(ERROR_EXPECTED_ITERABLE, 1 + sizeof(pos)); + break; + } + default: { + LOG("NOT ITERABLE\n"); + ERROR(ERROR_EXPECTED_ITERABLE, sizeof(pos)); } - break; - } - default: ERROR(ERROR_EXPECTED_ITERABLE, sizeof(pos)); } - vm->ip += sizeof(pos); + vm->ip += (uint32_t)sizeof(pos); dispatch(); #undef CALL_DICT_ITERATOR_FN } @@ -962,55 +969,54 @@ void vm_execute(struct vm *vm) { vm->ip++; char *str = (char *)&vm->code.data[vm->ip]; // must be null terminated LOG("USE %s\n", str); - vm->ip += strlen(str)+1; + vm->ip += (uint32_t)strlen(str) + 1; vm_load_module(vm, str); dispatch(); } } struct value vm_call(struct vm *vm, const struct value fn, const a_arguments *args) { - static struct value errorval; - errorval.type = TYPE_INTERPRETER_ERROR; - struct function *ifn = NULL; + const uint16_t nargs = args->length; - if (fn.type == TYPE_NATIVE_FN) { - for (int64_t i = args->length - 1; i >= 0; i--) { + if (value_get_type(fn) == TYPE_NATIVE_FN) { + for (size_t i = args->length; i-- > 0;) { array_push(vm->stack, args->data[i]); } - fn.as.fn(vm, args->length); - } else if(fn.type == TYPE_DICT) { - const struct value *ctor = dict_get(fn.as.dict, "constructor"); - if(ctor == NULL) { + ((value_fn)(value_get_pointer(TYPE_NATIVE_FN, fn)))(vm, nargs); + } else if(value_get_type(fn) == TYPE_DICT) { + const struct value *pctor = dict_get(value_get_pointer(TYPE_DICT, fn), "constructor"); + if(pctor == NULL) { vm->error = ERROR_RECORD_NO_CONSTRUCTOR; - return errorval; + return value_interpreter_error(); } - if(ctor->type == TYPE_NATIVE_FN) { - for(int64_t i = args->length-1; i >= 0; i--) { + const struct value ctor = *pctor; + if(value_get_type(ctor) == TYPE_NATIVE_FN) { + for (size_t i = args->length; i-- > 0;) { array_push(vm->stack, args->data[i]); } - ctor->as.fn(vm, args->length); - if(vm->error) return errorval; + ((value_fn)(value_get_pointer(TYPE_NATIVE_FN, ctor)))(vm, nargs); + if (vm->error) return value_interpreter_error(); const struct value val = array_top(vm->stack); array_pop(vm->stack); return val; - } else if(ctor->type != TYPE_FN) { - vm->error = ERROR_CONSTRUCTOR_NOT_FUNCTION; - return errorval; + } else if (value_get_type(ctor) == TYPE_FN) { + ifn = value_get_pointer(TYPE_FN, ctor); } else { - ifn = ctor->as.ifn; + vm->error = ERROR_CONSTRUCTOR_NOT_FUNCTION; + return value_interpreter_error(); } - } else if (fn.type == TYPE_FN) { - ifn = fn.as.ifn; + } else if (value_get_type(fn) == TYPE_FN) { + ifn = value_get_pointer(TYPE_FN, fn); } else { vm->error = ERROR_EXPECTED_CALLABLE; - return errorval; + return value_interpreter_error(); } if((uint32_t)args->length != ifn->nargs) { vm->ip = ifn->ip; vm->error = ERROR_MISMATCH_ARGUMENTS; - return errorval; + return value_interpreter_error(); } const uint32_t last = vm->ip; @@ -1019,18 +1025,19 @@ struct value vm_call(struct vm *vm, const struct value fn, const a_arguments *ar vm->ip = (uint32_t)-1; struct env *curenv = vm_enter_env(vm, ifn); // setup stack/ip - for(int64_t i = args->length-1; i >= 0; i--) { + printf("%ld\n", args->length); + for (size_t i = (size_t)args->length; i-- > 0;) { array_push(vm->stack, args->data[i]); } // call it vm_execute(vm); if(vm->error || vm->exframe_fallthrough != NULL) { // exception LOG("falling through psl wait %ld", vm->native_call_depth); - return errorval; + return value_interpreter_error(); } if(vm->localenv != curenv) { // exception occurred outside of function's scope // NOTE: curenv already free'd from unwinding - return errorval; + return value_interpreter_error(); } // restore ip LOG("vm_call complete\n"); diff --git a/src/vm/vm.h b/src/vm/vm.h index 6dc23ff..cad3900 100644 --- a/src/vm/vm.h +++ b/src/vm/vm.h @@ -113,6 +113,7 @@ struct exframe *vm_enter_exframe(struct vm *); bool vm_leave_exframe(struct vm *); bool vm_raise(struct vm *); +struct function; struct value vm_call(struct vm *, const struct value, const a_arguments*); struct env *vm_enter_env(struct vm *, struct function *); struct env *vm_enter_env_tail(struct vm *, struct function *); diff --git a/src/vmbindings/cnativeval.rs b/src/vmbindings/cnativeval.rs index 0b43e39..343bac9 100644 --- a/src/vmbindings/cnativeval.rs +++ b/src/vmbindings/cnativeval.rs @@ -1,88 +1,120 @@ //! Provides the native value representation //! used by the virtual machine -use super::function::Function; -use super::gc::{ref_dec, ref_inc, Gc, GcManager, GcTraceable}; -use super::record::Record; +extern crate num_derive; +use num_traits::cast::{FromPrimitive, ToPrimitive}; + +use super::gc::{ref_dec, ref_inc, Gc}; use super::value::{NativeFnData, Value}; #[repr(u8)] #[allow(non_camel_case_types, dead_code)] -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, FromPrimitive, ToPrimitive)] /// Type of the native value pub enum NativeValueType { - TYPE_NIL = 0, TYPE_INT = 1, - TYPE_FLOAT = 2, - TYPE_NATIVE_FN = 3, - TYPE_FN = 4, - TYPE_STR = 5, - TYPE_DICT = 6, - TYPE_ARRAY = 7, - TYPE_INTERPRETER_ERROR = 127, + TYPE_NATIVE_FN = 2, + TYPE_FN = 3, + TYPE_STR = 4, + TYPE_DICT = 5, + TYPE_ARRAY = 6, + TYPE_INTERPRETER_ERROR = 7, + TYPE_INTERPRETER_ITERATOR = 8, + TYPE_NIL = 9, + TYPE_FLOAT, } -#[repr(C, packed)] +#[repr(transparent)] #[derive(Debug, PartialEq, Clone, Copy)] /// Native value representation used by the virtual machine -pub struct NativeValue { - pub data: u64, - pub r#type: NativeValueType, -} +pub struct NativeValue(u64); + +const RESERVED_NAN: u64 = 0x7ff; +const INT_MASK: u64 = 0b01111111_11110001_00000000_00000000_00000000_00000000_00000000_00000000; +const TAG_BIT_MASK: u64 = + 0b00000000_00001111_00000000_00000000_00000000_00000000_00000000_00000000; +const LOWER_MASK: u64 = 0xffffffffffff; impl NativeValue { + pub fn tag(&self) -> NativeValueType { + if !f64::is_nan(unsafe { std::mem::transmute(self.0) }) { + return NativeValueType::TYPE_FLOAT; + } + if let Some(x) = NativeValueType::from_u8(((self.0 & TAG_BIT_MASK) >> 48) as u8) { + x + } else { + unreachable!() + } + } + fn get_low48(&self) -> u64 { + self.0 & LOWER_MASK + } + + pub fn new_nil() -> NativeValue { + NativeValue(((RESERVED_NAN << 4) | NativeValueType::TYPE_NIL.to_u64().unwrap()) << 48) + } + pub fn new_i32(u: i32) -> NativeValue { + NativeValue(INT_MASK | ((u as u32) as u64 & 0xffffffff)) + } + pub fn new_f64(u: f64) -> NativeValue { + NativeValue(unsafe { std::mem::transmute(u) }) + } + pub fn new_tagged_pointer(tag: NativeValueType, ptr: *const T) -> NativeValue { + let loptr = unsafe { std::mem::transmute::<_, u64>(ptr) & 0xffffffffffff }; + NativeValue((((RESERVED_NAN << 4) | tag.to_u64().unwrap()) << 48) | loptr) + } + /// Converts the native value into a wrapped Value. pub fn unwrap(&self) -> Value { use std::mem::transmute; - #[allow(non_camel_case_types)] - match &self.r#type { - NativeValueType::TYPE_NIL => Value::Nil, - NativeValueType::TYPE_INT => unsafe { Value::Int(transmute::(self.data)) }, - NativeValueType::TYPE_FLOAT => Value::Float(f64::from_bits(self.data)), - NativeValueType::TYPE_NATIVE_FN => unsafe { - Value::NativeFn(transmute::(self.data)) - }, - NativeValueType::TYPE_FN => unsafe { - Value::Fn(Gc::from_raw(self.data as *mut Function)) - }, - NativeValueType::TYPE_STR => unsafe { - Value::Str(Gc::from_raw(self.data as *mut String)) - }, - NativeValueType::TYPE_DICT => unsafe { - Value::Record(Gc::from_raw(self.data as *mut Record)) - }, - NativeValueType::TYPE_ARRAY => unsafe { - Value::Array(Gc::from_raw(self.data as *mut Vec)) - }, - _ => { - panic!("type was: {:?}", self.r#type) + unsafe { + match self.tag() { + NativeValueType::TYPE_FLOAT => Value::Float(transmute(self.0)), + NativeValueType::TYPE_INT => Value::Int((self.0 & 0xffffffff) as i32), + NativeValueType::TYPE_NATIVE_FN => { + Value::NativeFn(transmute::<_, NativeFnData>(self.get_low48())) + } + NativeValueType::TYPE_FN => Value::Fn(Gc::from_raw(transmute(self.get_low48()))), + NativeValueType::TYPE_STR => Value::Str(Gc::from_raw(transmute(self.get_low48()))), + NativeValueType::TYPE_DICT => { + Value::Record(Gc::from_raw(transmute(self.get_low48()))) + } + NativeValueType::TYPE_ARRAY => { + Value::Array(Gc::from_raw(transmute(self.get_low48()))) + } + NativeValueType::TYPE_NIL => Value::Nil, + _ => Value::Nil, } } } - pub fn as_pointer(&self) -> Option<*mut libc::c_void> { + pub unsafe fn as_gc_pointer(&self) -> Option<*mut libc::c_void> { #[allow(non_camel_case_types)] - match self.r#type { + match self.tag() { NativeValueType::TYPE_FN | NativeValueType::TYPE_STR | NativeValueType::TYPE_DICT | NativeValueType::TYPE_ARRAY => { - if self.data == 0 { None } - else { Some(self.data as *mut libc::c_void) } - }, - _ => None + let low48 = self.get_low48(); + if low48 == 0 { + None + } else { + Some(std::mem::transmute(low48)) + } + } + _ => None, } } // reference counting pub unsafe fn ref_inc(&self) { #[allow(non_camel_case_types)] - match self.r#type { + match self.tag() { NativeValueType::TYPE_FN | NativeValueType::TYPE_STR | NativeValueType::TYPE_DICT | NativeValueType::TYPE_ARRAY => { - ref_inc(self.data as *mut libc::c_void); + ref_inc(std::mem::transmute(self.get_low48())); } _ => {} } @@ -90,12 +122,12 @@ impl NativeValue { pub unsafe fn ref_dec(&self) { #[allow(non_camel_case_types)] - match self.r#type { + match self.tag() { NativeValueType::TYPE_FN | NativeValueType::TYPE_STR | NativeValueType::TYPE_DICT | NativeValueType::TYPE_ARRAY => { - ref_dec(self.data as *mut libc::c_void); + ref_dec(std::mem::transmute(self.get_low48())); } _ => {} } diff --git a/src/vmbindings/foreignc.rs b/src/vmbindings/foreignc.rs index 70a79a6..fda849f 100644 --- a/src/vmbindings/foreignc.rs +++ b/src/vmbindings/foreignc.rs @@ -5,7 +5,6 @@ use super::cnativeval::NativeValue; use super::env::Env; use super::exframe::ExFrame; use super::function::Function; -use super::gc::Gc; use super::record::Record; use super::value::Value; use super::vm::Vm; @@ -131,7 +130,9 @@ mod foreignc { } #[no_mangle] - unsafe extern "C" fn string_repeat(cleft: *const String, n: i64, vm: *const Vm) -> *mut String { + unsafe extern "C" fn string_repeat( + cleft: *const String, n: i64, vm: *const Vm, + ) -> *mut String { let left: &'static String = &*cleft; (&*vm).malloc(left.repeat(n as usize)).into_raw() } @@ -150,9 +151,7 @@ mod foreignc { } #[no_mangle] - unsafe extern "C" fn string_at( - left: *const String, idx: i64, vm: *const Vm, - ) -> *mut String { + unsafe extern "C" fn string_at(left: *const String, idx: i64, vm: *const Vm) -> *mut String { let left: &'static String = &*left; if let Some(ch) = left.graphemes(true).nth(idx as usize) { (&*vm).malloc(ch.to_string()).into_raw() @@ -368,11 +367,7 @@ mod foreignc { #[no_mangle] #[allow(safe_packed_borrows)] pub extern "C" fn value_print(val: NativeValue) { - if (val.r#type as u8) < 127 { - eprint!("{:?}", val.unwrap()); - } else { - eprint!("[interpreter {}]", val.data); - } + eprint!("{:?}", val.unwrap()); } // #endregion diff --git a/src/vmbindings/function.rs b/src/vmbindings/function.rs index e9f1d2a..c2951c6 100644 --- a/src/vmbindings/function.rs +++ b/src/vmbindings/function.rs @@ -1,7 +1,7 @@ //! Provides a function value in Hana use super::env::Env; -use crate::vmbindings::gc::{GcNode, GcTraceable, push_gray_body}; +use crate::vmbindings::gc::{push_gray_body, GcNode, GcTraceable}; use std::ptr::null_mut; #[repr(C)] @@ -44,7 +44,7 @@ impl Function { impl GcTraceable for Function { unsafe fn trace(&self, gray_nodes: &mut Vec<*mut GcNode>) { for val in self.bound.slots.iter() { - if let Some(ptr) = val.as_pointer() { + if let Some(ptr) = val.as_gc_pointer() { push_gray_body(gray_nodes, ptr); } } diff --git a/src/vmbindings/gc.rs b/src/vmbindings/gc.rs index 9b43ecd..0299758 100644 --- a/src/vmbindings/gc.rs +++ b/src/vmbindings/gc.rs @@ -100,7 +100,8 @@ impl GcManager { Gc { ptr: NonNull::new(unsafe { self.malloc_raw(vm, val, |ptr| drop_in_place::(ptr as *mut T)) - }).unwrap(), + }) + .unwrap(), } } @@ -128,22 +129,24 @@ impl GcManager { //eprintln!("GRAY NODES MARKING: {:?}, {:?}", gray_nodes, self.gray_nodes); // nothing left to traverse, sweeping phase: if self.gray_nodes.is_empty() && self.bytes_allocated > self.threshold { - let mut node: *mut GcNode = self.first_node; let mut prev: *mut GcNode = null_mut(); // don't sweep nodes with at least one native reference - node = self.first_node; + let mut node: *mut GcNode = self.first_node; while !node.is_null() { let next: *mut GcNode = (*node).next; let ptr = node.add(1) as *mut c_void; if (*node).native_refs > 0 && (*node).color != GcNodeColor::Black { //eprintln!("{:p}", node); // get its children and subchildren - let mut children : Vec<*mut GcNode> = Vec::new(); + let mut children: Vec<*mut GcNode> = Vec::new(); ((*node).tracer)(std::mem::transmute(ptr), std::mem::transmute(&mut children)); let mut i = 0; while i < children.len() { let node = children[i]; - ((*node).tracer)(std::mem::transmute(ptr), std::mem::transmute(&mut children)); + ((*node).tracer)( + std::mem::transmute(ptr), + std::mem::transmute(&mut children), + ); i += 1; } // color them black @@ -297,14 +300,14 @@ type GenericTraceFunction = unsafe fn(*mut c_void, *mut c_void); // native traceables impl GcTraceable for String { - unsafe fn trace(&self, manager: &mut Vec<*mut GcNode>) {} + unsafe fn trace(&self, _: &mut Vec<*mut GcNode>) {} } use super::cnativeval::NativeValue; impl GcTraceable for Vec { unsafe fn trace(&self, gray_nodes: &mut Vec<*mut GcNode>) { for val in self.iter() { - if let Some(ptr) = val.as_pointer() { + if let Some(ptr) = val.as_gc_pointer() { push_gray_body(gray_nodes, ptr); } } @@ -336,4 +339,4 @@ pub unsafe fn push_gray_body(gray_nodes: &mut Vec<*mut GcNode>, ptr: *mut c_void } (*node).color = GcNodeColor::Gray; gray_nodes.push(node); -} \ No newline at end of file +} diff --git a/src/vmbindings/record.rs b/src/vmbindings/record.rs index cdcdb64..2b05282 100644 --- a/src/vmbindings/record.rs +++ b/src/vmbindings/record.rs @@ -2,7 +2,7 @@ use super::chmap::CHashMap; use super::cnativeval::NativeValue; -use super::gc::{GcNode, GcTraceable, push_gray_body}; +use super::gc::{push_gray_body, GcNode, GcTraceable}; use super::value::Value; use std::any::Any; use std::borrow::Borrow; @@ -71,8 +71,8 @@ impl Record { impl GcTraceable for Record { unsafe fn trace(&self, gray_nodes: &mut Vec<*mut GcNode>) { - for(_, val) in self.iter() { - if let Some(ptr) = val.as_pointer() { + for (_, val) in self.iter() { + if let Some(ptr) = val.as_gc_pointer() { push_gray_body(gray_nodes, ptr); } } diff --git a/src/vmbindings/value.rs b/src/vmbindings/value.rs index cc8f6d8..ee07b98 100644 --- a/src/vmbindings/value.rs +++ b/src/vmbindings/value.rs @@ -20,7 +20,7 @@ pub enum Value { True, False, - Int(i64), + Int(i32), Float(f64), NativeFn(NativeFnData), Fn(Gc), @@ -40,7 +40,7 @@ extern "C" { impl Value { // #region coerce value to type #[cfg_attr(tarpaulin, skip)] - pub fn int(&self) -> i64 { + pub fn int(&self) -> i32 { match self { Value::Int(s) => *s, _ => { @@ -92,52 +92,30 @@ impl Value { // wrapper for native pub fn wrap(&self) -> NativeValue { - use std::mem::transmute; #[allow(non_camel_case_types)] - unsafe { - match &self { - Value::Nil => NativeValue { - r#type: NativeValueType::TYPE_NIL, - data: 0, - }, - Value::True => NativeValue { - r#type: NativeValueType::TYPE_INT, - data: 1, - }, - Value::False => NativeValue { - r#type: NativeValueType::TYPE_INT, - data: 0, - }, - Value::Int(n) => NativeValue { - r#type: NativeValueType::TYPE_INT, - data: transmute::(*n), - }, - Value::Float(n) => NativeValue { - r#type: NativeValueType::TYPE_FLOAT, - data: transmute::(*n), - }, - Value::NativeFn(f) => NativeValue { - r#type: NativeValueType::TYPE_NATIVE_FN, - data: transmute::(*f), - }, - Value::Fn(p) => NativeValue { - r#type: NativeValueType::TYPE_FN, - data: transmute::<*const Function, u64>(p.to_raw()), - }, - Value::Str(p) => NativeValue { - r#type: NativeValueType::TYPE_STR, - data: transmute::<*const String, u64>(p.to_raw()), - }, - Value::Record(p) => NativeValue { - r#type: NativeValueType::TYPE_DICT, - data: transmute::<*const Record, u64>(p.to_raw()), - }, - Value::Array(p) => NativeValue { - r#type: NativeValueType::TYPE_ARRAY, - data: transmute::<*const Vec, u64>(p.to_raw()), - }, - _ => unimplemented!(), + match &self { + Value::Nil => NativeValue::new_nil(), + Value::True => NativeValue::new_i32(1), + Value::False => NativeValue::new_i32(0), + Value::Int(n) => NativeValue::new_i32(*n), + Value::Float(n) => NativeValue::new_f64(*n), + Value::NativeFn(p) => unsafe { + NativeValue::new_tagged_pointer( + NativeValueType::TYPE_NATIVE_FN, + std::mem::transmute::<_, *mut libc::c_void>(*p), + ) + }, + Value::Fn(p) => NativeValue::new_tagged_pointer(NativeValueType::TYPE_FN, p.to_raw()), + Value::Str(p) => { + NativeValue::new_tagged_pointer(NativeValueType::TYPE_STR, p.to_raw()) + } + Value::Record(p) => { + NativeValue::new_tagged_pointer(NativeValueType::TYPE_DICT, p.to_raw()) } + Value::Array(p) => { + NativeValue::new_tagged_pointer(NativeValueType::TYPE_ARRAY, p.to_raw()) + } + _ => unreachable!(), } } diff --git a/src/vmbindings/vm.rs b/src/vmbindings/vm.rs index 5de86a1..b2ddf21 100644 --- a/src/vmbindings/vm.rs +++ b/src/vmbindings/vm.rs @@ -9,7 +9,7 @@ use std::rc::Rc; extern crate libc; use super::chmap::CHashMap; -use super::cnativeval::{NativeValueType, NativeValue}; +use super::cnativeval::{NativeValue, NativeValueType}; use super::env::Env; use super::exframe::ExFrame; use super::function::Function; @@ -246,14 +246,14 @@ impl Vm { pub(crate) unsafe fn gc_new_gray_node_stack(&self) -> Vec<*mut GcNode> { let mut vec = Vec::new(); for (_, val) in self.global().iter() { - if let Some(ptr) = val.as_pointer() { + if let Some(ptr) = val.as_gc_pointer() { push_gray_body(&mut vec, ptr); } } // stack let stack = &self.stack; for val in stack.iter() { - if let Some(ptr) = val.as_pointer() { + if let Some(ptr) = val.as_gc_pointer() { push_gray_body(&mut vec, ptr); } } @@ -263,7 +263,7 @@ impl Vm { let localenv = localenv.as_ptr(); while env != localenv { for val in (*env).slots.as_slice().iter() { - if let Some(ptr) = (*val).as_pointer() { + if let Some(ptr) = (*val).as_gc_pointer() { push_gray_body(&mut vec, ptr); } } @@ -271,7 +271,7 @@ impl Vm { } env = localenv; for val in (*env).slots.as_slice().iter() { - if let Some(ptr) = (*val).as_pointer() { + if let Some(ptr) = (*val).as_gc_pointer() { push_gray_body(&mut vec, ptr); } } @@ -381,7 +381,7 @@ impl Vm { // functions pub fn call(&mut self, fun: NativeValue, args: &Vec) -> Option { let val = unsafe { vm_call(self, fun, args) }; - if val.r#type == NativeValueType::TYPE_INTERPRETER_ERROR { + if val.tag() == NativeValueType::TYPE_INTERPRETER_ERROR { None } else { Some(val) diff --git a/tests/hanayo.rs b/tests/hanayo.rs index 589da47..96ef3e5 100644 --- a/tests/hanayo.rs +++ b/tests/hanayo.rs @@ -60,6 +60,18 @@ y = (10).prototype == Int ); assert_eq!(vm.global().get("y").unwrap().unwrap().int(), 1); } + + #[test] + fn native_tail_ret() { + let _vm: Vm = eval!( + " +function x() begin + return print('hello') +end +x() +" + ); + } // #endregion // #region int diff --git a/tests/interpreter.rs b/tests/interpreter.rs index 11e2113..3a45cd1 100644 --- a/tests/interpreter.rs +++ b/tests/interpreter.rs @@ -832,9 +832,12 @@ use './module_relative_import' fn module_native_import() { std::fs::write("/tmp/module_native_import.hana", "$y = 10").unwrap(); std::env::set_var("HANA_PATH", "/tmp"); - let prog = grammar::start(" + let prog = grammar::start( + " use 'module_native_import' - ").unwrap(); + ", + ) + .unwrap(); let mut c = compiler::Compiler::new(); c.modules_info.borrow_mut().files.push("/tmp/x".to_string()); for stmt in prog { diff --git a/tests/vm.rs b/tests/vm.rs index ded2898..0df9c1a 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -94,8 +94,8 @@ pub mod vm_tests { let mut c = Compiler::new(); c.cpushop(VmOpcode::OP_PUSHF64); c.cpushf64(1.5); - c.cpushop(VmOpcode::OP_PUSH64); - c.cpush64(15); + c.cpushop(VmOpcode::OP_PUSH32); + c.cpush32(15); c.cpushop(VmOpcode::OP_DIV); c.cpushop(VmOpcode::OP_HALT); let mut vm = c.into_vm();