diff --git a/Cargo.lock b/Cargo.lock index 5a68b01..f946e24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,6 +2210,8 @@ dependencies = [ name = "valuescript_compiler" version = "0.1.0" dependencies = [ + "num-bigint", + "num-traits", "queues", "serde", "serde_json", @@ -2232,6 +2234,8 @@ dependencies = [ name = "valuescript_vm" version = "0.1.0" dependencies = [ + "num-bigint", + "num-traits", "valuescript_common", ] diff --git a/inputs/passing/projEuler/p16.ts b/inputs/passing/projEuler/p16.ts index 0c874f1..7a96621 100644 --- a/inputs/passing/projEuler/p16.ts +++ b/inputs/passing/projEuler/p16.ts @@ -1,23 +1,7 @@ +// test_output! 1366 + import plus from "./helpers/plus.ts"; -import SillyBigInt from "./helpers/SillyBigInt.ts"; export default function main() { - let sbi = new SillyBigInt(1); - - for (let pow = 0; pow < 1000; pow++) { - sbi.add(sbi); - } - - return sbi.data.map(digitSum).reduce(plus); -} - -function digitSum(n: number) { - const nStr = `${n}`; - let sum = 0; - - for (let i = 0; i < nStr.length; i++) { - sum += +nStr[i]; - } - - return sum; + return Array.from(`${2n ** 1000n}`).map(Number).reduce(plus); } diff --git a/inputs/passing/projEuler/p16_sbi.ts b/inputs/passing/projEuler/p16_sbi.ts new file mode 100644 index 0000000..0c874f1 --- /dev/null +++ b/inputs/passing/projEuler/p16_sbi.ts @@ -0,0 +1,23 @@ +import plus from "./helpers/plus.ts"; +import SillyBigInt from "./helpers/SillyBigInt.ts"; + +export default function main() { + let sbi = new SillyBigInt(1); + + for (let pow = 0; pow < 1000; pow++) { + sbi.add(sbi); + } + + return sbi.data.map(digitSum).reduce(plus); +} + +function digitSum(n: number) { + const nStr = `${n}`; + let sum = 0; + + for (let i = 0; i < nStr.length; i++) { + sum += +nStr[i]; + } + + return sum; +} diff --git a/inputs/passing/projEuler/p25.ts b/inputs/passing/projEuler/p25.ts index 251fb5f..195d95b 100644 --- a/inputs/passing/projEuler/p25.ts +++ b/inputs/passing/projEuler/p25.ts @@ -1,14 +1,16 @@ -import SillyBigInt from "./helpers/SillyBigInt.ts"; +// test_output! 4782 export default function main() { - let fibLast = new SillyBigInt(1); - let fib = new SillyBigInt(1); + let fibLast = 1n; + let fib = 1n; let fibIndex = 2; - while (fib.toString().length < 1000) { - const tmp = fib; - fib.add(fibLast); - fibLast = tmp; + // TODO: Remove the temptation pull out this constant (optimization to eval + // known expressions). + const threshold = 10n ** 999n; + + while (fib < threshold) { + [fib, fibLast] = [fib + fibLast, fib]; fibIndex++; } diff --git a/inputs/passing/projEuler/p25_sbi.ts b/inputs/passing/projEuler/p25_sbi.ts new file mode 100644 index 0000000..251fb5f --- /dev/null +++ b/inputs/passing/projEuler/p25_sbi.ts @@ -0,0 +1,16 @@ +import SillyBigInt from "./helpers/SillyBigInt.ts"; + +export default function main() { + let fibLast = new SillyBigInt(1); + let fib = new SillyBigInt(1); + let fibIndex = 2; + + while (fib.toString().length < 1000) { + const tmp = fib; + fib.add(fibLast); + fibLast = tmp; + fibIndex++; + } + + return fibIndex; +} diff --git a/valuescript_compiler/Cargo.toml b/valuescript_compiler/Cargo.toml index dd19e1e..ec2ecfa 100644 --- a/valuescript_compiler/Cargo.toml +++ b/valuescript_compiler/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +num-bigint = "0.4" +num-traits = "0.2" serde = "1.0" serde_json = "1.0" swc_atoms = "0.2" diff --git a/valuescript_compiler/src/asm.rs b/valuescript_compiler/src/asm.rs index 581257b..3ffbd79 100644 --- a/valuescript_compiler/src/asm.rs +++ b/valuescript_compiler/src/asm.rs @@ -1,3 +1,5 @@ +use num_bigint::BigInt; + #[derive(Debug, Clone)] pub struct Module { pub export_default: Value, @@ -483,6 +485,7 @@ pub enum Value { Null, Bool(bool), Number(f64), + BigInt(BigInt), String(String), Array(Box), Object(Box), @@ -509,6 +512,7 @@ impl std::fmt::Display for Value { write!(f, "{}", value) } } + Value::BigInt(value) => write!(f, "{}n", value), Value::String(value) => write!( f, "{}", diff --git a/valuescript_compiler/src/assembler.rs b/valuescript_compiler/src/assembler.rs index 0998120..a31c165 100644 --- a/valuescript_compiler/src/assembler.rs +++ b/valuescript_compiler/src/assembler.rs @@ -4,6 +4,8 @@ use std::{ str::FromStr, }; +use num_bigint::{BigInt, Sign}; + use valuescript_common::BuiltinName; use crate::asm::{ @@ -231,6 +233,7 @@ impl Assembler { self.register(register); } Value::Number(number) => self.number(*number), + Value::BigInt(bigint) => self.bigint(bigint), Value::String(string) => self.string(string), Value::Bool(boolean) => match boolean { false => self.output.push(ValueType::False as u8), @@ -310,6 +313,21 @@ impl Assembler { } } + fn bigint(&mut self, value: &BigInt) { + self.output.push(ValueType::BigInt as u8); + + let (sign, mut bytes) = value.to_bytes_le(); + + self.output.push(match sign { + Sign::Minus => 0, + Sign::NoSign => 1, + Sign::Plus => 2, + }); + + self.varsize_uint(bytes.len()); + self.output.append(&mut bytes); + } + fn string(&mut self, value: &String) { self.output.push(ValueType::String as u8); self.varsize_uint(value.len()); @@ -377,6 +395,7 @@ enum ValueType { Builtin = 0x10, Class = 0x11, Lazy = 0x12, + BigInt = 0x13, } #[derive(Hash, PartialEq, Eq, Clone)] diff --git a/valuescript_compiler/src/assembly_parser.rs b/valuescript_compiler/src/assembly_parser.rs index d20b8c1..39818b3 100644 --- a/valuescript_compiler/src/assembly_parser.rs +++ b/valuescript_compiler/src/assembly_parser.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::str::FromStr; +use num_bigint::BigInt; use valuescript_common::BUILTIN_NAMES; use crate::asm::{ @@ -662,7 +663,7 @@ impl<'a> AssemblyParser<'a> { } Some('$') => Value::Builtin(self.assemble_builtin()), Some('[') => Value::Array(Box::new(self.assemble_array())), - Some('-' | '.' | '0'..='9') => Value::Number(self.assemble_number()), + Some('-' | '.' | '0'..='9') => self.assemble_number(), Some('"') => Value::String(self.parse_string_literal()), Some('{') => Value::Object(Box::new(self.assemble_object())), Some(ref_c) => { @@ -801,16 +802,16 @@ impl<'a> AssemblyParser<'a> { LabelRef { name } } - fn assemble_number(&mut self) -> f64 { + fn assemble_number(&mut self) -> Value { if self.parse_one_of(&["-Infinity", ""]) == "-Infinity" { - return f64::NEG_INFINITY; + return Value::Number(f64::NEG_INFINITY); } let mut num_string = "".to_string(); loop { match self.pos.peek() { - Some('-' | '.' | 'e' | '0'..='9') => { + Some('-' | '.' | 'e' | 'n' | '0'..='9') => { num_string.push(self.pos.next().unwrap()); } _ => { @@ -819,6 +820,23 @@ impl<'a> AssemblyParser<'a> { } } + if num_string.chars().last() == Some('n') { + num_string.pop(); + + match BigInt::parse_bytes(num_string.as_bytes(), 10) { + Some(bigint) => return Value::BigInt(bigint), + None => { + panic!( + "{}", + self.render_pos( + -(num_string.len() as isize + 1), + &format!("Expected valid number") + ) + ); + } + } + } + let value_result = f64::from_str(num_string.as_str()); if value_result.is_err() { @@ -831,7 +849,7 @@ impl<'a> AssemblyParser<'a> { ); } - value_result.unwrap() + Value::Number(value_result.unwrap()) } fn assemble_object(&mut self) -> Object { diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index cee4ab5..6dafaf7 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -1334,7 +1334,7 @@ impl<'a> ExpressionCompiler<'a> { Bool(bool_) => return Value::Bool(bool_.value), Null(_) => return Value::Null, Num(num) => return Value::Number(num.value), - BigInt(_) => ("_todo_bigint_literal", "BigInt literals"), + BigInt(bigint) => return Value::BigInt(bigint.value.clone()), Regex(_) => ("_todo_regex_literal", "Regex literals"), JSXText(_) => ("_todo_jsxtext_literal", "JSXText literals"), }; diff --git a/valuescript_compiler/src/link_module.rs b/valuescript_compiler/src/link_module.rs index 92902c8..2b9a56c 100644 --- a/valuescript_compiler/src/link_module.rs +++ b/valuescript_compiler/src/link_module.rs @@ -213,26 +213,20 @@ where use Value::*; match value { - Void => {} - Undefined => {} - Null => {} - Bool(_) => {} - Number(_) => {} - String(_) => {} + Void | Undefined | Null | Bool(_) | Number(_) | BigInt(_) | String(_) | Register(_) + | Builtin(_) => {} Array(array) => { self.array(owner, array); } Object(object) => { self.object(owner, object); } - Register(_) => {} Pointer(pointer) => { (self.visitor)(match owner { Some(owner) => PointerVisitation::Reference(owner, pointer), None => PointerVisitation::Export(pointer), }); } - Builtin(_) => {} } } diff --git a/valuescript_vm/Cargo.toml b/valuescript_vm/Cargo.toml index e034276..2c3f016 100644 --- a/valuescript_vm/Cargo.toml +++ b/valuescript_vm/Cargo.toml @@ -6,4 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +num-bigint = "0.4" +num-traits = "0.2" valuescript_common = { path = "../valuescript_common" } diff --git a/valuescript_vm/src/bigint_methods.rs b/valuescript_vm/src/bigint_methods.rs new file mode 100644 index 0000000..1f51e60 --- /dev/null +++ b/valuescript_vm/src/bigint_methods.rs @@ -0,0 +1,42 @@ +use std::rc::Rc; + +use num_bigint::BigInt; + +use crate::{ + native_function::NativeFunction, + todo_fn::TODO, + vs_value::{Val, ValTrait}, +}; + +pub fn op_sub_bigint(_bigint: &BigInt, subscript: &Val) -> Val { + match subscript.val_to_string().as_str() { + "toLocaleString" => Val::Static(&TODO), + "toString" => Val::Static(&TO_STRING), + "valueOf" => Val::Static(&VALUE_OF), + _ => Val::Undefined, + } +} + +static TO_STRING: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Val { + match this { + Val::BigInt(_) => match params.get(0) { + Some(_) => { + panic!("TODO: toString with radix"); + } + + None => Val::String(Rc::new(this.val_to_string())), + }, + _ => panic!("TODO: exceptions/bigint indirection"), + } + }, +}; + +static VALUE_OF: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Val { + match this { + Val::BigInt(bigint) => Val::BigInt(bigint.clone()), + _ => panic!("TODO: exceptions/bigint indirection"), + } + }, +}; diff --git a/valuescript_vm/src/builtins/array_builtin.rs b/valuescript_vm/src/builtins/array_builtin.rs index 13a0870..da8ed5c 100644 --- a/valuescript_vm/src/builtins/array_builtin.rs +++ b/valuescript_vm/src/builtins/array_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::{ native_function::NativeFunction, operations::op_sub, @@ -42,6 +44,9 @@ impl ValTrait for ArrayBuiltin { fn bind(&self, _params: Vec) -> Option { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -66,7 +71,7 @@ impl ValTrait for ArrayBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -111,7 +116,7 @@ static FROM: NativeFunction = NativeFunction { Val::Void | Val::Undefined | Val::Null => { panic!("TODO: Exceptions (TypeError: items is not iterable)") } - Val::Bool(..) | Val::Number(..) => Val::Array(Rc::new(VsArray::new())), + Val::Bool(..) | Val::Number(..) | Val::BigInt(..) => Val::Array(Rc::new(VsArray::new())), Val::Object(..) | Val::Function(..) | Val::Class(..) | Val::Static(..) | Val::Custom(..) => { let len = op_sub( first_param.clone(), diff --git a/valuescript_vm/src/builtins/boolean_builtin.rs b/valuescript_vm/src/builtins/boolean_builtin.rs index 18d627b..952fd8d 100644 --- a/valuescript_vm/src/builtins/boolean_builtin.rs +++ b/valuescript_vm/src/builtins/boolean_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::{ vs_array::VsArray, vs_class::VsClass, @@ -40,6 +42,9 @@ impl ValTrait for BooleanBuiltin { fn bind(&self, _params: Vec) -> Option { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -59,7 +64,7 @@ impl ValTrait for BooleanBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/debug_builtin.rs b/valuescript_vm/src/builtins/debug_builtin.rs index a30729f..dff6f8e 100644 --- a/valuescript_vm/src/builtins/debug_builtin.rs +++ b/valuescript_vm/src/builtins/debug_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::native_function::NativeFunction; use crate::vs_array::VsArray; use crate::vs_class::VsClass; @@ -40,6 +42,9 @@ impl ValTrait for DebugBuiltin { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -63,7 +68,7 @@ impl ValTrait for DebugBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/math_builtin.rs b/valuescript_vm/src/builtins/math_builtin.rs index 817cedd..a7a4402 100644 --- a/valuescript_vm/src/builtins/math_builtin.rs +++ b/valuescript_vm/src/builtins/math_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::native_function::NativeFunction; use crate::operations::to_u32; use crate::vs_array::VsArray; @@ -41,6 +43,9 @@ impl ValTrait for MathBuiltin { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -108,7 +113,7 @@ impl ValTrait for MathBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/number_builtin.rs b/valuescript_vm/src/builtins/number_builtin.rs index 1369e93..cb2e100 100644 --- a/valuescript_vm/src/builtins/number_builtin.rs +++ b/valuescript_vm/src/builtins/number_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::{ native_function::NativeFunction, vs_array::VsArray, @@ -41,6 +43,9 @@ impl ValTrait for NumberBuiltin { fn bind(&self, _params: Vec) -> Option { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -75,7 +80,7 @@ impl ValTrait for NumberBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/string_builtin.rs b/valuescript_vm/src/builtins/string_builtin.rs index 2c188d2..6b7e2dc 100644 --- a/valuescript_vm/src/builtins/string_builtin.rs +++ b/valuescript_vm/src/builtins/string_builtin.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use num_bigint::BigInt; + use crate::{ native_function::NativeFunction, vs_array::VsArray, @@ -41,6 +43,9 @@ impl ValTrait for StringBuiltin { fn bind(&self, _params: Vec) -> Option { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -68,7 +73,7 @@ impl ValTrait for StringBuiltin { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -89,7 +94,7 @@ static FROM_CODE_POINT: NativeFunction = NativeFunction { let char = match std::char::from_u32(code_point) { Some(c) => c, - None => panic!("Not implemented: exceptions (RangeError: Invalid code point)"), + None => panic!("TODO: Exceptions (RangeError: Invalid code point)"), }; result.push(char); diff --git a/valuescript_vm/src/bytecode_decoder.rs b/valuescript_vm/src/bytecode_decoder.rs index c898db9..547642f 100644 --- a/valuescript_vm/src/bytecode_decoder.rs +++ b/valuescript_vm/src/bytecode_decoder.rs @@ -1,6 +1,9 @@ use std::collections::BTreeMap; use std::rc::Rc; +use num_bigint::BigInt; +use num_bigint::Sign; + use crate::builtins::BUILTIN_VALS; use super::instruction::Instruction; @@ -37,6 +40,7 @@ pub enum BytecodeType { Register = 0x0e, Builtin = 0x10, Class = 0x11, + BigInt = 0x13, Unrecognized = 0xff, } @@ -62,6 +66,8 @@ impl BytecodeType { 0x10 => Builtin, 0x11 => Class, + 0x13 => BigInt, + _ => Unrecognized, }; } @@ -133,6 +139,7 @@ impl BytecodeDecoder { constructor: self.decode_val(registers), instance_prototype: self.decode_val(registers), })), + BytecodeType::BigInt => Val::BigInt(self.decode_bigint()), BytecodeType::Unrecognized => std::panic!("Unrecognized bytecode type at {}", self.pos - 1), }; } @@ -151,6 +158,22 @@ impl BytecodeDecoder { return f64::from_le_bytes(buf); } + pub fn decode_bigint(&mut self) -> BigInt { + let sign = match self.decode_byte() { + 0 => Sign::Minus, + 1 => Sign::NoSign, + 2 => Sign::Plus, + + _ => std::panic!("Invalid sign for bigint"), + }; + + let len = self.decode_varsize_uint(); + let bytes = &self.data[self.pos..self.pos + len]; + self.pos += len; + + return BigInt::from_bytes_le(sign, bytes); + } + pub fn decode_string(&mut self) -> String { let len = self.decode_varsize_uint(); let start = self.pos; // Start after decoding varsize diff --git a/valuescript_vm/src/lib.rs b/valuescript_vm/src/lib.rs index 77621d7..1715479 100644 --- a/valuescript_vm/src/lib.rs +++ b/valuescript_vm/src/lib.rs @@ -1,4 +1,5 @@ mod array_higher_functions; +mod bigint_methods; mod builtins; mod bytecode_decoder; mod bytecode_stack_frame; diff --git a/valuescript_vm/src/native_frame_function.rs b/valuescript_vm/src/native_frame_function.rs index 7630c59..c6c5a46 100644 --- a/valuescript_vm/src/native_frame_function.rs +++ b/valuescript_vm/src/native_frame_function.rs @@ -1,37 +1,59 @@ use std::rc::Rc; -use super::vs_value::{ - Val, - VsType, - ValTrait, - LoadFunctionResult, -}; -use super::vs_object::VsObject; +use num_bigint::BigInt; + +use super::stack_frame::StackFrame; use super::vs_array::VsArray; use super::vs_class::VsClass; -use super::stack_frame::StackFrame; +use super::vs_object::VsObject; +use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; pub struct NativeFrameFunction { pub make_frame: fn() -> StackFrame, } impl ValTrait for NativeFrameFunction { - fn typeof_(&self) -> VsType { VsType::Function } - fn val_to_string(&self) -> String { "function() { [native code] }".to_string() } - fn to_number(&self) -> f64 { f64::NAN } - fn to_index(&self) -> Option { None } - fn is_primitive(&self) -> bool { false } - fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) } - fn is_truthy(&self) -> bool { true } - fn is_nullish(&self) -> bool { false } + fn typeof_(&self) -> VsType { + VsType::Function + } + fn val_to_string(&self) -> String { + "function() { [native code] }".to_string() + } + fn to_number(&self) -> f64 { + f64::NAN + } + fn to_index(&self) -> Option { + None + } + fn is_primitive(&self) -> bool { + false + } + fn to_primitive(&self) -> Val { + Val::String(Rc::new(self.val_to_string())) + } + fn is_truthy(&self) -> bool { + true + } + fn is_nullish(&self) -> bool { + false + } fn bind(&self, _params: Vec) -> Option { std::panic!("Not implemented"); } - fn as_array_data(&self) -> Option> { None } - fn as_object_data(&self) -> Option> { None } - fn as_class_data(&self) -> Option> { None } + fn as_bigint_data(&self) -> Option { + None + } + fn as_array_data(&self) -> Option> { + None + } + fn as_object_data(&self) -> Option> { + None + } + fn as_class_data(&self) -> Option> { + None + } fn load_function(&self) -> LoadFunctionResult { LoadFunctionResult::StackFrame((self.make_frame)()) @@ -42,7 +64,7 @@ impl ValTrait for NativeFrameFunction { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/native_function.rs b/valuescript_vm/src/native_function.rs index fbc6391..bca33fd 100644 --- a/valuescript_vm/src/native_function.rs +++ b/valuescript_vm/src/native_function.rs @@ -1,36 +1,58 @@ use std::rc::Rc; -use super::vs_value::{ - Val, - VsType, - ValTrait, - LoadFunctionResult, -}; -use super::vs_object::VsObject; +use num_bigint::BigInt; + use super::vs_array::VsArray; use super::vs_class::VsClass; +use super::vs_object::VsObject; +use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; pub struct NativeFunction { pub fn_: fn(this: &mut Val, params: Vec) -> Val, } impl ValTrait for NativeFunction { - fn typeof_(&self) -> VsType { VsType::Function } - fn val_to_string(&self) -> String { "function() { [native code] }".to_string() } - fn to_number(&self) -> f64 { f64::NAN } - fn to_index(&self) -> Option { None } - fn is_primitive(&self) -> bool { false } - fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) } - fn is_truthy(&self) -> bool { true } - fn is_nullish(&self) -> bool { false } + fn typeof_(&self) -> VsType { + VsType::Function + } + fn val_to_string(&self) -> String { + "function() { [native code] }".to_string() + } + fn to_number(&self) -> f64 { + f64::NAN + } + fn to_index(&self) -> Option { + None + } + fn is_primitive(&self) -> bool { + false + } + fn to_primitive(&self) -> Val { + Val::String(Rc::new(self.val_to_string())) + } + fn is_truthy(&self) -> bool { + true + } + fn is_nullish(&self) -> bool { + false + } fn bind(&self, _params: Vec) -> Option { std::panic!("Not implemented"); } - fn as_array_data(&self) -> Option> { None } - fn as_object_data(&self) -> Option> { None } - fn as_class_data(&self) -> Option> { None } + fn as_bigint_data(&self) -> Option { + None + } + fn as_array_data(&self) -> Option> { + None + } + fn as_object_data(&self) -> Option> { + None + } + fn as_class_data(&self) -> Option> { + None + } fn load_function(&self) -> LoadFunctionResult { LoadFunctionResult::NativeFunction(self.fn_) @@ -41,7 +63,7 @@ impl ValTrait for NativeFunction { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/number_methods.rs b/valuescript_vm/src/number_methods.rs index d5020c2..5fde8d3 100644 --- a/valuescript_vm/src/number_methods.rs +++ b/valuescript_vm/src/number_methods.rs @@ -85,6 +85,7 @@ static TO_STRING: NativeFunction = NativeFunction { Some(_) => { panic!("TODO: toString with radix"); } + None => Val::String(Rc::new(this.val_to_string())), }, _ => panic!("TODO: exceptions/number indirection"), diff --git a/valuescript_vm/src/operations.rs b/valuescript_vm/src/operations.rs index 08a247a..9764729 100644 --- a/valuescript_vm/src/operations.rs +++ b/valuescript_vm/src/operations.rs @@ -1,5 +1,9 @@ use std::rc::Rc; +use num_bigint::Sign; +use num_traits::ToPrimitive; + +use crate::bigint_methods::op_sub_bigint; use crate::native_function::NativeFunction; use crate::number_methods::op_sub_number; use crate::string_methods::op_sub_string; @@ -12,86 +16,156 @@ pub fn op_plus(left: Val, right: Val) -> Val { let left_prim = left.to_primitive(); let right_prim = right.to_primitive(); - if left_prim.typeof_() == VsType::String || right_prim.typeof_() == VsType::String { + let left_type = left_prim.typeof_(); + let right_type = right_prim.typeof_(); + + if left_type == VsType::String || right_type == VsType::String { return Val::String(Rc::new( left_prim.val_to_string() + &right_prim.val_to_string(), )); } + if left_type == VsType::BigInt || right_type == VsType::BigInt { + if left_type != right_type { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)"); + } + + match (left_prim.as_bigint_data(), right_prim.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => { + return Val::BigInt(left_bigint + right_bigint); + } + _ => std::panic!("Not implemented"), + } + } + return Val::Number(left_prim.to_number() + right_prim.to_number()); } pub fn op_unary_plus(input: Val) -> Val { - return Val::Number(input.to_number()); + match input.as_bigint_data() { + Some(bigint) => Val::BigInt(bigint), + _ => Val::Number(input.to_number()), + } } pub fn op_minus(left: Val, right: Val) -> Val { - return Val::Number(left.to_number() - right.to_number()); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint - right_bigint), + (Some(_), None) | (None, Some(_)) => { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => Val::Number(left.to_number() - right.to_number()), + } } pub fn op_unary_minus(input: Val) -> Val { - return Val::Number(-input.to_number()); + match input.as_bigint_data() { + Some(bigint) => Val::BigInt(-bigint), + _ => Val::Number(-input.to_number()), + } } pub fn op_mul(left: Val, right: Val) -> Val { - return Val::Number(left.to_number() * right.to_number()); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint * right_bigint), + (Some(_), None) | (None, Some(_)) => { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => Val::Number(left.to_number() * right.to_number()), + } } pub fn op_div(left: Val, right: Val) -> Val { - return Val::Number(left.to_number() / right.to_number()); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint / right_bigint), + (Some(_), None) | (None, Some(_)) => { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => Val::Number(left.to_number() / right.to_number()), + } } pub fn op_mod(left: Val, right: Val) -> Val { - return Val::Number(left.to_number() % right.to_number()); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint % right_bigint), + (Some(_), None) | (None, Some(_)) => { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => Val::Number(left.to_number() % right.to_number()), + } } pub fn op_exp(left: Val, right: Val) -> Val { - return Val::Number(left.to_number().powf(right.to_number())); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => { + if right_bigint.sign() == Sign::Minus { + std::panic!("TODO: Exceptions (RangeError: Exponent must be non-negative)"); + } + + let exp = match right_bigint.to_u32() { + Some(exp) => exp, + None => { + std::panic!("TODO: Exceptions (RangeError: Exponent must be less than 2^32)") + } + }; + + Val::BigInt(left_bigint.pow(exp)) + } + (Some(_), None) | (None, Some(_)) => { + std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => Val::Number(left.to_number().powf(right.to_number())), + } } pub fn op_eq(left: Val, right: Val) -> Val { - if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() == right.to_number()); + Val::Bool(match (left, right) { + (Val::Undefined, Val::Undefined) => true, + (Val::Null, Val::Null) => true, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool == right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number == right_number, + (Val::String(left_string), Val::String(right_string)) => left_string == right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint == right_bigint, + _ => panic!("TODO"), + }) } pub fn op_ne(left: Val, right: Val) -> Val { - if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() != right.to_number()); + Val::Bool(match (left, right) { + (Val::Undefined, Val::Undefined) => false, + (Val::Null, Val::Null) => false, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool != right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number != right_number, + (Val::String(left_string), Val::String(right_string)) => left_string != right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint != right_bigint, + _ => panic!("TODO"), + }) } pub fn op_triple_eq_impl(left: Val, right: Val) -> bool { - let type_ = left.typeof_(); - - if right.typeof_() != type_ { - return false; - } - - match type_ { - VsType::Undefined | VsType::Null => { - return true; + match (&left, &right) { + (Val::Undefined, Val::Undefined) => true, + (Val::Null, Val::Null) => true, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool == right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number == right_number, + (Val::String(left_string), Val::String(right_string)) => left_string == right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint == right_bigint, + _ => { + if left.typeof_() != right.typeof_() { + false + } else { + panic!("TODO") + } } - _ => {} - }; - - return match (left, right) { - (Val::Number(lnum), Val::Number(rnum)) => lnum == rnum, - (Val::String(lstr), Val::String(rstr)) => lstr == rstr, - _ => std::panic!("Not implemented"), - }; + } } pub fn op_triple_eq(left: Val, right: Val) -> Val { - return Val::Bool(op_triple_eq_impl(left, right)); + Val::Bool(op_triple_eq_impl(left, right)) } pub fn op_triple_ne(left: Val, right: Val) -> Val { - return Val::Bool(!op_triple_eq_impl(left, right)); + Val::Bool(!op_triple_eq_impl(left, right)) } pub fn op_and(left: Val, right: Val) -> Val { @@ -107,42 +181,57 @@ pub fn op_not(input: Val) -> Val { } pub fn op_less(left: Val, right: Val) -> Val { - let left_type = left.typeof_(); - let right_type = right.typeof_(); - - if left_type == VsType::Undefined || right_type == VsType::Undefined { - return Val::Bool(false); - } - - if left_type != VsType::Number || right_type != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() < right.to_number()); + Val::Bool(match (&left, &right) { + (Val::Undefined, Val::Undefined) => false, + (Val::Null, Val::Null) => false, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool < right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number < right_number, + (Val::String(left_string), Val::String(right_string)) => left_string < right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint < right_bigint, + _ => { + if left.typeof_() == VsType::Undefined || right.typeof_() == VsType::Undefined { + false + } else { + panic!("TODO") + } + } + }) } pub fn op_less_eq(left: Val, right: Val) -> Val { - if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() <= right.to_number()); + Val::Bool(match (left, right) { + (Val::Undefined, Val::Undefined) => false, + (Val::Null, Val::Null) => true, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool <= right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number <= right_number, + (Val::String(left_string), Val::String(right_string)) => left_string <= right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint <= right_bigint, + _ => panic!("TODO"), + }) } pub fn op_greater(left: Val, right: Val) -> Val { - if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() > right.to_number()); + Val::Bool(match (left, right) { + (Val::Undefined, Val::Undefined) => false, + (Val::Null, Val::Null) => false, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool > right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number > right_number, + (Val::String(left_string), Val::String(right_string)) => left_string > right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint > right_bigint, + _ => panic!("TODO"), + }) } pub fn op_greater_eq(left: Val, right: Val) -> Val { - if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number { - std::panic!("Not implemented"); - } - - return Val::Bool(left.to_number() >= right.to_number()); + Val::Bool(match (left, right) { + (Val::Undefined, Val::Undefined) => false, + (Val::Null, Val::Null) => true, + (Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool >= right_bool, + (Val::Number(left_number), Val::Number(right_number)) => left_number >= right_number, + (Val::String(left_string), Val::String(right_string)) => left_string >= right_string, + (Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint >= right_bigint, + _ => panic!("TODO"), + }) } pub fn op_nullish_coalesce(left: Val, right: Val) -> Val { @@ -179,38 +268,97 @@ pub fn to_u32(x: f64) -> u32 { } pub fn op_bit_and(left: Val, right: Val) -> Val { - let res_i32 = to_i32(left.to_number()) & to_i32(right.to_number()); - return Val::Number(res_i32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint & right_bigint), + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_i32 = to_i32(left.to_number()) & to_i32(right.to_number()); + Val::Number(res_i32 as f64) + } + } } pub fn op_bit_or(left: Val, right: Val) -> Val { - let res_i32 = to_i32(left.to_number()) | to_i32(right.to_number()); - return Val::Number(res_i32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint | right_bigint), + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_i32 = to_i32(left.to_number()) | to_i32(right.to_number()); + Val::Number(res_i32 as f64) + } + } } pub fn op_bit_not(input: Val) -> Val { - let res_i32 = !to_i32(input.to_number()); - return Val::Number(res_i32 as f64); + match input.as_bigint_data() { + Some(bigint) => Val::BigInt(!bigint), + None => { + let res_i32 = !to_i32(input.to_number()); + Val::Number(res_i32 as f64) + } + } } pub fn op_bit_xor(left: Val, right: Val) -> Val { - let res_i32 = to_i32(left.to_number()) ^ to_i32(right.to_number()); - return Val::Number(res_i32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint ^ right_bigint), + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_i32 = to_i32(left.to_number()) ^ to_i32(right.to_number()); + Val::Number(res_i32 as f64) + } + } } pub fn op_left_shift(left: Val, right: Val) -> Val { - let res_i32 = to_i32(left.to_number()) << (to_u32(right.to_number()) & 0x1f); - return Val::Number(res_i32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => { + Val::BigInt(left_bigint << right_bigint.to_i64().expect("TODO")) + } + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_i32 = to_i32(left.to_number()) << (to_u32(right.to_number()) & 0x1f); + Val::Number(res_i32 as f64) + } + } } pub fn op_right_shift(left: Val, right: Val) -> Val { - let res_i32 = to_i32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); - return Val::Number(res_i32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(left_bigint), Some(right_bigint)) => { + Val::BigInt(left_bigint >> right_bigint.to_i64().expect("TODO")) + } + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_i32 = to_i32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); + Val::Number(res_i32 as f64) + } + } } pub fn op_right_shift_unsigned(left: Val, right: Val) -> Val { - let res_u32 = to_u32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); - return Val::Number(res_u32 as f64); + match (left.as_bigint_data(), right.as_bigint_data()) { + (Some(_), Some(_)) => { + panic!("TODO: Exceptions (TypeError: BigInts don't support unsigned right shift)") + } + (Some(_), None) | (None, Some(_)) => { + panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)") + } + _ => { + let res_u32 = to_u32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); + Val::Number(res_u32 as f64) + } + } } pub fn op_typeof(input: Val) -> Val { @@ -221,6 +369,7 @@ pub fn op_typeof(input: Val) -> Val { Null => "object".to_string(), Bool => "boolean".to_string(), Number => "number".to_string(), + BigInt => "bigint".to_string(), String => "string".to_string(), Array => "object".to_string(), Object => "object".to_string(), @@ -240,14 +389,15 @@ pub fn op_in(_left: Val, _right: Val) -> Val { pub fn op_sub(left: Val, right: Val) -> Val { return match left { Val::Void => std::panic!("Shouldn't happen"), - Val::Undefined => std::panic!("Not implemented: exceptions"), - Val::Null => std::panic!("Not implemented: exceptions"), + Val::Undefined => std::panic!("TODO: Exceptions"), + Val::Null => std::panic!("TODO: Exceptions"), Val::Bool(_) => match right.val_to_string().as_str() { "toString" => Val::Static(&BOOL_TO_STRING), "valueOf" => Val::Static(&BOOL_VALUE_OF), _ => Val::Undefined, }, Val::Number(number) => op_sub_number(number, &right), + Val::BigInt(bigint) => op_sub_bigint(&bigint, &right), Val::String(string_data) => op_sub_string(&string_data, &right), Val::Array(array_data) => { let right_index = match right.to_index() { @@ -287,11 +437,12 @@ pub fn op_sub(left: Val, right: Val) -> Val { pub fn op_submov(target: &mut Val, subscript: Val, value: Val) { match target { Val::Void => std::panic!("Shouldn't happen"), - Val::Undefined => std::panic!("Not implemented: exceptions"), - Val::Null => std::panic!("Not implemented: exceptions"), - Val::Bool(_) => std::panic!("Not implemented: exceptions"), - Val::Number(_) => std::panic!("Not implemented: exceptions"), - Val::String(_) => std::panic!("Not implemented: exceptions"), + Val::Undefined => std::panic!("TODO: Exceptions"), + Val::Null => std::panic!("TODO: Exceptions"), + Val::Bool(_) => std::panic!("TODO: Exceptions"), + Val::Number(_) => std::panic!("TODO: Exceptions"), + Val::BigInt(_) => std::panic!("TODO: Exceptions"), + Val::String(_) => std::panic!("TODO: Exceptions"), Val::Array(array_data) => { let subscript_index = match subscript.to_index() { None => std::panic!("Not implemented: non-uint array subscript assignment"), @@ -321,7 +472,7 @@ pub fn op_submov(target: &mut Val, subscript: Val, value: Val) { } Val::Function(_) => std::panic!("Not implemented: function subscript assignment"), Val::Class(_) => std::panic!("Not implemented: class subscript assignment"), - Val::Static(_) => std::panic!("Not implemented: exceptions"), + Val::Static(_) => std::panic!("TODO: Exceptions"), Val::Custom(_) => std::panic!("Not implemented"), } } @@ -330,7 +481,7 @@ static BOOL_TO_STRING: NativeFunction = NativeFunction { fn_: |this: &mut Val, _args: Vec| -> Val { match &this { Val::Bool(b) => Val::String(Rc::new(b.to_string())), - _ => std::panic!("Not implemented: exceptions/bool indirection"), + _ => std::panic!("TODO: Exceptions/bool indirection"), } }, }; @@ -339,7 +490,7 @@ static BOOL_VALUE_OF: NativeFunction = NativeFunction { fn_: |this: &mut Val, _args: Vec| -> Val { match &this { Val::Bool(b) => Val::Bool(*b), - _ => std::panic!("Not implemented: exceptions/bool indirection"), + _ => std::panic!("TODO: Exceptions/bool indirection"), } }, }; diff --git a/valuescript_vm/src/vs_array.rs b/valuescript_vm/src/vs_array.rs index 7a790af..160bf9c 100644 --- a/valuescript_vm/src/vs_array.rs +++ b/valuescript_vm/src/vs_array.rs @@ -1,6 +1,8 @@ use std::cmp::{max, min}; use std::rc::Rc; +use num_bigint::BigInt; + use crate::helpers::{to_wrapping_index, to_wrapping_index_clamped}; use super::array_higher_functions::array_every::EVERY; @@ -81,6 +83,9 @@ impl ValTrait for ArrayPrototype { None } + fn as_bigint_data(&self) -> Option { + None + } fn as_array_data(&self) -> Option> { None } @@ -135,7 +140,7 @@ impl ValTrait for ArrayPrototype { } fn submov(&mut self, _key: Val, _value: Val) { - std::panic!("Not implemented: exceptions"); + std::panic!("TODO: Exceptions"); } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -154,7 +159,7 @@ static AT: NativeFunction = NativeFunction { None => Val::Undefined, Some(i) => array_data.elements[i].clone(), }, - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), } }, }; @@ -180,7 +185,7 @@ static CONCAT: NativeFunction = NativeFunction { return Val::Array(Rc::new(new_array)); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -252,7 +257,7 @@ static COPY_WITHIN: NativeFunction = NativeFunction { return this.clone(); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -263,7 +268,7 @@ static ENTRIES: NativeFunction = NativeFunction { Val::Array(_array_data) => { std::panic!("Not implemented: ENTRIES"); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -293,7 +298,7 @@ static FILL: NativeFunction = NativeFunction { return this.clone(); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -323,7 +328,7 @@ static FLAT: NativeFunction = NativeFunction { return Val::Array(Rc::new(VsArray::from(new_elems))); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -342,7 +347,7 @@ static INCLUDES: NativeFunction = NativeFunction { return Val::Bool(false); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -361,7 +366,7 @@ static INDEX_OF: NativeFunction = NativeFunction { return Val::Number(-1_f64); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -401,7 +406,7 @@ static JOIN: NativeFunction = NativeFunction { return Val::String(Rc::new(res)); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -412,7 +417,7 @@ static KEYS: NativeFunction = NativeFunction { Val::Array(_array_data) => { std::panic!("Not implemented: KEYS"); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -431,7 +436,7 @@ static LAST_INDEX_OF: NativeFunction = NativeFunction { return Val::Number(-1_f64); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -455,7 +460,7 @@ static POP: NativeFunction = NativeFunction { _ => removed_el, }; } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -472,7 +477,7 @@ static PUSH: NativeFunction = NativeFunction { return Val::Number(array_data_mut.elements.len() as f64); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -499,7 +504,7 @@ static REVERSE: NativeFunction = NativeFunction { return this.clone(); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -516,7 +521,7 @@ static SHIFT: NativeFunction = NativeFunction { return array_data_mut.elements.remove(0); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -543,7 +548,7 @@ static SLICE: NativeFunction = NativeFunction { return Val::Array(Rc::new(VsArray::from(new_elems))); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -620,7 +625,7 @@ static SPLICE: NativeFunction = NativeFunction { return Val::Array(Rc::new(VsArray::from(deleted_elements))); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -631,7 +636,7 @@ static TO_LOCALE_STRING: NativeFunction = NativeFunction { Val::Array(_array_data) => { std::panic!("Not implemented: TO_LOCALE_STRING"); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -657,7 +662,7 @@ static UNSHIFT: NativeFunction = NativeFunction { return Val::Number(array_data_mut.elements.len() as f64); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; @@ -668,7 +673,7 @@ static VALUES: NativeFunction = NativeFunction { Val::Array(_array_data) => { std::panic!("Not implemented: VALUES"); } - _ => std::panic!("Not implemented: exceptions/array indirection"), + _ => std::panic!("TODO: Exceptions/array indirection"), }; }, }; diff --git a/valuescript_vm/src/vs_pointer.rs b/valuescript_vm/src/vs_pointer.rs index 3836925..3b699d9 100644 --- a/valuescript_vm/src/vs_pointer.rs +++ b/valuescript_vm/src/vs_pointer.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; use std::rc::Rc; +use num_bigint::BigInt; + use super::bytecode_decoder::{BytecodeDecoder, BytecodeType}; use super::vs_array::VsArray; use super::vs_class::VsClass; @@ -68,6 +70,7 @@ impl ValTrait for VsPointer { BytecodeType::Register => std::panic!("Invalid: pointer to register"), BytecodeType::Builtin => std::panic!("Invalid: pointer to builtin"), BytecodeType::Class => VsType::Class, + BytecodeType::BigInt => VsType::BigInt, BytecodeType::Unrecognized => std::panic!("Unrecognized bytecode type at {}", self.pos - 1), }; } @@ -90,6 +93,7 @@ impl ValTrait for VsPointer { VsType::Null => true, VsType::Bool => true, VsType::Number => true, + VsType::BigInt => true, VsType::String => true, VsType::Array => false, VsType::Object => false, @@ -114,6 +118,10 @@ impl ValTrait for VsPointer { return self.resolve().is_nullish(); } + fn as_bigint_data(&self) -> Option { + return self.resolve().as_bigint_data(); + } + fn as_array_data(&self) -> Option> { return self.resolve().as_array_data(); } diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index f049db6..c7628b8 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -1,6 +1,10 @@ use std::rc::Rc; use std::str::FromStr; +use num_bigint::BigInt; +use num_traits::cast::ToPrimitive; +use num_traits::Zero; + use super::operations::{op_sub, op_submov}; use super::stack_frame::StackFrame; use super::vs_array::VsArray; @@ -15,6 +19,7 @@ pub enum Val { Null, Bool(bool), Number(f64), + BigInt(BigInt), String(Rc), Array(Rc), Object(Rc), @@ -30,6 +35,7 @@ pub enum VsType { Null, Bool, Number, + BigInt, String, Array, Object, @@ -55,6 +61,7 @@ pub trait ValTrait { fn bind(&self, params: Vec) -> Option; + fn as_bigint_data(&self) -> Option; fn as_array_data(&self) -> Option>; fn as_object_data(&self) -> Option>; fn as_class_data(&self) -> Option>; @@ -78,6 +85,7 @@ impl ValTrait for Val { Null => VsType::Null, Bool(_) => VsType::Bool, Number(_) => VsType::Number, + BigInt(_) => VsType::BigInt, String(_) => VsType::String, Array(_) => VsType::Array, Object(_) => VsType::Object, @@ -107,6 +115,7 @@ impl ValTrait for Val { x.to_string() } } // TODO: Match js's number string format + BigInt(x) => x.to_string(), String(s) => s.to_string(), Array(vals) => { if vals.elements.len() == 0 { @@ -148,6 +157,7 @@ impl ValTrait for Val { Null => 0_f64, Bool(b) => *b as u8 as f64, Number(x) => *x, + BigInt(x) => x.to_f64().unwrap_or(f64::NAN), String(s) => f64::from_str(s).unwrap_or(f64::NAN), Array(vals) => match vals.elements.len() { 0 => 0_f64, @@ -171,6 +181,7 @@ impl ValTrait for Val { Null => None, Bool(_) => None, Number(x) => number_to_index(*x), + BigInt(b) => number_to_index(b.to_f64().unwrap_or(f64::NAN)), String(s) => match f64::from_str(s) { Ok(x) => number_to_index(x), Err(_) => None, @@ -193,6 +204,7 @@ impl ValTrait for Val { Null => true, Bool(_) => true, Number(_) => true, + BigInt(_) => true, String(_) => true, Array(_) => false, Object(_) => false, @@ -220,6 +232,7 @@ impl ValTrait for Val { Null => false, Bool(b) => *b, Number(x) => *x != 0_f64 && !x.is_nan(), + BigInt(x) => !x.is_zero(), String(s) => s.len() > 0, Array(_) => true, Object(_) => true, @@ -239,6 +252,7 @@ impl ValTrait for Val { Null => true, Bool(_) => false, Number(_) => false, + BigInt(_) => false, String(_) => false, Array(_) => false, Object(_) => false, @@ -260,6 +274,18 @@ impl ValTrait for Val { }; } + fn as_bigint_data(&self) -> Option { + use Val::*; + + return match self { + BigInt(b) => Some(b.clone()), + // TODO: Static? Others too? + Custom(val) => val.as_bigint_data(), + + _ => None, + }; + } + fn as_array_data(&self) -> Option> { use Val::*; @@ -325,6 +351,7 @@ impl ValTrait for Val { Val::Null => "null".into(), Val::Bool(_) => self.val_to_string(), Val::Number(_) => self.val_to_string(), + Val::BigInt(_) => self.val_to_string(), Val::String(str) => stringify_string(str), Val::Array(vals) => { if vals.elements.len() == 0 { @@ -386,6 +413,7 @@ impl std::fmt::Display for Val { Val::Null => write!(f, "\x1b[1mnull\x1b[22m"), Val::Bool(_) => write!(f, "\x1b[33m{}\x1b[39m", self.val_to_string()), Val::Number(_) => write!(f, "\x1b[33m{}\x1b[39m", self.val_to_string()), + Val::BigInt(_) => write!(f, "\x1b[33m{}n\x1b[39m", self.val_to_string()), Val::String(_) => write!(f, "\x1b[32m{}\x1b[39m", self.codify()), Val::Array(array) => { if array.elements.len() == 0 {