From 1c415e03c1ff6101763769f44da154ff8f3b93cb Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Thu, 29 Feb 2024 16:44:59 -0800 Subject: [PATCH] The Type type - CustomType now has a single function: return a reference to the Muse `Type` definition for this Rust type. - Type utilizes a vtable approach and offers helpers that can be used to do things like fall back to a member field (Exception uses this to fall back to the value implementations). This also provides a way to use a built-in vtable and create a new type with your own vtable instead, to allow creating a custom string/list/map type with new functions. - The default Vm now loads a core library that defines two types currently. Using a `Call` instruction on a Symbol now tries to resolve the symbol, and resolve() now accepts a fully qualified path: "$.path". E.g. map is "$.core.Map". Some of this is just barely flushed out, but it all passes testing (I think). --- muse-ui/src/lib.rs | 204 +++--- src/compiler.rs | 2 - src/exception.rs | 62 +- src/list.rs | 67 +- src/map.rs | 82 ++- src/regex.rs | 268 +++---- src/string.rs | 289 ++++---- src/value.rs | 1570 +++++++++++++++++++++++++++++++++++++----- src/vm.rs | 247 ++++--- src/vm/bitcode.rs | 16 +- src/vm/dispatched.rs | 129 +--- 11 files changed, 2055 insertions(+), 881 deletions(-) diff --git a/muse-ui/src/lib.rs b/muse-ui/src/lib.rs index 518311f..049571a 100644 --- a/muse-ui/src/lib.rs +++ b/muse-ui/src/lib.rs @@ -8,8 +8,8 @@ use cushy::widgets::{Expand, Slider}; use cushy::window::WindowHandle; use cushy::{App, Application, Open, PendingApp}; use muse::symbol::Symbol; -use muse::value::{CustomType, RustFunction, Value}; -use muse::vm::{Arity, Fault, Register, Vm}; +use muse::value::{CustomType, RustFunction, RustType, TypeRef, Value}; +use muse::vm::{Fault, Register, Vm}; pub fn install(vm: &mut Vm) { vm.declare( @@ -59,82 +59,90 @@ impl DerefMut for DynamicValue { } impl CustomType for DynamicValue { - fn call( - &self, - _vm: &mut Vm, - _this: &muse::value::AnyDynamic, - arity: Arity, - ) -> Result { - if arity == 0 { - Ok(self.0.get()) - } else { - Err(Fault::NotAFunction) - } - } + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("DynamicValue", |t| { + t.with_invoke(|_| { + |this, vm, name, arity| { + if name == Symbol::set_symbol() && arity == 1 { + let value = vm[Register(0)].take(); + if let Ok(mut contents) = this.0.try_lock() { + if contents.equals(Some(vm), &value)? { + Ok(value) + } else { + let old_value = std::mem::replace(&mut *contents, value); + Ok(old_value) + } + } else { + Ok(Value::Nil) + } + } else if name == &Symbol::from("slider_between") && arity == 2 { + let start = vm[Register(0)].take(); + let end = vm[Register(1)].take(); - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - if name == Symbol::set_symbol() && arity == 1 { - let value = vm[Register(0)].take(); - if let Ok(mut contents) = self.0.try_lock() { - if contents.equals(Some(vm), &value)? { - Ok(value) - } else { - let old_value = std::mem::replace(&mut *contents, value); - Ok(old_value) + match this.0.map_ref(numeric_kind) + | numeric_kind(&end) + | numeric_kind(&start) + { + NumericKind::Unknown => Err(Fault::UnsupportedOperation), + NumericKind::Float => Ok(Value::dynamic(MuseWidget::FloatSlider( + this.0 + .linked( + |v| v.as_f64().unwrap_or_default(), + |v| Value::Float(*v), + ) + .slider_between( + linked_dynamic_value( + &start, + |value| value.as_f64().unwrap_or_default(), + |float| Value::Float(*float), + ), + linked_dynamic_value( + &end, + |value| value.as_f64().unwrap_or_default(), + |float| Value::Float(*float), + ), + ), + ))), + NumericKind::Int => Ok(Value::dynamic(MuseWidget::IntSlider( + this.0 + .linked(|v| v.as_i64().unwrap_or_default(), |v| Value::Int(*v)) + .slider_between( + linked_dynamic_value( + &start, + |value| value.as_i64().unwrap_or_default(), + |int| Value::Int(*int), + ), + linked_dynamic_value( + &end, + |value| value.as_i64().unwrap_or_default(), + |int| Value::Int(*int), + ), + ), + ))), + } + } else { + Err(Fault::UnknownSymbol) + } } - } else { - Ok(Value::Nil) - } - } else if name == &Symbol::from("slider_between") && arity == 2 { - let start = vm[Register(0)].take(); - let end = vm[Register(1)].take(); - - match self.0.map_ref(numeric_kind) | numeric_kind(&end) | numeric_kind(&start) { - NumericKind::Unknown => Err(Fault::UnsupportedOperation), - NumericKind::Float => Ok(Value::dynamic(MuseWidget::FloatSlider( - self.0 - .linked(|v| v.as_f64().unwrap_or_default(), |v| Value::Float(*v)) - .slider_between( - linked_dynamic_value( - &start, - |value| value.as_f64().unwrap_or_default(), - |float| Value::Float(*float), - ), - linked_dynamic_value( - &end, - |value| value.as_f64().unwrap_or_default(), - |float| Value::Float(*float), - ), - ), - ))), - NumericKind::Int => Ok(Value::dynamic(MuseWidget::IntSlider( - self.0 - .linked(|v| v.as_i64().unwrap_or_default(), |v| Value::Int(*v)) - .slider_between( - linked_dynamic_value( - &start, - |value| value.as_i64().unwrap_or_default(), - |int| Value::Int(*int), - ), - linked_dynamic_value( - &end, - |value| value.as_i64().unwrap_or_default(), - |int| Value::Int(*int), - ), - ), - ))), - } - } else { - Err(Fault::UnknownSymbol) - } - } - - // All functions below are pass-throughs to the values contained. - - fn deep_clone(&self) -> Option { - self.0 - .map_ref(|value| value.deep_clone()) - .map(|value| muse::value::AnyDynamic::new(Self(Dynamic::new(value)))) + }) + .with_call(|_| { + |this, _vm, arity| { + if arity == 0 { + Ok(this.0.get()) + } else { + Err(Fault::NotAFunction) + } + } + }) + .with_deep_clone(|_| { + |this| { + this.0 + .map_ref(|value| value.deep_clone()) + .map(|value| muse::value::AnyDynamic::new(Self(Dynamic::new(value)))) + } + }) + }); + &TYPE } } @@ -231,25 +239,37 @@ impl MakeWidget for &'_ MuseWidget { } impl CustomType for MuseWidget { - fn invoke(&self, _vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - if name == &Symbol::from("open") && arity == 0 { - let widget = self.make_widget(); - Ok(widget - .open(&muse_app()?) - .unwrap() - .map(|handle| Value::dynamic(OpenWindow(handle))) - .unwrap_or_default()) - } else if name == &Symbol::from("expand") && arity == 0 { - Ok(Value::dynamic(MuseWidget::Expand( - self.make_widget().expand(), - ))) - } else { - Err(Fault::UnknownSymbol) - } + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("Widget", |t| { + t.with_invoke(|_| { + |this, _vm, name, arity| { + if name == &Symbol::from("open") && arity == 0 { + let widget = this.make_widget(); + Ok(widget + .open(&muse_app()?) + .unwrap() + .map(|handle| Value::dynamic(OpenWindow(handle))) + .unwrap_or_default()) + } else if name == &Symbol::from("expand") && arity == 0 { + Ok(Value::dynamic(MuseWidget::Expand( + this.make_widget().expand(), + ))) + } else { + Err(Fault::UnknownSymbol) + } + } + }) + }); + &TYPE } } #[derive(Debug)] pub struct OpenWindow(WindowHandle); -impl CustomType for OpenWindow {} +impl CustomType for OpenWindow { + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("Window", |t| t); + &TYPE + } +} diff --git a/src/compiler.rs b/src/compiler.rs index 69df9e2..36b9644 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1828,8 +1828,6 @@ pub enum UnaryKind { Copy, Resolve, Jump, - NewMap, - NewList, SetExceptionHandler, } diff --git a/src/exception.rs b/src/exception.rs index dbb79b1..24afb84 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,6 +1,5 @@ -use crate::symbol::Symbol; -use crate::value::{AnyDynamic, CustomType, Value}; -use crate::vm::{Fault, StackFrame, Vm}; +use crate::value::{AnyDynamic, CustomType, RustType, TypeRef, Value}; +use crate::vm::{StackFrame, Vm}; #[derive(Debug)] pub struct Exception { @@ -26,36 +25,31 @@ impl Exception { } impl CustomType for Exception { - fn eq(&self, vm: Option<&mut Vm>, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(self.value.equals(vm, &rhs.value)? && self.stack_trace == rhs.stack_trace) - } else { - Ok(false) - } - } - - fn matches(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.value.matches(vm, rhs) - } - - fn total_cmp(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.value.total_cmp(vm, rhs) - } - - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: crate::vm::Arity) -> Result { - self.value.invoke(vm, name, arity) - } - - fn to_string(&self, vm: &mut Vm) -> Result { - self.value.to_string(vm) - } - - fn deep_clone(&self) -> Option { - self.value.deep_clone().map(|value| { - AnyDynamic::new(Self { - value, - stack_trace: self.stack_trace.clone(), - }) - }) + fn muse_type(&self) -> &TypeRef { + static EXCEPTION_TYPE: RustType = RustType::new("Exception", |t| { + t.with_fallback(|this| this.value.clone()) + .with_eq(|_| { + |this, vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(this.value.equals(vm, &rhs.value)? + && this.stack_trace == rhs.stack_trace) + } else { + Ok(false) + } + } + }) + .with_matches(|_| |this, vm, rhs| this.value.matches(vm, rhs)) + .with_deep_clone(|_| { + |this| { + this.value.deep_clone().map(|value| { + AnyDynamic::new(Exception { + value, + stack_trace: this.stack_trace.clone(), + }) + }) + } + }) + }); + &EXCEPTION_TYPE } } diff --git a/src/list.rs b/src/list.rs index 69f7c67..85153ee 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,8 +1,47 @@ use std::sync::Mutex; use crate::symbol::Symbol; -use crate::value::{CustomType, StaticRustFunctionTable, Value}; -use crate::vm::{Arity, Fault, Register, Vm}; +use crate::value::{CustomType, RustType, StaticRustFunctionTable, TypeRef, Value}; +use crate::vm::{Fault, Register, Vm}; + +pub static LIST_TYPE: RustType = RustType::new("List", |t| { + t.with_construct(|_| { + |vm, arity| { + let list = List::new(); + for reg_index in 0..arity.0 { + let value = vm[Register(reg_index)].take(); + list.push(value)?; + } + Ok(list) + } + }) + .with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn(Symbol::len_symbol(), 0, |_vm: &mut Vm, this: &List| { + Value::try_from(this.0.lock().expect("poisoned").len()) + }) + .with_fn(Symbol::set_symbol(), 2, |vm, this| { + let index = vm[Register(0)].take(); + let value = vm[Register(1)].take(); + this.set(&index, value.clone())?; + Ok(value) + }) + .with_fn( + [Symbol::nth_symbol(), Symbol::get_symbol()], + 1, + |vm, this| { + let key = vm[Register(0)].take(); + this.get(&key) + }, + ) + }); + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) +}); #[derive(Debug)] pub struct List(Mutex>); @@ -68,27 +107,7 @@ impl FromIterator for List { } impl CustomType for List { - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = StaticRustFunctionTable::new(|table| { - table - .with_fn(Symbol::len_symbol(), 0, |_vm: &mut Vm, this: &List| { - Value::try_from(this.0.lock().expect("poisoned").len()) - }) - .with_fn(Symbol::set_symbol(), 2, |vm, this| { - let index = vm[Register(0)].take(); - let value = vm[Register(1)].take(); - this.set(&index, value.clone())?; - Ok(value) - }) - .with_fn( - [Symbol::nth_symbol(), Symbol::get_symbol()], - 1, - |vm, this| { - let key = vm[Register(0)].take(); - this.get(&key) - }, - ) - }); - FUNCTIONS.invoke(vm, name, arity, self) + fn muse_type(&self) -> &TypeRef { + &LIST_TYPE } } diff --git a/src/map.rs b/src/map.rs index 88faf13..5ffcfad 100644 --- a/src/map.rs +++ b/src/map.rs @@ -3,12 +3,59 @@ use std::sync::Mutex; use crate::list::List; use crate::symbol::Symbol; -use crate::value::{CustomType, StaticRustFunctionTable, Value}; -use crate::vm::{Arity, Fault, Register, Vm}; +use crate::value::{CustomType, RustType, StaticRustFunctionTable, TypeRef, Value}; +use crate::vm::{Fault, Register, Vm}; #[derive(Debug)] pub struct Map(Mutex>); +pub static MAP_TYPE: RustType = RustType::new("Map", |t| { + t.with_construct(|_| { + |vm, arity| { + let map = Map::new(); + for reg_index in (0..arity.0 * 2).step_by(2) { + let key = vm[Register(reg_index)].take(); + let value = vm[Register(reg_index + 1)].take(); + map.insert(vm, key, value)?; + } + Ok(map) + } + }) + .with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn(Symbol::set_symbol(), 1, |vm, this| { + let key = vm[Register(0)].take(); + let value = key.clone(); + this.insert(vm, key, value.clone())?; + Ok(value) + }) + .with_fn(Symbol::set_symbol(), 2, |vm, this| { + let key = vm[Register(0)].take(); + let value = vm[Register(1)].take(); + this.insert(vm, key, value.clone())?; + Ok(value) + }) + .with_fn(Symbol::get_symbol(), 1, |vm, this| { + let key = vm[Register(0)].take(); + this.get(vm, &key)?.ok_or(Fault::OutOfBounds) + }) + .with_fn(Symbol::nth_symbol(), 1, |vm, this| { + let index = vm[Register(0)].take(); + this.nth(&index) + }) + .with_fn(Symbol::len_symbol(), 0, |_vm, this| { + let contents = this.0.lock().expect("poisoned"); + Value::try_from(contents.len()) + }) + }); + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) +}); + impl Map { #[must_use] pub const fn new() -> Self { @@ -91,35 +138,8 @@ impl Map { } impl CustomType for Map { - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = StaticRustFunctionTable::new(|table| { - table - .with_fn(Symbol::set_symbol(), 1, |vm, this| { - let key = vm[Register(0)].take(); - let value = key.clone(); - this.insert(vm, key, value.clone())?; - Ok(value) - }) - .with_fn(Symbol::set_symbol(), 2, |vm, this| { - let key = vm[Register(0)].take(); - let value = vm[Register(1)].take(); - this.insert(vm, key, value.clone())?; - Ok(value) - }) - .with_fn(Symbol::get_symbol(), 1, |vm, this| { - let key = vm[Register(0)].take(); - this.get(vm, &key)?.ok_or(Fault::OutOfBounds) - }) - .with_fn(Symbol::nth_symbol(), 1, |vm, this| { - let index = vm[Register(0)].take(); - this.nth(&index) - }) - .with_fn(Symbol::len_symbol(), 0, |_vm, this| { - let contents = this.0.lock().expect("poisoned"); - Value::try_from(contents.len()) - }) - }); - FUNCTIONS.invoke(vm, name, arity, self) + fn muse_type(&self) -> &TypeRef { + &MAP_TYPE } } diff --git a/src/regex.rs b/src/regex.rs index 5004c4c..2c4d978 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -8,8 +8,8 @@ use regex::{Captures, Regex}; use crate::string::MuseString; use crate::symbol::Symbol; use crate::syntax::token::RegexLiteral; -use crate::value::{AnyDynamic, CustomType, StaticRustFunctionTable, Value, ValueHasher}; -use crate::vm::{Arity, Fault, Register, Vm}; +use crate::value::{AnyDynamic, CustomType, RustType, StaticRustFunctionTable, TypeRef, Value}; +use crate::vm::{Fault, Register, Vm}; #[derive(Debug, Clone)] pub struct MuseRegex { @@ -56,82 +56,83 @@ impl Deref for MuseRegex { } impl CustomType for MuseRegex { - fn hash(&self, _vm: &mut Vm, hasher: &mut ValueHasher) { - self.expr.as_str().hash(hasher); - } - - fn eq(&self, _vm: Option<&mut Vm>, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(self.expr.as_str() == rhs.expr.as_str()) - } else { - Ok(false) - } - } - - fn total_cmp(&self, _vm: &mut Vm, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(self.expr.as_str().cmp(rhs.expr.as_str())) - } else if rhs.as_dynamic().is_none() { - // Dynamics sort after primitive values - Ok(std::cmp::Ordering::Greater) - } else { - Err(Fault::UnsupportedOperation) - } - } - - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = - StaticRustFunctionTable::new(|table| { - table - .with_fn("total_captures", 0, |_vm: &mut Vm, this: &MuseRegex| { - Value::try_from(this.captures_len()) - }) - .with_fn( - "total_static_captures", - 0, - |_vm: &mut Vm, this: &MuseRegex| { - Ok(this - .static_captures_len() - .map(Value::try_from) - .transpose()? - .unwrap_or_default()) - }, - ) - .with_fn( - Symbol::captures_symbol(), - 1, - |vm: &mut Vm, this: &MuseRegex| { - let haystack = vm[Register(0)].take(); - haystack.map_str(vm, |_vm, haystack| { - this.captures(haystack) - .map(Value::dynamic) - .unwrap_or_default() - }) - }, - ) - }); - - FUNCTIONS.invoke(vm, name, arity, self) - } - - fn truthy(&self, _vm: &mut Vm) -> bool { - !self.expr.as_str().is_empty() - } - - fn to_string(&self, _vm: &mut Vm) -> Result { - Ok(Symbol::from(self.expr.as_str().to_string())) - } - - fn deep_clone(&self) -> Option { - Some(AnyDynamic::new(self.clone())) - } - - fn matches(&self, vm: &mut Vm, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(self.is_match(&rhs.lock())) - } else { - rhs.map_str(vm, |_vm, rhs| self.is_match(rhs)) - } + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("Regex", |t| { + t.with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn("total_captures", 0, |_vm: &mut Vm, this: &MuseRegex| { + Value::try_from(this.captures_len()) + }) + .with_fn( + "total_static_captures", + 0, + |_vm: &mut Vm, this: &MuseRegex| { + Ok(this + .static_captures_len() + .map(Value::try_from) + .transpose()? + .unwrap_or_default()) + }, + ) + .with_fn( + Symbol::captures_symbol(), + 1, + |vm: &mut Vm, this: &MuseRegex| { + let haystack = vm[Register(0)].take(); + haystack.map_str(vm, |_vm, haystack| { + this.captures(haystack) + .map(Value::dynamic) + .unwrap_or_default() + }) + }, + ) + }); + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) + .with_hash(|_| { + |this, _vm, hasher| { + this.expr.as_str().hash(hasher); + } + }) + .with_eq(|_| { + |this, _vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(this.expr.as_str() == rhs.expr.as_str()) + } else { + Ok(false) + } + } + }) + .with_total_cmp(|_| { + |this, _vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(this.expr.as_str().cmp(rhs.expr.as_str())) + } else if rhs.as_any_dynamic().is_none() { + // Dynamics sort after primitive values + Ok(std::cmp::Ordering::Greater) + } else { + Err(Fault::UnsupportedOperation) + } + } + }) + .with_truthy(|_| |this, _vm| !this.expr.as_str().is_empty()) + .with_to_string(|_| |this, _vm| Ok(Symbol::from(this.expr.as_str().to_string()))) + .with_clone() + .with_matches(|_| { + |this, vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(this.is_match(&rhs.lock())) + } else { + rhs.map_str(vm, |_vm, rhs| this.is_match(rhs)) + } + } + }) + }); + &TYPE } } @@ -179,38 +180,46 @@ impl MuseCaptures { } impl CustomType for MuseCaptures { - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = - StaticRustFunctionTable::new(|table| { - table - .with_fn( - Symbol::get_symbol(), - 1, - |vm: &mut Vm, this: &MuseCaptures| { - let index = vm[Register(0)].take(); - let index = if let Some(index) = index.as_usize() { - index - } else { - let name = index.to_string(vm)?; - let Some(index) = this.by_name.get(&name).copied() else { - return Ok(Value::Nil); - }; - index - }; - this.matches.get(index).cloned().ok_or(Fault::OutOfBounds) - }, - ) - .with_fn( - Symbol::nth_symbol(), - 1, - |vm: &mut Vm, this: &MuseCaptures| { - let index = vm[Register(0)].as_usize().ok_or(Fault::OutOfBounds)?; - this.matches.get(index).cloned().ok_or(Fault::OutOfBounds) - }, - ) - }); - - FUNCTIONS.invoke(vm, name, arity, self) + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("RegexCaptures", |t| { + t.with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn( + Symbol::get_symbol(), + 1, + |vm: &mut Vm, this: &MuseCaptures| { + let index = vm[Register(0)].take(); + let index = if let Some(index) = index.as_usize() { + index + } else { + let name = index.to_string(vm)?; + let Some(index) = this.by_name.get(&name).copied() + else { + return Ok(Value::Nil); + }; + index + }; + this.matches.get(index).cloned().ok_or(Fault::OutOfBounds) + }, + ) + .with_fn( + Symbol::nth_symbol(), + 1, + |vm: &mut Vm, this: &MuseCaptures| { + let index = + vm[Register(0)].as_usize().ok_or(Fault::OutOfBounds)?; + this.matches.get(index).cloned().ok_or(Fault::OutOfBounds) + }, + ) + }); + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) + }); + &TYPE } } @@ -221,24 +230,25 @@ pub struct MuseMatch { } impl CustomType for MuseMatch { - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = - StaticRustFunctionTable::new(|table| { - table - .with_fn("content", 0, |_vm: &mut Vm, this: &MuseMatch| { - Ok(Value::Dynamic(this.content.clone())) - }) - .with_fn("start", 0, |_vm: &mut Vm, this: &MuseMatch| { - Value::try_from(this.start) - }) - }); - - FUNCTIONS - .invoke(vm, name, arity, self) - .or_else(|_| self.content.invoke(vm, name, arity)) - } - - fn to_string(&self, vm: &mut Vm) -> Result { - self.content.to_string(vm) + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("RegexMatch", |t| { + t.with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn("content", 0, |_vm: &mut Vm, this: &MuseMatch| { + Ok(Value::Dynamic(this.content.clone())) + }) + .with_fn("start", 0, |_vm: &mut Vm, this: &MuseMatch| { + Value::try_from(this.start) + }) + }); + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) + .with_to_string(|_| |this, vm| this.content.to_string(vm)) + }); + &TYPE } } diff --git a/src/string.rs b/src/string.rs index 8c1f105..696a000 100644 --- a/src/string.rs +++ b/src/string.rs @@ -5,8 +5,8 @@ use std::sync::{Mutex, MutexGuard}; use crate::list::List; use crate::regex::MuseRegex; use crate::symbol::Symbol; -use crate::value::{AnyDynamic, CustomType, StaticRustFunctionTable, Value, ValueHasher}; -use crate::vm::{Arity, Fault, Register, Vm}; +use crate::value::{AnyDynamic, CustomType, RustType, StaticRustFunctionTable, TypeRef, Value}; +use crate::vm::{Fault, Register, Vm}; #[derive(Debug)] pub struct MuseString(Mutex); @@ -30,142 +30,159 @@ impl From<&'_ str> for MuseString { } impl CustomType for MuseString { - fn hash(&self, _vm: &mut Vm, hasher: &mut ValueHasher) { - self.0.lock().expect("poisoned").hash(hasher); - } - - fn eq(&self, _vm: Option<&mut Vm>, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(*self.0.lock().expect("poisoned") == *rhs.0.lock().expect("poisoned")) - } else { - Ok(false) - } - } - - fn total_cmp(&self, _vm: &mut Vm, rhs: &Value) -> Result { - if let Some(rhs) = rhs.as_downcast_ref::() { - Ok(self - .0 - .lock() - .expect("poisoned") - .cmp(&rhs.0.lock().expect("poisoned"))) - } else if rhs.as_dynamic().is_none() { - // Dynamics sort after primitive values - Ok(std::cmp::Ordering::Greater) - } else { - Err(Fault::UnsupportedOperation) - } - } - - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = - StaticRustFunctionTable::new(|table| { - table - .with_fn( - Symbol::len_symbol(), - 0, - |_vm: &mut Vm, this: &MuseString| { - Value::try_from(this.0.lock().expect("poisoned").len()) - }, - ) - .with_fn("split", 1, |vm: &mut Vm, this: &MuseString| { - let needle = vm[Register(0)].take(); - if let Some(needle) = needle.as_downcast_ref::() { - if std::ptr::eq(&this.0, &needle.0) { - Ok(Value::dynamic(List::from(vec![Value::dynamic( - MuseString::from(String::default()), - )]))) - } else { - let haystack = this.lock(); - let needle = needle.lock(); - Ok(Value::dynamic( - haystack - .split(&*needle) - .map(|segment| Value::dynamic(MuseString::from(segment))) - .collect::(), - )) - } - } else if let Some(needle) = needle.as_downcast_ref::() { - let haystack = this.lock(); - Ok(Value::dynamic( - needle - .split(&haystack) - .map(|segment| Value::dynamic(MuseString::from(segment))) - .collect::(), - )) + #[allow(clippy::too_many_lines)] + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("String", |t| { + t.with_hash(|_| { + |this, _vm, hasher| { + this.0.lock().expect("poisoned").hash(hasher); + } + }) + .with_eq(|_| { + |this, _vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(*this.0.lock().expect("poisoned") == *rhs.0.lock().expect("poisoned")) + } else { + Ok(false) + } + } + }) + .with_total_cmp(|_| { + |this, _vm, rhs| { + if let Some(rhs) = rhs.as_downcast_ref::() { + Ok(this + .0 + .lock() + .expect("poisoned") + .cmp(&rhs.0.lock().expect("poisoned"))) + } else if rhs.as_any_dynamic().is_none() { + // Dynamics sort after primitive values + Ok(std::cmp::Ordering::Greater) + } else { + Err(Fault::UnsupportedOperation) + } + } + }) + .with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn( + Symbol::len_symbol(), + 0, + |_vm: &mut Vm, this: &MuseString| { + Value::try_from(this.0.lock().expect("poisoned").len()) + }, + ) + .with_fn("split", 1, |vm: &mut Vm, this: &MuseString| { + let needle = vm[Register(0)].take(); + if let Some(needle) = needle.as_downcast_ref::() { + if std::ptr::eq(&this.0, &needle.0) { + Ok(Value::dynamic(List::from(vec![Value::dynamic( + MuseString::from(String::default()), + )]))) + } else { + let haystack = this.lock(); + let needle = needle.lock(); + Ok(Value::dynamic( + haystack + .split(&*needle) + .map(|segment| { + Value::dynamic(MuseString::from(segment)) + }) + .collect::(), + )) + } + } else if let Some(needle) = + needle.as_downcast_ref::() + { + let haystack = this.lock(); + Ok(Value::dynamic( + needle + .split(&haystack) + .map(|segment| { + Value::dynamic(MuseString::from(segment)) + }) + .collect::(), + )) + } else { + Err(Fault::ExpectedString) + } + }) + }); + + FUNCTIONS.invoke(vm, name, arity, &this) + } + }) + .with_add(|_| { + |this, vm, rhs| { + let lhs = this.0.lock().expect("poisoned"); + if let Some(rhs) = rhs.as_downcast_ref::() { + if std::ptr::eq(&this.0, &rhs.0) { + let mut repeated = String::with_capacity( + lhs.len().checked_mul(2).ok_or(Fault::OutOfMemory)?, + ); + repeated.push_str(&lhs); + repeated.push_str(&lhs); + Ok(Value::dynamic(MuseString::from(repeated))) } else { - Err(Fault::ExpectedString) + let rhs = rhs.0.lock().expect("poisoned"); + let mut combined = String::with_capacity(lhs.len() + rhs.len()); + combined.push_str(&lhs); + combined.push_str(&rhs); + Ok(Value::dynamic(MuseString::from(combined))) } - }) - }); - - FUNCTIONS.invoke(vm, name, arity, self) - } - - fn add(&self, vm: &mut Vm, rhs: &Value) -> Result { - let this = self.0.lock().expect("poisoned"); - if let Some(rhs) = rhs.as_downcast_ref::() { - if std::ptr::eq(&self.0, &rhs.0) { - let mut repeated = - String::with_capacity(this.len().checked_mul(2).ok_or(Fault::OutOfMemory)?); - repeated.push_str(&this); - repeated.push_str(&this); - Ok(Value::dynamic(MuseString::from(repeated))) - } else { - let rhs = rhs.0.lock().expect("poisoned"); - let mut combined = String::with_capacity(this.len() + rhs.len()); - combined.push_str(&this); - combined.push_str(&rhs); - Ok(Value::dynamic(MuseString::from(combined))) - } - } else { - let rhs = rhs.to_string(vm)?; - let mut combined = String::with_capacity(this.len() + rhs.len()); - combined.push_str(&this); - combined.push_str(&rhs); - Ok(Value::dynamic(MuseString::from(combined))) - } - } - - fn add_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - let this = self.0.lock().expect("poisoned"); - let lhs = lhs.to_string(vm)?; - let mut combined = String::with_capacity(this.len() + lhs.len()); - combined.push_str(&lhs); - combined.push_str(&this); - Ok(Value::dynamic(MuseString::from(combined))) - } - - fn mul(&self, _vm: &mut Vm, rhs: &Value) -> Result { - let Some(times) = rhs.as_usize() else { - return Err(Fault::ExpectedInteger); - }; - let this = self.0.lock().expect("poisoned"); - - Ok(Value::dynamic(MuseString::from(this.repeat(times)))) - } - - fn mul_right(&self, _vm: &mut Vm, lhs: &Value) -> Result { - let Some(times) = lhs.as_usize() else { - return Err(Fault::ExpectedInteger); - }; - let this = self.0.lock().expect("poisoned"); - - Ok(Value::dynamic(MuseString::from(this.repeat(times)))) - } - - fn truthy(&self, _vm: &mut Vm) -> bool { - !self.0.lock().expect("poisoned").is_empty() - } - - fn to_string(&self, _vm: &mut Vm) -> Result { - Ok(Symbol::from(&*self.0.lock().expect("poisoned"))) - } - - fn deep_clone(&self) -> Option { - Some(AnyDynamic::new(Self(Mutex::new( - self.0.lock().expect("poisoned").clone(), - )))) + } else { + let rhs = rhs.to_string(vm)?; + let mut combined = String::with_capacity(lhs.len() + rhs.len()); + combined.push_str(&lhs); + combined.push_str(&rhs); + Ok(Value::dynamic(MuseString::from(combined))) + } + } + }) + .with_add_right(|_| { + |this, vm, lhs| { + let lhs = lhs.to_string(vm)?; + let rhs = this.0.lock().expect("poisoned"); + let mut combined = String::with_capacity(rhs.len() + lhs.len()); + combined.push_str(&lhs); + combined.push_str(&rhs); + Ok(Value::dynamic(MuseString::from(combined))) + } + }) + .with_mul(|_| { + |this, _vm, rhs| { + let Some(times) = rhs.as_usize() else { + return Err(Fault::ExpectedInteger); + }; + let this = this.0.lock().expect("poisoned"); + + Ok(Value::dynamic(MuseString::from(this.repeat(times)))) + } + }) + .with_mul_right(|_| { + |this, _vm, lhs| { + let Some(times) = lhs.as_usize() else { + return Err(Fault::ExpectedInteger); + }; + let this = this.0.lock().expect("poisoned"); + + Ok(Value::dynamic(MuseString::from(this.repeat(times)))) + } + }) + .with_truthy(|_| |this, _vm| !this.0.lock().expect("poisoned").is_empty()) + .with_to_string(|_| |this, _vm| Ok(Symbol::from(&*this.0.lock().expect("poisoned")))) + .with_deep_clone(|_| { + |this| { + Some(AnyDynamic::new(Self(Mutex::new( + this.0.lock().expect("poisoned").clone(), + )))) + } + }) + }); + &TYPE } } diff --git a/src/value.rs b/src/value.rs index 132ab91..af59398 100644 --- a/src/value.rs +++ b/src/value.rs @@ -171,13 +171,24 @@ impl Value { } #[must_use] - pub fn as_dynamic(&self) -> Option<&AnyDynamic> { + pub fn as_any_dynamic(&self) -> Option<&AnyDynamic> { match self { Value::Dynamic(value) => Some(value), _ => None, } } + #[must_use] + pub fn as_dynamic(&self) -> Option> + where + T: DynamicValue, + { + match self { + Value::Dynamic(value) => value.as_type(), + _ => None, + } + } + #[must_use] pub fn as_downcast_ref(&self) -> Option<&T> where @@ -215,6 +226,7 @@ impl Value { pub fn call(&self, vm: &mut Vm, arity: impl Into) -> Result { match self { Value::Dynamic(dynamic) => dynamic.call(vm, arity), + Value::Symbol(name) => vm.resolve(name).and_then(|named| named.call(vm, arity)), Value::Nil => vm.recurse_current_function(arity.into()), _ => Err(Fault::NotAFunction), } @@ -235,7 +247,7 @@ impl Value { .clone(); self.add(vm, &rhs) } - (Value::Dynamic(dynamic), _) => dynamic.0.invoke(vm, name, arity.into()), + (Value::Dynamic(dynamic), _) => dynamic.invoke(vm, name, arity.into()), (Value::Nil, _) => Err(Fault::OperationOnNil), _ => Err(Fault::UnknownSymbol), } @@ -576,10 +588,7 @@ impl Value { } pub fn not(&self, vm: &mut Vm) -> Result { - match self { - Value::Dynamic(value) => value.not(vm), - _ => Ok(Value::Bool(!self.truthy(vm))), - } + Ok(Value::Bool(!self.truthy(vm))) } pub fn negate(&self, vm: &mut Vm) -> Result { @@ -1075,7 +1084,7 @@ impl AnyDynamic { T: DynamicValue, { if Arc::get_mut(&mut self.0).is_none() { - *self = (**self.0).deep_clone()?; + *self = self.deep_clone()?; } let dynamic = &mut *Arc::get_mut(&mut self.0).expect("always 1 ref"); @@ -1094,11 +1103,11 @@ impl AnyDynamic { #[must_use] pub fn deep_clone(&self) -> Option { - self.0.deep_clone() + (self.0.muse_type().vtable.deep_clone)(self) } pub fn call(&self, vm: &mut Vm, arity: impl Into) -> Result { - self.0.call(vm, self, arity.into()) + (self.0.muse_type().vtable.call)(self, vm, arity.into()) } pub fn invoke( @@ -1107,117 +1116,113 @@ impl AnyDynamic { symbol: &Symbol, arity: impl Into, ) -> Result { - self.0.invoke(vm, symbol, arity.into()) + (self.0.muse_type().vtable.invoke)(self, vm, symbol, arity.into()) } pub fn add(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.add(vm, rhs) + (self.0.muse_type().vtable.add)(self, vm, rhs) } pub fn add_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.add_right(vm, lhs) + (self.0.muse_type().vtable.add_right)(self, vm, lhs) } pub fn sub(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.sub(vm, rhs) + (self.0.muse_type().vtable.sub)(self, vm, rhs) } pub fn sub_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.sub_right(vm, lhs) + (self.0.muse_type().vtable.sub_right)(self, vm, lhs) } pub fn mul(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.mul(vm, rhs) + (self.0.muse_type().vtable.mul)(self, vm, rhs) } pub fn mul_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.mul_right(vm, lhs) + (self.0.muse_type().vtable.mul_right)(self, vm, lhs) } pub fn div(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.div(vm, rhs) + (self.0.muse_type().vtable.div)(self, vm, rhs) } pub fn div_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.div_right(vm, lhs) + (self.0.muse_type().vtable.div_right)(self, vm, lhs) } pub fn rem(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.rem(vm, rhs) + (self.0.muse_type().vtable.rem)(self, vm, rhs) } pub fn rem_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.rem_right(vm, lhs) + (self.0.muse_type().vtable.rem_right)(self, vm, lhs) } pub fn idiv(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.div(vm, rhs) + (self.0.muse_type().vtable.div)(self, vm, rhs) } pub fn idiv_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - self.0.idiv_right(vm, lhs) + (self.0.muse_type().vtable.idiv_right)(self, vm, lhs) } pub fn hash(&self, vm: &mut Vm, hasher: &mut ValueHasher) { - self.0.hash(vm, hasher); - } - - pub fn not(&self, vm: &mut Vm) -> Result { - self.0.not(vm) + (self.0.muse_type().vtable.hash)(self, vm, hasher); } pub fn bitwise_not(&self, vm: &mut Vm) -> Result { - self.0.bitwise_not(vm) + (self.0.muse_type().vtable.bitwise_not)(self, vm) } pub fn bitwise_and(&self, vm: &mut Vm, other: &Value) -> Result { - self.0.bitwise_and(vm, other) + (self.0.muse_type().vtable.bitwise_and)(self, vm, other) } pub fn bitwise_or(&self, vm: &mut Vm, other: &Value) -> Result { - self.0.bitwise_or(vm, other) + (self.0.muse_type().vtable.bitwise_or)(self, vm, other) } pub fn bitwise_xor(&self, vm: &mut Vm, other: &Value) -> Result { - self.0.bitwise_xor(vm, other) + (self.0.muse_type().vtable.bitwise_xor)(self, vm, other) } pub fn shift_left(&self, vm: &mut Vm, amount: &Value) -> Result { - self.0.shift_left(vm, amount) + (self.0.muse_type().vtable.shift_left)(self, vm, amount) } pub fn shift_right(&self, vm: &mut Vm, amount: &Value) -> Result { - self.0.shift_right(vm, amount) + (self.0.muse_type().vtable.shift_right)(self, vm, amount) } pub fn negate(&self, vm: &mut Vm) -> Result { - self.0.negate(vm) + (self.0.muse_type().vtable.negate)(self, vm) } pub fn to_string(&self, vm: &mut Vm) -> Result { - self.0.to_string(vm) + (self.0.muse_type().vtable.to_string)(self, vm) } pub fn truthy(&self, vm: &mut Vm) -> bool { - self.0.truthy(vm) + (self.0.muse_type().vtable.truthy)(self, vm) } pub fn eq(&self, vm: Option<&mut Vm>, rhs: &Value) -> Result { match rhs { Value::Dynamic(dynamic) if Arc::ptr_eq(&self.0, &dynamic.0) => Ok(true), - _ => self.0.eq(vm, rhs), + _ => (self.0.muse_type().vtable.eq)(self, vm, rhs), } } pub fn matches(&self, vm: &mut Vm, rhs: &Value) -> Result { match rhs { Value::Dynamic(dynamic) if Arc::ptr_eq(&self.0, &dynamic.0) => Ok(true), - _ => self.0.matches(vm, rhs), + _ => (self.0.muse_type().vtable.matches)(self, vm, rhs), } } pub fn cmp(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.0.total_cmp(vm, rhs) + (self.0.muse_type().vtable.total_cmp)(self, vm, rhs) } } @@ -1312,162 +1317,1316 @@ where } } -pub trait CustomType: Send + Sync + Debug + 'static { - #[allow(unused_variables)] - fn call(&self, vm: &mut Vm, this: &AnyDynamic, arity: Arity) -> Result { - Err(Fault::NotAFunction) +pub struct StaticType(OnceLock, fn() -> Type); + +impl StaticType { + pub const fn new(init: fn() -> Type) -> Self { + Self(OnceLock::new(), init) } +} - #[allow(unused_variables)] - fn hash(&self, vm: &mut Vm, hasher: &mut ValueHasher) { - (self as *const Self).hash(hasher); +impl Deref for StaticType { + type Target = TypeRef; + + fn deref(&self) -> &Self::Target { + self.0.get_or_init(|| self.1().seal()) } +} + +pub struct RustType( + OnceLock, + &'static str, + fn(TypedTypeBuilder) -> TypedTypeBuilder, +); - #[allow(unused_variables)] - fn not(&self, vm: &mut Vm) -> Result { - Err(Fault::UnsupportedOperation) +impl RustType { + pub const fn new( + name: &'static str, + init: fn(TypedTypeBuilder) -> TypedTypeBuilder, + ) -> Self { + Self(OnceLock::new(), name, init) } +} - #[allow(unused_variables)] - fn and(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) +impl Deref for RustType +where + T: CustomType, +{ + type Target = TypeRef; + + fn deref(&self) -> &Self::Target { + self.0 + .get_or_init(|| self.2(TypedTypeBuilder::new(self.1)).seal()) } +} - #[allow(unused_variables)] - fn bitwise_not(&self, vm: &mut Vm) -> Result { - Err(Fault::UnsupportedOperation) +// #[derive(Debug)] +pub struct Type { + pub name: Symbol, + pub vtable: TypeVtable, +} + +impl Type { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + vtable: TypeVtable::default(), + } } - #[allow(unused_variables)] - fn bitwise_and(&self, vm: &mut Vm, other: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_construct(mut self, func: impl FnOnce(ConstructFn) -> Func) -> Self + where + Func: Fn(&mut Vm, Arity) -> Result + Send + Sync + 'static, + { + self.vtable.construct = Box::new(func(self.vtable.construct)); + self } - #[allow(unused_variables)] - fn bitwise_or(&self, vm: &mut Vm, other: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_call(mut self, func: impl FnOnce(CallFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, Arity) -> Result + Send + Sync + 'static, + { + self.vtable.call = Box::new(func(self.vtable.call)); + self } - #[allow(unused_variables)] - fn bitwise_xor(&self, vm: &mut Vm, other: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_invoke(mut self, func: impl FnOnce(InvokeFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Symbol, Arity) -> Result + + Send + + Sync + + 'static, + { + self.vtable.invoke = Box::new(func(self.vtable.invoke)); + self } - #[allow(unused_variables)] - fn shift_left(&self, vm: &mut Vm, amount: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_hash(mut self, func: impl FnOnce(HashFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &mut ValueHasher) + Send + Sync + 'static, + { + self.vtable.hash = Box::new(func(self.vtable.hash)); + self } - #[allow(unused_variables)] - fn shift_right(&self, vm: &mut Vm, amount: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_bitwise_not(mut self, func: impl FnOnce(UnaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm) -> Result + Send + Sync + 'static, + { + self.vtable.bitwise_not = Box::new(func(self.vtable.bitwise_not)); + self } - #[allow(unused_variables)] - fn negate(&self, vm: &mut Vm) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_bitwise_and(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.bitwise_and = Box::new(func(self.vtable.bitwise_and)); + self } - #[allow(unused_variables)] - fn eq(&self, vm: Option<&mut Vm>, rhs: &Value) -> Result { - Ok(false) + #[must_use] + pub fn with_bitwise_or(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.bitwise_or = Box::new(func(self.vtable.bitwise_or)); + self } - #[allow(unused_variables)] - fn matches(&self, vm: &mut Vm, rhs: &Value) -> Result { - self.eq(Some(vm), rhs) + #[must_use] + pub fn with_bitwise_xor(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.bitwise_xor = Box::new(func(self.vtable.bitwise_xor)); + self } - #[allow(unused_variables)] - fn total_cmp(&self, vm: &mut Vm, rhs: &Value) -> Result { - if rhs.as_dynamic().is_none() { - // Dynamics sort after primitive values - Ok(Ordering::Greater) - } else { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_shift_left(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.shift_left = Box::new(func(self.vtable.shift_left)); + self + } + + #[must_use] + pub fn with_shift_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.shift_right = Box::new(func(self.vtable.shift_right)); + self + } + + #[must_use] + pub fn with_negate(mut self, func: impl FnOnce(UnaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm) -> Result + Send + Sync + 'static, + { + self.vtable.negate = Box::new(func(self.vtable.negate)); + self + } + + #[must_use] + pub fn with_eq(mut self, func: impl FnOnce(EqFn) -> Func) -> Self + where + Func: + Fn(&AnyDynamic, Option<&mut Vm>, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.vtable.eq); + self.vtable.eq = Box::new(move |this, vm, rhs| func(this, vm, rhs)); + self + } + + #[must_use] + pub fn with_matches(mut self, func: impl FnOnce(MatchesFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.vtable.matches); + self.vtable.matches = Box::new(move |this, vm, rhs| func(this, vm, rhs)); + self + } + + #[must_use] + pub fn with_total_cmp(mut self, func: impl FnOnce(TotalCmpFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.vtable.total_cmp); + self.vtable.total_cmp = Box::new(move |this, vm, rhs| func(this, vm, rhs)); + self + } + + #[must_use] + pub fn with_add(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.add = Box::new(func(self.vtable.add)); + self + } + + #[must_use] + pub fn with_add_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.add_right = Box::new(func(self.vtable.add_right)); + self + } + + #[must_use] + pub fn with_sub(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.sub = Box::new(func(self.vtable.sub)); + self + } + + #[must_use] + pub fn with_sub_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.sub_right = Box::new(func(self.vtable.sub_right)); + self + } + + #[must_use] + pub fn with_mul(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.mul = Box::new(func(self.vtable.mul)); + self + } + + #[must_use] + pub fn with_mul_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.mul_right = Box::new(func(self.vtable.mul_right)); + self + } + + #[must_use] + pub fn with_div(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.div = Box::new(func(self.vtable.div)); + self + } + + #[must_use] + pub fn with_div_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.div_right = Box::new(func(self.vtable.div_right)); + self + } + + #[must_use] + pub fn with_idiv(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.idiv = Box::new(func(self.vtable.idiv)); + self + } + + #[must_use] + pub fn with_idiv_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.idiv_right = Box::new(func(self.vtable.idiv_right)); + self + } + + #[must_use] + pub fn with_rem(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.rem = Box::new(func(self.vtable.rem)); + self + } + + #[must_use] + pub fn with_rem_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + self.vtable.rem_right = Box::new(func(self.vtable.rem_right)); + self + } + + #[must_use] + pub fn with_truthy(mut self, func: impl FnOnce(TruthyFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm) -> bool + Send + Sync + 'static, + { + self.vtable.truthy = Box::new(func(self.vtable.truthy)); + self + } + + #[must_use] + pub fn with_to_string(mut self, func: impl FnOnce(ToStringFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic, &mut Vm) -> Result + Send + Sync + 'static, + { + self.vtable.to_string = Box::new(func(self.vtable.to_string)); + self + } + + #[must_use] + pub fn with_deep_clone(mut self, func: impl FnOnce(DeepCloneFn) -> Func) -> Self + where + Func: Fn(&AnyDynamic) -> Option + Send + Sync + Send + Sync + 'static, + { + self.vtable.deep_clone = Box::new(func(self.vtable.deep_clone)); + self + } + + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn with_fallback(mut self, mapping: Mapping) -> Self + where + Mapping: Fn(&AnyDynamic) -> Value + Send + Sync + Clone + 'static, + { + let mapping = Arc::new(mapping); + // Entries that aren't mutable are not fallible or do not make sense to + // have a fallback + let TypeVtable { + construct, + mut call, + hash, + mut bitwise_not, + mut bitwise_and, + mut bitwise_or, + mut bitwise_xor, + mut shift_left, + mut shift_right, + mut negate, + mut eq, + mut matches, + mut total_cmp, + mut invoke, + mut add, + mut add_right, + mut sub, + mut sub_right, + mut mul, + mut mul_right, + mut div, + mut div_right, + mut idiv, + mut idiv_right, + mut rem, + mut rem_right, + truthy, + mut to_string, + mut deep_clone, + } = self.vtable; + + call = Box::new({ + let mapping = mapping.clone(); + move |this, vm, arity| match call(this, vm, arity) { + Ok(value) => Ok(value), + Err(Fault::NotAFunction | Fault::UnsupportedOperation) => { + mapping(this).call(vm, arity) + } + Err(other) => Err(other), + } + }); + + bitwise_not = Box::new({ + let mapping = mapping.clone(); + move |this, vm| match bitwise_not(this, vm) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).bitwise_not(vm) + } + Err(other) => Err(other), + } + }); + + bitwise_and = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match bitwise_and(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).bitwise_and(vm, rhs) + } + Err(other) => Err(other), + } + }); + + bitwise_or = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match bitwise_or(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).bitwise_or(vm, rhs) + } + Err(other) => Err(other), + } + }); + + bitwise_xor = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match bitwise_xor(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).bitwise_xor(vm, rhs) + } + Err(other) => Err(other), + } + }); + + shift_left = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match shift_left(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).shift_left(vm, rhs) + } + Err(other) => Err(other), + } + }); + + shift_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match shift_right(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).shift_right(vm, rhs) + } + Err(other) => Err(other), + } + }); + + negate = Box::new({ + let mapping = mapping.clone(); + move |this, vm| match negate(this, vm) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => mapping(this).negate(vm), + Err(other) => Err(other), + } + }); + + eq = Box::new({ + let mapping = mapping.clone(); + move |this, mut vm, rhs| match eq(this, vm.as_deref_mut(), rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).equals(vm, rhs) + } + Err(other) => Err(other), + } + }); + + matches = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match matches(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).matches(vm, rhs) + } + Err(other) => Err(other), + } + }); + + total_cmp = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match total_cmp(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).total_cmp(vm, rhs) + } + Err(other) => Err(other), + } + }); + + invoke = Box::new({ + let mapping = mapping.clone(); + move |this, vm, name, arity| match invoke(this, vm, name, arity) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).invoke(vm, name, arity) + } + Err(other) => Err(other), + } + }); + + add = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match add(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).add(vm, rhs) + } + Err(other) => Err(other), + } + }); + + add_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match add_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.add(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + sub = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match sub(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).sub(vm, rhs) + } + Err(other) => Err(other), + } + }); + + sub_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match sub_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.sub(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + mul = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match mul(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).mul(vm, rhs) + } + Err(other) => Err(other), + } + }); + + mul_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match mul_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.mul(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + div = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match div(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).div(vm, rhs) + } + Err(other) => Err(other), + } + }); + + div_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match div_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.div(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + idiv = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match idiv(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).idiv(vm, rhs) + } + Err(other) => Err(other), + } + }); + + idiv_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match idiv_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.idiv(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + rem = Box::new({ + let mapping = mapping.clone(); + move |this, vm, rhs| match rem(this, vm, rhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).rem(vm, rhs) + } + Err(other) => Err(other), + } + }); + + rem_right = Box::new({ + let mapping = mapping.clone(); + move |this, vm, lhs| match rem_right(this, vm, lhs) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + lhs.rem(vm, &mapping(this)) + } + Err(other) => Err(other), + } + }); + + to_string = Box::new({ + let mapping = mapping.clone(); + move |this, vm| match to_string(this, vm) { + Ok(value) => Ok(value), + Err(Fault::UnknownSymbol | Fault::UnsupportedOperation) => { + mapping(this).to_string(vm) + } + Err(other) => Err(other), + } + }); + + deep_clone = Box::new({ + let mapping = mapping.clone(); + move |this| match deep_clone(this) { + Some(value) => Some(value), + None => mapping(this) + .deep_clone() + .and_then(|value| value.as_any_dynamic().cloned()), + } + }); + + self.vtable = TypeVtable { + construct, + call, + invoke, + hash, + bitwise_not, + bitwise_and, + bitwise_or, + bitwise_xor, + shift_left, + shift_right, + negate, + eq, + matches, + total_cmp, + add, + add_right, + sub, + sub_right, + mul, + mul_right, + div, + div_right, + idiv, + idiv_right, + rem, + rem_right, + truthy, + to_string, + deep_clone, + }; + self + } + + #[must_use] + pub fn seal(self) -> TypeRef { + TypeRef::new(self) + } +} + +impl Debug for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Type") + .field("name", &self.name) + .finish_non_exhaustive() + } +} + +pub struct TypedTypeBuilder { + t: Type, + _t: PhantomData, +} + +impl TypedTypeBuilder +where + T: CustomType, +{ + fn new(name: &'static str) -> Self { + Self { + t: Type::new(name), + _t: PhantomData, } } - #[allow(unused_variables)] - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - Err(Fault::UnknownSymbol) + #[must_use] + pub fn with_construct(mut self, func: impl FnOnce(ConstructFn) -> Func) -> Self + where + Func: Fn(&mut Vm, Arity) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.construct); + self.t.vtable.construct = Box::new(move |vm, arity| func(vm, arity).map(Value::dynamic)); + self + } + + #[must_use] + pub fn with_call(mut self, func: impl FnOnce(CallFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, Arity) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.call); + self.t.vtable.call = Box::new(move |this, vm, arity| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, arity) + }); + self + } + + #[must_use] + pub fn with_invoke(mut self, func: impl FnOnce(InvokeFn) -> Func) -> Self + where + Func: + Fn(Dynamic, &mut Vm, &Symbol, Arity) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.invoke); + self.t.vtable.invoke = Box::new(move |this, vm, name, arity| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, name, arity) + }); + self + } + + #[must_use] + pub fn with_hash(mut self, func: impl FnOnce(HashFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &mut ValueHasher) + Send + Sync + Send + Sync + 'static, + { + let func = func(self.t.vtable.hash); + self.t.vtable.hash = Box::new(move |this, vm, hasher| { + let Some(this) = this.as_type::() else { + return; + }; + func(this, vm, hasher); + }); + self + } + + #[must_use] + pub fn with_bitwise_not(mut self, func: impl FnOnce(UnaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm) -> Result + Send + Sync + Send + Sync + 'static, + { + let func = func(self.t.vtable.bitwise_not); + self.t.vtable.bitwise_not = Box::new(move |this, vm| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm) + }); + self + } + + #[must_use] + pub fn with_bitwise_and(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.bitwise_and); + self.t.vtable.bitwise_and = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_bitwise_or(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.bitwise_or); + self.t.vtable.bitwise_or = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_bitwise_xor(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.bitwise_xor); + self.t.vtable.bitwise_xor = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_shift_left(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.shift_left); + self.t.vtable.shift_left = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_shift_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.shift_right); + self.t.vtable.shift_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_negate(mut self, func: impl FnOnce(UnaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm) -> Result + Send + Sync + Send + Sync + 'static, + { + let func = func(self.t.vtable.negate); + self.t.vtable.negate = Box::new(move |this, vm| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm) + }); + self + } + + #[must_use] + pub fn with_eq(mut self, func: impl FnOnce(EqFn) -> Func) -> Self + where + Func: + Fn(Dynamic, Option<&mut Vm>, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.eq); + self.t.vtable.eq = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_matches(mut self, func: impl FnOnce(MatchesFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.matches); + self.t.vtable.matches = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_total_cmp(mut self, func: impl FnOnce(TotalCmpFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.total_cmp); + self.t.vtable.total_cmp = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn add(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_add(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.add); + self.t.vtable.add = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn add_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_add_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.add_right); + self.t.vtable.add_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn sub(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_sub(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.sub); + self.t.vtable.sub = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn sub_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_sub_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.sub_right); + self.t.vtable.sub_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn mul(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_mul(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.mul); + self.t.vtable.mul = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn mul_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_mul_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.mul_right); + self.t.vtable.mul_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_div(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.div); + self.t.vtable.div = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_div_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.div_right); + self.t.vtable.div_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_idiv(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.idiv); + self.t.vtable.idiv = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_idiv_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.idiv_right); + self.t.vtable.idiv_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self + } + + #[must_use] + pub fn with_rem(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.rem); + self.t.vtable.rem = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn div(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_rem_right(mut self, func: impl FnOnce(BinaryFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm, &Value) -> Result + + Send + + Sync + + Send + + Sync + + 'static, + { + let func = func(self.t.vtable.rem_right); + self.t.vtable.rem_right = Box::new(move |this, vm, rhs| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm, rhs) + }); + self } - #[allow(unused_variables)] - fn div_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_truthy(mut self, func: impl FnOnce(TruthyFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm) -> bool + Send + Sync + 'static, + { + let func = func(self.t.vtable.truthy); + self.t.vtable.truthy = Box::new(move |this, vm| { + let Some(this) = this.as_type::() else { + return true; + }; + func(this, vm) + }); + self } - #[allow(unused_variables)] - fn idiv(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_to_string(mut self, func: impl FnOnce(ToStringFn) -> Func) -> Self + where + Func: Fn(Dynamic, &mut Vm) -> Result + Send + Sync + 'static, + { + let func = func(self.t.vtable.to_string); + self.t.vtable.to_string = Box::new(move |this, vm| { + let this = this.as_type::().ok_or(Fault::UnsupportedOperation)?; + func(this, vm) + }); + self } - #[allow(unused_variables)] - fn idiv_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_deep_clone(mut self, func: impl FnOnce(DeepCloneFn) -> Func) -> Self + where + Func: Fn(Dynamic) -> Option + Send + Sync + Send + Sync + 'static, + { + let func = func(self.t.vtable.deep_clone); + self.t.vtable.deep_clone = Box::new(move |this| { + let this = this.as_type::()?; + func(this) + }); + self } - #[allow(unused_variables)] - fn rem(&self, vm: &mut Vm, rhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + pub fn with_clone(self) -> Self + where + T: Clone, + { + self.with_deep_clone(|_| |this| Some(AnyDynamic::new((*this).clone()))) } - #[allow(unused_variables)] - fn rem_right(&self, vm: &mut Vm, lhs: &Value) -> Result { - Err(Fault::UnsupportedOperation) + #[must_use] + #[allow(clippy::too_many_lines)] + pub fn with_fallback(mut self, mapping: Mapping) -> Self + where + Mapping: Fn(Dynamic) -> Value + Send + Sync + Clone + 'static, + { + self.t = self + .t + .with_fallback(move |dynamic| dynamic.as_type::().map_or(Value::Nil, &mapping)); + self } - #[allow(unused_variables)] - fn truthy(&self, vm: &mut Vm) -> bool { - true + fn seal(self) -> TypeRef { + self.t.seal() } +} - #[allow(unused_variables)] - fn to_string(&self, vm: &mut Vm) -> Result { - Err(Fault::UnsupportedOperation) +impl CustomType for Type { + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("Type", |t| { + t.with_call(|_| |this, vm, arity| (this.vtable.construct)(vm, arity)) + }); + &TYPE } +} - fn deep_clone(&self) -> Option { - None +pub type TypeRef = Dynamic; + +pub type ConstructFn = Box Result + Send + Sync>; +pub type CallFn = Box Result + Send + Sync>; +pub type HashFn = Box; +pub type UnaryFn = Box Result + Send + Sync>; +pub type BinaryFn = Box Result + Send + Sync>; +pub type MatchesFn = Box Result + Send + Sync>; +pub type EqFn = + Box, &Value) -> Result + Send + Sync>; +pub type TotalCmpFn = + Box Result + Send + Sync>; +pub type InvokeFn = + Box Result + Send + Sync>; +pub type DeepCloneFn = Box Option + Send + Sync>; +pub type TruthyFn = Box bool + Send + Sync>; +pub type ToStringFn = Box Result + Send + Sync>; + +#[allow(clippy::type_complexity)] +pub struct TypeVtable { + construct: ConstructFn, + call: CallFn, + invoke: InvokeFn, + hash: HashFn, + bitwise_not: UnaryFn, + bitwise_and: BinaryFn, + bitwise_or: BinaryFn, + bitwise_xor: BinaryFn, + shift_left: BinaryFn, + shift_right: BinaryFn, + negate: UnaryFn, + eq: EqFn, + matches: MatchesFn, + total_cmp: TotalCmpFn, + add: BinaryFn, + add_right: BinaryFn, + sub: BinaryFn, + sub_right: BinaryFn, + mul: BinaryFn, + mul_right: BinaryFn, + div: BinaryFn, + div_right: BinaryFn, + idiv: BinaryFn, + idiv_right: BinaryFn, + rem: BinaryFn, + rem_right: BinaryFn, + truthy: TruthyFn, + to_string: ToStringFn, + deep_clone: DeepCloneFn, +} + +impl Default for TypeVtable { + fn default() -> Self { + Self { + construct: Box::new(|_vm, _arity| Err(Fault::UnsupportedOperation)), + call: Box::new(|_this, _vm, _arity| Err(Fault::NotAFunction)), + invoke: Box::new(|_this, _vm, _name, _arity| Err(Fault::UnknownSymbol)), + hash: Box::new(|this, _vm, hasher| Arc::as_ptr(&this.0).hash(hasher)), + bitwise_not: Box::new(|_this, _vm| Err(Fault::UnsupportedOperation)), + bitwise_and: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + bitwise_or: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + bitwise_xor: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + shift_left: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + shift_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + negate: Box::new(|_this, _vm| Err(Fault::UnsupportedOperation)), + eq: Box::new(|_this, _vm, _rhs| Ok(false)), + matches: Box::new(|this, vm, rhs| this.eq(Some(vm), rhs)), + total_cmp: Box::new(|_this, _vm, rhs| { + if rhs.as_any_dynamic().is_none() { + // Dynamics sort after primitive values + Ok(Ordering::Greater) + } else { + Err(Fault::UnsupportedOperation) + } + }), + add: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + add_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + sub: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + sub_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + mul: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + mul_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + div: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + div_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + idiv: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + idiv_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + rem: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + rem_right: Box::new(|_this, _vm, _rhs| Err(Fault::UnsupportedOperation)), + truthy: Box::new(|_this, _vm| true), + to_string: Box::new(|_this, _vm| Err(Fault::UnsupportedOperation)), + deep_clone: Box::new(|_this| None), + } } } +pub trait CustomType: Send + Sync + Debug + 'static { + fn muse_type(&self) -> &TypeRef; +} + pub struct RustFunctionTable { functions: Map>>>, } @@ -1553,16 +2712,21 @@ where } } +type ArcRustFn = Arc Result + Send + Sync>; + #[derive(Clone)] -pub struct RustFunction(F); +pub struct RustFunction(ArcRustFn); -impl RustFunction { - pub fn new(function: F) -> Self { - Self(function) +impl RustFunction { + pub fn new(function: F) -> Self + where + F: Fn(&mut Vm, Arity) -> Result + Send + Sync + 'static, + { + Self(Arc::new(function)) } } -impl Debug for RustFunction { +impl Debug for RustFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("RustFunction") .field(&std::ptr::addr_of!(self.0).cast::<()>()) @@ -1570,28 +2734,42 @@ impl Debug for RustFunction { } } -impl CustomType for RustFunction -where - F: Fn(&mut Vm, Arity) -> Result + Send + Sync + 'static, -{ - fn call(&self, vm: &mut Vm, _this: &AnyDynamic, arity: Arity) -> Result { - vm.enter_anonymous_frame()?; - let result = self.0(vm, arity)?; - vm.exit_frame()?; - Ok(result) +impl CustomType for RustFunction { + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("NativFunction", |t| { + t.with_call(|_previous| { + |this, vm, arity| { + vm.enter_anonymous_frame()?; + let result = this.0(vm, arity)?; + vm.exit_frame()?; + Ok(result) + } + }) + }); + &TYPE } } +type ArcAsyncFunction = Arc< + dyn Fn(&mut Vm, Arity) -> Pin> + Send + Sync>> + + Send + + Sync, +>; + #[derive(Clone)] -pub struct AsyncFunction(F); +pub struct AsyncFunction(ArcAsyncFunction); -impl AsyncFunction { - pub fn new(function: F) -> Self { - Self(function) +impl AsyncFunction { + pub fn new(function: F) -> Self + where + F: Fn(&mut Vm, Arity) -> Fut + Send + Sync + 'static, + Fut: Future> + Send + Sync + 'static, + { + Self(Arc::new(move |vm, arity| Box::pin(function(vm, arity)))) } } -impl Debug for AsyncFunction { +impl Debug for AsyncFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("AsyncFunction") .field(&std::ptr::addr_of!(self.0).cast::<()>()) @@ -1599,53 +2777,64 @@ impl Debug for AsyncFunction { } } -impl CustomType for AsyncFunction -where - F: Fn(&mut Vm, Arity) -> Fut + Send + Sync + 'static, - Fut: Future> + Send + Sync + 'static, -{ - fn call(&self, vm: &mut Vm, _this: &AnyDynamic, arity: Arity) -> Result { - vm.enter_anonymous_frame()?; - if vm.current_instruction() == 0 { - let future = self.0(vm, arity); - vm.allocate(1)?; - vm.current_frame_mut()[0] = - Value::dynamic(ValueFuture(Arc::new(Mutex::new(Box::pin(future))))); - vm.jump_to(1); - } - - let Some(future) = vm.current_frame()[0] - .as_dynamic() - .and_then(|d| d.downcast_ref::>()) - else { - unreachable!("missing future") - }; +impl CustomType for AsyncFunction { + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("AsyncNativeFunction", |t| { + t.with_call(|_previous| { + |this, vm, arity| { + vm.enter_anonymous_frame()?; + if vm.current_instruction() == 0 { + let future = this.0(vm, arity); + vm.allocate(1)?; + vm.current_frame_mut()[0] = + Value::dynamic(ValueFuture(Arc::new(Mutex::new(Box::pin(future))))); + vm.jump_to(1); + } + + let Some(future) = vm.current_frame()[0] + .as_any_dynamic() + .and_then(|d| d.downcast_ref::()) + else { + unreachable!("missing future") + }; + + let mut future = future.0.lock().expect("poisoned"); + let mut cx = Context::from_waker(vm.waker()); + match Future::poll(std::pin::pin!(&mut *future), &mut cx) { + Poll::Ready(Ok(result)) => { + drop(future); + vm.exit_frame()?; + Ok(result) + } + Poll::Ready(Err(Fault::Waiting)) | Poll::Pending => Err(Fault::Waiting), + Poll::Ready(Err(err)) => Err(err), + } + } + }) + }); - let mut future = future.0.lock().expect("poisoned"); - let mut cx = Context::from_waker(vm.waker()); - match Future::poll(std::pin::pin!(&mut *future), &mut cx) { - Poll::Ready(Ok(result)) => { - drop(future); - vm.exit_frame()?; - Ok(result) - } - Poll::Ready(Err(Fault::Waiting)) | Poll::Pending => Err(Fault::Waiting), - Poll::Ready(Err(err)) => Err(err), - } + &TYPE } } -struct ValueFuture(Arc>>>); +type ArcFuture = Arc> + Send + Sync>>>>; -impl Clone for ValueFuture { +struct ValueFuture(ArcFuture); + +impl Clone for ValueFuture { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl CustomType for ValueFuture where F: Send + Sync + 'static {} +impl CustomType for ValueFuture { + fn muse_type(&self) -> &TypeRef { + static TYPE: StaticType = StaticType::new(|| Type::new("AsyncValue")); + &TYPE + } +} -impl Debug for ValueFuture { +impl Debug for ValueFuture { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ValueFuture").finish_non_exhaustive() } @@ -1700,8 +2889,9 @@ where #[test] fn dynamic() { impl CustomType for usize { - fn deep_clone(&self) -> Option { - Some(AnyDynamic::new(*self)) + fn muse_type(&self) -> &TypeRef { + static TYPE: RustType = RustType::new("usize", TypedTypeBuilder::with_clone); + &TYPE } } let mut dynamic = AnyDynamic::new(1_usize); diff --git a/src/vm.rs b/src/vm.rs index e7474b6..6215f23 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -23,16 +23,14 @@ use self::bitcode::{ }; use crate::compiler::{BitcodeModule, BlockDeclaration, SourceMap, UnaryKind}; use crate::exception::Exception; -#[cfg(not(feature = "dispatched"))] -use crate::list::List; -#[cfg(not(feature = "dispatched"))] -use crate::map; use crate::regex::MuseRegex; use crate::string::MuseString; use crate::symbol::{IntoOptionSymbol, Symbol}; use crate::syntax::token::RegexLiteral; use crate::syntax::{BitwiseKind, CompareKind, SourceRange}; -use crate::value::{AnyDynamic, CustomType, Dynamic, StaticRustFunctionTable, Value, WeakDynamic}; +use crate::value::{ + AnyDynamic, CustomType, Dynamic, RustType, StaticRustFunctionTable, Value, WeakDynamic, +}; pub mod bitcode; @@ -85,7 +83,7 @@ impl Default for Vm { max_depth: usize::MAX, budget: Budget::default(), execute_until: None, - modules: vec![Dynamic::new(Module::default())], + modules: vec![Module::with_core()], waker: Waker::from(Arc::new(VmWaker(unparker))), parker, code: Vec::new(), @@ -246,11 +244,11 @@ impl Vm { .clone(); drop(module_declarations); - let Some(function) = function.as_downcast_ref::() else { + let Some(function) = function.as_dynamic::() else { return Err(ExecutionError::new(Fault::NotAFunction, self)); }; - match function.call(self, module_dynamic.as_any_dynamic(), arity) { + match function.as_any_dynamic().call(self, arity) { Ok(value) => Ok(value), Err(Fault::FrameChanged) => self.resume(), Err(other) => Err(ExecutionError::new(other, self)), @@ -436,7 +434,37 @@ impl Vm { self.declare_inner(name, Value::dynamic(function), true) } - pub fn resolve(&self, name: &Symbol) -> Result { + pub fn resolve(&mut self, name: &Symbol) -> Result { + if let Some(path) = name.strip_prefix("$.") { + let mut module = self.modules[0].clone(); + let mut path = path.split('.').peekable(); + let name = loop { + let Some(name) = path.next() else { + return Err(Fault::UnknownSymbol); + }; + let name = Symbol::from(name); + if path.peek().is_some() { + let declarations = module.declarations(); + let value = &declarations.get(&name).ok_or(Fault::UnknownSymbol)?.value; + let Some(inner) = value.as_dynamic::() else { + return Err(Fault::MissingModule); + }; + drop(declarations); + module = inner; + } else { + // Final path component + break name; + } + }; + + return Ok(module + .declarations() + .get(&name) + .ok_or(Fault::UnknownSymbol)? + .value + .clone()); + } + let current_frame = &self.frames[self.current_frame]; if let Some(decl) = current_frame.variables.get(name) { self.current_frame() @@ -707,8 +735,6 @@ impl Vm { LoadedOp::Copy(loaded) => self.op_copy(code_index, loaded.op, loaded.dest), LoadedOp::Resolve(loaded) => self.op_resolve(code_index, loaded.op, loaded.dest), LoadedOp::Jump(loaded) => self.op_jump(code_index, loaded.op, loaded.dest), - LoadedOp::NewMap(loaded) => self.op_new_map(code_index, loaded.op, loaded.dest), - LoadedOp::NewList(loaded) => self.op_new_list(code_index, loaded.op, loaded.dest), LoadedOp::SetExceptionHandler(loaded) => { self.op_set_exception_handler(code_index, loaded.op, loaded.dest) } @@ -1132,50 +1158,6 @@ impl Vm { } } - fn op_new_map( - &mut self, - code_index: usize, - element_count: LoadedSource, - dest: OpDestination, - ) -> Result<(), Fault> { - let element_count = self.op_load(code_index, element_count)?; - if let Some(element_count) = element_count - .as_u64() - .and_then(|c| u8::try_from(c).ok()) - .filter(|count| count < &128) - { - let map = map::Map::new(); - for reg_index in (0..element_count * 2).step_by(2) { - let key = self[Register(reg_index)].take(); - let value = self[Register(reg_index + 1)].take(); - map.insert(self, key, value)?; - } - self.op_store(code_index, Value::dynamic(map), dest) - } else { - // TODO handle large map initialization - Err(Fault::InvalidArity) - } - } - - fn op_new_list( - &mut self, - code_index: usize, - element_count: LoadedSource, - dest: OpDestination, - ) -> Result<(), Fault> { - let element_count = self.op_load(code_index, element_count)?; - if let Some(element_count) = element_count.as_u64().and_then(|c| u8::try_from(c).ok()) { - let list = List::new(); - for reg_index in 0..element_count { - let value = self[Register(reg_index)].take(); - list.push(value)?; - } - self.op_store(code_index, Value::dynamic(list), dest) - } else { - Err(Fault::InvalidArity) - } - } - fn op_set_exception_handler( &mut self, code_index: usize, @@ -1550,22 +1532,26 @@ impl Function { } impl CustomType for Function { - fn call(&self, vm: &mut Vm, this: &AnyDynamic, arity: Arity) -> Result { - if let Some(body) = self.bodies.get(&arity).or_else(|| { - self.varg_bodies - .iter() - .rev() - .find_map(|va| (va.key() <= &arity).then_some(&va.value)) - }) { - let module = self.module.ok_or(Fault::MissingModule)?; - vm.execute_function(body, this, module) - } else { - Err(Fault::IncorrectNumberOfArguments) - } - } - - fn deep_clone(&self) -> Option { - Some(AnyDynamic::new(self.clone())) + fn muse_type(&self) -> &crate::value::TypeRef { + static TYPE: RustType = RustType::new("Function", |t| { + t.with_call(|_| { + |this, vm, arity| { + if let Some(body) = this.bodies.get(&arity).or_else(|| { + this.varg_bodies + .iter() + .rev() + .find_map(|va| (va.key() <= &arity).then_some(&va.value)) + }) { + let module = this.module.ok_or(Fault::MissingModule)?; + vm.execute_function(body, this.as_any_dynamic(), module) + } else { + Err(Fault::IncorrectNumberOfArguments) + } + } + }) + .with_clone() + }); + &TYPE } } @@ -1694,8 +1680,6 @@ impl CodeData { UnaryKind::Copy => LoadedOp::Copy(unary), UnaryKind::Resolve => LoadedOp::Resolve(unary), UnaryKind::Jump => LoadedOp::Jump(unary), - UnaryKind::NewMap => LoadedOp::NewMap(unary), - UnaryKind::NewList => LoadedOp::NewList(unary), UnaryKind::SetExceptionHandler => LoadedOp::SetExceptionHandler(unary), }, range, @@ -1884,45 +1868,98 @@ pub struct Module { } impl Module { + #[must_use] + pub fn with_core() -> Dynamic { + let module = Dynamic::new(Self::default()); + + let core = Self { + parent: Some(module.downgrade()), + ..Self::core() + }; + + module.declarations().insert( + Symbol::from("core"), + ModuleDeclaration { + mutable: false, + value: Value::dynamic(core), + }, + ); + + module + } + + pub fn core() -> Self { + let core = Self::default(); + + let mut declarations = core.declarations(); + declarations.insert( + Symbol::from("Map"), + ModuleDeclaration { + mutable: false, + value: Value::Dynamic(crate::map::MAP_TYPE.as_any_dynamic().clone()), + }, + ); + declarations.insert( + Symbol::from("List"), + ModuleDeclaration { + mutable: false, + value: Value::Dynamic(crate::list::LIST_TYPE.as_any_dynamic().clone()), + }, + ); + drop(declarations); + + core + } + fn declarations(&self) -> MutexGuard<'_, Map> { self.declarations.lock().expect("poisoned") } } impl CustomType for Module { - fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result { - static FUNCTIONS: StaticRustFunctionTable = StaticRustFunctionTable::new(|table| { - table - .with_fn(Symbol::set_symbol(), 2, |vm, this| { - let field = vm[Register(0)].take(); - let sym = field.as_symbol().ok_or(Fault::ExpectedSymbol)?; - let value = vm[Register(1)].take(); - - match this.declarations().get_mut(sym) { - Some(decl) if decl.mutable => Ok(std::mem::replace(&mut decl.value, value)), - Some(_) => Err(Fault::NotMutable), - None => Err(Fault::UnknownSymbol), + fn muse_type(&self) -> &crate::value::TypeRef { + static TYPE: RustType = RustType::new("Module", |t| { + t.with_invoke(|_| { + |this, vm, name, arity| { + static FUNCTIONS: StaticRustFunctionTable = + StaticRustFunctionTable::new(|table| { + table + .with_fn(Symbol::set_symbol(), 2, |vm, this| { + let field = vm[Register(0)].take(); + let sym = field.as_symbol().ok_or(Fault::ExpectedSymbol)?; + let value = vm[Register(1)].take(); + + match this.declarations().get_mut(sym) { + Some(decl) if decl.mutable => { + Ok(std::mem::replace(&mut decl.value, value)) + } + Some(_) => Err(Fault::NotMutable), + None => Err(Fault::UnknownSymbol), + } + }) + .with_fn(Symbol::get_symbol(), 1, |vm, this| { + let field = vm[Register(0)].take(); + let sym = field.as_symbol().ok_or(Fault::ExpectedSymbol)?; + + this.declarations() + .get(sym) + .map(|decl| decl.value.clone()) + .ok_or(Fault::UnknownSymbol) + }) + }); + let declarations = this.declarations(); + if let Some(decl) = declarations.get(name) { + let possible_invoke = decl.value.clone(); + drop(declarations); + possible_invoke.call(vm, arity) + } else { + drop(declarations); + FUNCTIONS.invoke(vm, name, arity, &this) } - }) - .with_fn(Symbol::get_symbol(), 1, |vm, this| { - let field = vm[Register(0)].take(); - let sym = field.as_symbol().ok_or(Fault::ExpectedSymbol)?; - - this.declarations() - .get(sym) - .map(|decl| decl.value.clone()) - .ok_or(Fault::UnknownSymbol) - }) + } + }) }); - let declarations = self.declarations(); - if let Some(decl) = declarations.get(name) { - let possible_invoke = decl.value.clone(); - drop(declarations); - possible_invoke.call(vm, arity) - } else { - drop(declarations); - FUNCTIONS.invoke(vm, name, arity, self) - } + &TYPE } } @@ -1994,8 +2031,6 @@ enum LoadedOp { Copy(LoadedUnary), Resolve(LoadedUnary), Jump(LoadedUnary), - NewMap(LoadedUnary), - NewList(LoadedUnary), SetExceptionHandler(LoadedUnary), LogicalXor(LoadedBinary), Assign(LoadedBinary), diff --git a/src/vm/bitcode.rs b/src/vm/bitcode.rs index f20abed..50919b5 100644 --- a/src/vm/bitcode.rs +++ b/src/vm/bitcode.rs @@ -381,11 +381,8 @@ impl BitcodeBlock { element_count: impl Into, dest: impl Into, ) { - self.push(Op::Unary { - op: element_count.into(), - dest: dest.into(), - kind: UnaryKind::NewMap, - }); + self.call(Symbol::from("$.core.Map"), element_count); + self.copy(Register(0), dest); } pub fn new_list( @@ -393,11 +390,8 @@ impl BitcodeBlock { element_count: impl Into, dest: impl Into, ) { - self.push(Op::Unary { - op: element_count.into(), - dest: dest.into(), - kind: UnaryKind::NewList, - }); + self.call(Symbol::from("$.core.List"), element_count); + self.copy(Register(0), dest); } pub fn resolve(&mut self, source: impl Into, dest: impl Into) { @@ -584,8 +578,6 @@ impl From<&'_ Code> for BitcodeBlock { LoadedOp::Copy(loaded) => loaded.as_op(UnaryKind::Copy, code), LoadedOp::Resolve(loaded) => loaded.as_op(UnaryKind::Resolve, code), LoadedOp::Jump(loaded) => loaded.as_op(UnaryKind::Jump, code), - LoadedOp::NewMap(loaded) => loaded.as_op(UnaryKind::NewMap, code), - LoadedOp::NewList(loaded) => loaded.as_op(UnaryKind::NewList, code), LoadedOp::SetExceptionHandler(loaded) => { loaded.as_op(UnaryKind::SetExceptionHandler, code) } diff --git a/src/vm/dispatched.rs b/src/vm/dispatched.rs index c4fe618..f4d913b 100644 --- a/src/vm/dispatched.rs +++ b/src/vm/dispatched.rs @@ -13,8 +13,6 @@ use super::{ Stack, }; use crate::compiler::{BitcodeModule, UnaryKind}; -use crate::list::List; -use crate::map::Map; use crate::string::MuseString; use crate::symbol::Symbol; use crate::syntax::{BitwiseKind, CompareKind}; @@ -79,16 +77,6 @@ impl CodeData { &loaded.dest, self, ), - LoadedOp::NewMap(loaded) => match_new_map( - &trusted_loaded_source_to_value(&loaded.op, self), - &loaded.dest, - self, - ), - LoadedOp::NewList(loaded) => match_new_list( - &trusted_loaded_source_to_value(&loaded.op, self), - &loaded.dest, - self, - ), LoadedOp::SetExceptionHandler(loaded) => match_set_exception_handler( &trusted_loaded_source_to_value(&loaded.op, self), &loaded.dest, @@ -528,83 +516,6 @@ where } } -#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] -pub struct NewMap { - pub element_count: Count, - pub dest: Dest, -} - -impl Instruction for NewMap -where - From: Source, - Dest: Destination, -{ - fn execute(&self, vm: &mut Vm) -> Result, Fault> { - let element_count = self.element_count.load(vm)?; - if let Some(element_count) = element_count - .as_u64() - .and_then(|c| u8::try_from(c).ok()) - .filter(|count| count < &128) - { - let map = Map::new(); - for reg_index in (0..element_count * 2).step_by(2) { - let key = vm[Register(reg_index)].take(); - let value = vm[Register(reg_index + 1)].take(); - map.insert(vm, key, value)?; - } - self.dest.store(vm, Value::dynamic(map))?; - - Ok(ControlFlow::Continue(())) - } else { - Err(Fault::InvalidArity) - } - } - - fn as_op(&self) -> Op { - Op::Unary { - op: self.element_count.as_source(), - dest: self.dest.as_dest(), - kind: UnaryKind::Copy, - } - } -} - -#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] -pub struct NewList { - pub element_count: Count, - pub dest: Dest, -} - -impl Instruction for NewList -where - From: Source, - Dest: Destination, -{ - fn execute(&self, vm: &mut Vm) -> Result, Fault> { - let element_count = self.element_count.load(vm)?; - if let Some(element_count) = element_count.as_u64().and_then(|c| u8::try_from(c).ok()) { - let list = List::new(); - for reg_index in 0..element_count { - let value = vm[Register(reg_index)].take(); - list.push(value)?; - } - self.dest.store(vm, Value::dynamic(list))?; - - Ok(ControlFlow::Continue(())) - } else { - Err(Fault::InvalidArity) - } - } - - fn as_op(&self) -> Op { - Op::Unary { - op: self.element_count.as_source(), - dest: self.dest.as_dest(), - kind: UnaryKind::Copy, - } - } -} - #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub struct SetExceptionHandler { pub handler: Handler, @@ -1302,40 +1213,6 @@ where }); } -decode_sd!(match_new_map, compile_new_map); - -fn compile_new_map( - _dest: &OpDestination, - code: &mut CodeData, - element_count: Arity, - dest: Dest, -) where - Arity: Source, - Dest: Destination, -{ - code.push_dispatched(NewMap { - element_count, - dest, - }); -} - -decode_sd!(match_new_list, compile_new_list); - -fn compile_new_list( - _dest: &OpDestination, - code: &mut CodeData, - element_count: Arity, - dest: Dest, -) where - Arity: Source, - Dest: Destination, -{ - code.push_dispatched(NewList { - element_count, - dest, - }); -} - decode_sd!(match_set_exception_handler, compile_set_exception_handler); fn compile_set_exception_handler( @@ -1570,8 +1447,10 @@ where module_index }; - self.dest - .store(vm, Value::Dynamic(vm.modules[loading_module.get()].clone()))?; + self.dest.store( + vm, + Value::Dynamic(vm.modules[loading_module.get()].as_any_dynamic().clone()), + )?; Ok(ControlFlow::Continue(())) }