Skip to content

Commit

Permalink
The Type type
Browse files Browse the repository at this point in the history
- 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).
  • Loading branch information
ecton committed Mar 1, 2024
1 parent 0e13098 commit 1c415e0
Show file tree
Hide file tree
Showing 11 changed files with 2,055 additions and 881 deletions.
204 changes: 112 additions & 92 deletions muse-ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<Value, Fault> {
if arity == 0 {
Ok(self.0.get())
} else {
Err(Fault::NotAFunction)
}
}
fn muse_type(&self) -> &TypeRef {
static TYPE: RustType<DynamicValue> = 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<Value, Fault> {
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<muse::value::AnyDynamic> {
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
}
}

Expand Down Expand Up @@ -231,25 +239,37 @@ impl MakeWidget for &'_ MuseWidget {
}

impl CustomType for MuseWidget {
fn invoke(&self, _vm: &mut Vm, name: &Symbol, arity: Arity) -> Result<Value, Fault> {
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<MuseWidget> = 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<OpenWindow> = RustType::new("Window", |t| t);
&TYPE
}
}
2 changes: 0 additions & 2 deletions src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1828,8 +1828,6 @@ pub enum UnaryKind {
Copy,
Resolve,
Jump,
NewMap,
NewList,
SetExceptionHandler,
}

Expand Down
62 changes: 28 additions & 34 deletions src/exception.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -26,36 +25,31 @@ impl Exception {
}

impl CustomType for Exception {
fn eq(&self, vm: Option<&mut Vm>, rhs: &Value) -> Result<bool, Fault> {
if let Some(rhs) = rhs.as_downcast_ref::<Self>() {
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<bool, Fault> {
self.value.matches(vm, rhs)
}

fn total_cmp(&self, vm: &mut Vm, rhs: &Value) -> Result<std::cmp::Ordering, Fault> {
self.value.total_cmp(vm, rhs)
}

fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: crate::vm::Arity) -> Result<Value, Fault> {
self.value.invoke(vm, name, arity)
}

fn to_string(&self, vm: &mut Vm) -> Result<Symbol, Fault> {
self.value.to_string(vm)
}

fn deep_clone(&self) -> Option<AnyDynamic> {
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<Exception> = RustType::new("Exception", |t| {
t.with_fallback(|this| this.value.clone())
.with_eq(|_| {
|this, vm, rhs| {
if let Some(rhs) = rhs.as_downcast_ref::<Exception>() {
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
}
}
67 changes: 43 additions & 24 deletions src/list.rs
Original file line number Diff line number Diff line change
@@ -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<List> = 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<List> =
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<Vec<Value>>);
Expand Down Expand Up @@ -68,27 +107,7 @@ impl FromIterator<Value> for List {
}

impl CustomType for List {
fn invoke(&self, vm: &mut Vm, name: &Symbol, arity: Arity) -> Result<Value, Fault> {
static FUNCTIONS: StaticRustFunctionTable<List> = 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
}
}
Loading

0 comments on commit 1c415e0

Please sign in to comment.