use std::cell::OnceCell; use std::cell::RefCell; use std::collections::HashSet; use std::hash::Hash; use std::rc::Rc; use derive_more::{IsVariant, Unwrap}; use super::common as c; use super::public as p; use crate::bytecode::OpCodes; use crate::error::*; use crate::vm::{Env, VM}; mod attrset; mod cnst; mod func; mod list; mod primop; mod string; pub use attrset::*; pub use cnst::Const; pub use func::*; pub use list::List; pub use primop::*; #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] pub enum Value<'vm> { Const(Const), Thunk(Rc>), ThunkRef(&'vm Thunk<'vm>), AttrSet(Rc>), List(Rc>), Catchable(c::Catchable), PrimOp(Rc>), PartialPrimOp(Rc>), Func(Rc>), Builtins, } impl Hash for Value<'_> { fn hash(&self, state: &mut H) { use Value::*; std::mem::discriminant(self).hash(state); match self { Const(x) => x.hash(state), Thunk(x) => (x.as_ref() as *const self::Thunk).hash(state), ThunkRef(x) => (*x as *const self::Thunk).hash(state), AttrSet(x) => (x.as_ref() as *const self::AttrSet).hash(state), List(x) => (x.as_ref() as *const self::List).hash(state), Catchable(x) => x.hash(state), PrimOp(x) => (x.as_ref() as *const self::PrimOp).hash(state), PartialPrimOp(x) => (x.as_ref() as *const self::PartialPrimOp).hash(state), Func(x) => (x.as_ref() as *const self::Func).hash(state), Builtins => (), } } } impl<'vm> Value<'vm> { fn eq_impl(&self, other: &Self, vm: &'vm VM<'_>) -> bool { use Value::*; match (self, other) { (Const(a), Const(b)) => a.eq(b), (AttrSet(a), AttrSet(b)) => a.eq_impl(b, vm), (List(a), List(b)) => a.eq(b), (Builtins, AttrSet(attrs)) => attrs.has_attr(vm.new_sym("builtins")), (AttrSet(attrs), Builtins) => attrs.has_attr(vm.new_sym("builtins")), _ => false, } } } impl Eq for Value<'_> {} #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] pub enum ValueAsRef<'v, 'vm: 'v> { Const(&'v Const), Thunk(&'v Thunk<'vm>), AttrSet(&'v AttrSet<'vm>), List(&'v List<'vm>), Catchable(&'v c::Catchable), PrimOp(&'v PrimOp<'vm>), PartialPrimOp(&'v PartialPrimOp<'vm>), Func(&'v Func<'vm>), } #[derive(Debug, IsVariant, Unwrap, PartialEq)] pub enum ValueAsMut<'v, 'vm: 'v> { Const(&'v mut Const), Thunk(&'v Thunk<'vm>), AttrSet(&'v mut AttrSet<'vm>), List(&'v mut List<'vm>), Catchable(&'v mut c::Catchable), PrimOp(&'v mut PrimOp<'vm>), PartialPrimOp(&'v mut PartialPrimOp<'vm>), Func(&'v Func<'vm>), } impl<'v, 'vm: 'v> Value<'vm> { pub fn as_ref(&'v self) -> ValueAsRef<'v, 'vm> { use Value::*; use ValueAsRef as R; match self { Const(x) => R::Const(x), Thunk(x) => R::Thunk(x), ThunkRef(x) => R::Thunk(x), AttrSet(x) => R::AttrSet(x), List(x) => R::List(x), Catchable(x) => R::Catchable(x), PrimOp(x) => R::PrimOp(x), PartialPrimOp(x) => R::PartialPrimOp(x), Func(x) => R::Func(x), Builtins => unreachable!(), } } pub fn as_mut(&'v mut self) -> ValueAsMut<'v, 'vm> { use Value::*; use ValueAsMut as M; match self { Const(x) => M::Const(x), Thunk(x) => M::Thunk(x), ThunkRef(x) => M::Thunk(x), AttrSet(x) => M::AttrSet(Rc::make_mut(x)), List(x) => M::List(Rc::make_mut(x)), Catchable(x) => M::Catchable(x), PrimOp(x) => M::PrimOp(Rc::make_mut(x)), PartialPrimOp(x) => M::PartialPrimOp(Rc::make_mut(x)), Func(x) => M::Func(x), Builtins => unreachable!(), } } } use Value::Const as VmConst; impl<'vm> Value<'vm> { pub fn ok(self) -> Result { Ok(self) } pub fn typename(&self) -> &'static str { use Value::*; match self { Const(_) => unreachable!(), Thunk(_) => "thunk", ThunkRef(_) => "thunk", AttrSet(_) => "set", List(_) => "list", Catchable(_) => unreachable!(), PrimOp(_) => "lambda", PartialPrimOp(_) => "lambda", Func(_) => "lambda", Builtins => "set", } } pub fn callable(&self) -> bool { match self { Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, Value::AttrSet(_) => todo!(), _ => false, } } pub fn call(&self, vm: &'vm VM<'_>, args: Vec) -> Result { use Value::*; match self { PrimOp(func) => func.call(vm, args), PartialPrimOp(func) => func.call(vm, args), func @ Value::Func(_) => { let mut iter = args.into_iter(); let mut func = func.clone(); while let Some(arg) = iter.next() { func = match func { PrimOp(func) => { return func.call(vm, [arg].into_iter().chain(iter).collect()); } PartialPrimOp(func) => { return func.call(vm, [arg].into_iter().chain(iter).collect()); } Func(func) => func.call(vm, arg)?, _ => todo!(), } } func.ok() } x @ Catchable(_) => x.clone().ok(), _ => todo!(), } } pub fn not(self) -> Self { use Const::*; match self { VmConst(Bool(bool)) => VmConst(Bool(!bool)), x @ Value::Catchable(_) => x, _ => todo!(), } } pub fn and(self, other: Self) -> Self { use Const::*; match (self, other) { (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a && b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn or(self, other: Self) -> Self { use Const::*; match (self, other) { (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a || b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn eq(self, other: Self) -> Self { use Const::Bool; match (self, other) { (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, (s, other) => VmConst(Bool(s == other)), } } pub fn lt(self, other: Self) -> Self { use Const::*; VmConst(Bool(match (self, other) { (VmConst(Int(a)), VmConst(Int(b))) => a < b, (VmConst(Int(a)), VmConst(Float(b))) => (a as f64) < b, (VmConst(Float(a)), VmConst(Int(b))) => a < b as f64, (VmConst(Float(a)), VmConst(Float(b))) => a < b, (VmConst(String(a)), VmConst(String(b))) => a < b, (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => return x, _ => todo!(), })) } pub fn neg(self) -> Self { use Const::*; match self { VmConst(Int(int)) => VmConst(Int(-int)), VmConst(Float(float)) => VmConst(Float(-float)), x @ Value::Catchable(_) => x, _ => todo!(), } } pub fn add(self, other: Self) -> Self { use Const::*; match (self, other) { (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a + b)), (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 + b)), (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a + b as f64)), (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a + b)), (VmConst(String(a)), VmConst(String(b))) => { let mut string = a.clone(); string.push_str(b.as_str()); VmConst(String(string)) } (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn mul(self, other: Self) -> Self { use Const::*; match (self, other) { (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a * b)), (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 * b)), (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a * b as f64)), (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a * b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn div(self, other: Self) -> Result { use Const::*; Ok(match (self, other) { (_, VmConst(Int(0))) => return Err(Error::EvalError("division by zero".to_string())), (_, VmConst(Float(0.))) => { return Err(Error::EvalError("division by zero".to_string())); } (VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a / b)), (VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 / b)), (VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a / b as f64)), (VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a / b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), }) } pub fn concat_string(&mut self, mut other: Self) -> &mut Self { match (self.coerce_to_string(), other.coerce_to_string()) { (VmConst(Const::String(a)), VmConst(Const::String(b))) => a.push_str(b.as_str()), (_, Value::Catchable(_)) => *self = other, (Value::Catchable(_), _) => (), _ => todo!(), } self } pub fn push(&mut self, elem: Self) -> &mut Self { if let Value::List(list) = self { Rc::make_mut(list).push(elem); } else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = elem { *self = elem; } else { todo!() } self } pub fn concat(self, other: Self) -> Self { match (self, other) { (Value::List(mut a), Value::List(b)) => { Rc::make_mut(&mut a).concat(b.as_ref()); Value::List(a) } (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self { if let Value::AttrSet(attrs) = self { Rc::make_mut(attrs).push_attr(sym, val) } else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = val { *self = val } else { todo!() } self } pub fn update(self, other: Self) -> Self { match (self, other) { (Value::AttrSet(mut a), Value::AttrSet(b)) => { Rc::make_mut(&mut a).update(b.as_ref()); Value::AttrSet(a) } (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn select(&mut self, sym: usize, vm: &'vm VM<'_>) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs .select(sym) .ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym)))), Value::Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", self.typename() ))), }?; if let Value::Builtins = val { } else { *self = val; } Ok(self) } pub fn select_with_default(&mut self, sym: usize, default: Self) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default), Value::Catchable(_) => return Ok(self), _ => { return Err(Error::EvalError(format!( "cannot select from {:?}", self.typename() ))); } }; if let Value::Builtins = val { } else { *self = val; } Ok(self) } pub fn has_attr(&mut self, sym: usize) -> &mut Self { if let Value::AttrSet(attrs) = self { let val = VmConst(Const::Bool(attrs.has_attr(sym))); *self = val; } else if let Value::Catchable(_) = self { } else { *self = VmConst(Const::Bool(false)); } self } pub fn coerce_to_string(&mut self) -> &mut Self { if let VmConst(Const::String(_)) = self { } else if let Value::Catchable(_) = self { } else { todo!() } self } pub fn force(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> { if let Value::Thunk(thunk) = self { let value = thunk.force(vm)?; *self = value } else if let Value::ThunkRef(thunk) = self { let value = thunk.force(vm)?; *self = value } Ok(self) } pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> { match self { Value::Thunk(thunk) => { let mut value = thunk.force(vm)?; let _ = value.force_deep(vm)?; *self = value; } Value::ThunkRef(thunk) => { let mut value = thunk.force(vm)?; let _ = value.force_deep(vm)?; *self = value; } Value::List(list) => Rc::make_mut(list).force_deep(vm)?, Value::AttrSet(attrs) => Rc::make_mut(attrs).force_deep(vm)?, _ => (), } Ok(self) } pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet>) -> p::Value { use self::Value::*; use p::Value; if seen.contains(self) { return Value::Repeated; } seen.insert(self.clone()); match self { AttrSet(attrs) => attrs.to_public(vm, seen), List(list) => list.to_public(vm, seen), Catchable(catchable) => Value::Catchable(catchable.clone()), Const(cnst) => Value::Const(cnst.clone().into()), Thunk(_) => Value::Thunk, ThunkRef(_) => Value::Thunk, PrimOp(primop) => Value::PrimOp(primop.name), PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), Func(_) => Value::Func, Builtins => Value::Repeated, } } } #[derive(Debug, Clone)] pub struct Thunk<'vm> { pub thunk: RefCell<_Thunk<'vm>>, } #[derive(Debug, IsVariant, Unwrap, Clone)] pub enum _Thunk<'vm> { Code(&'vm OpCodes, OnceCell>), SuspendedFrom(*const Thunk<'vm>), Value(Value<'vm>), } impl<'vm> Thunk<'vm> { pub fn new(opcodes: &'vm OpCodes) -> Self { Thunk { thunk: RefCell::new(_Thunk::Code(opcodes, OnceCell::new())), } } pub fn capture(&self, env: Env<'vm>) { if let _Thunk::Code(_, envcell) = &*self.thunk.borrow() { envcell.get_or_init(|| env); } } pub fn force(&self, vm: &'vm VM<'_>) -> Result> { match &*self.thunk.borrow() { _Thunk::Value(value) => return Ok(value.clone()), _Thunk::SuspendedFrom(from) => { return Err(Error::EvalError(format!( "thunk {:p} already suspended from {from:p} (infinite recursion encountered)", self as *const Thunk ))); } _Thunk::Code(..) => (), } let (opcodes, env) = std::mem::replace( &mut *self.thunk.borrow_mut(), _Thunk::SuspendedFrom(self as *const Thunk), ) .unwrap_code(); let value = vm.eval(opcodes.iter().copied(), env.get().unwrap().clone())?; let _ = std::mem::replace( &mut *self.thunk.borrow_mut(), _Thunk::Value(value.clone().into()), ); Ok(value) } pub fn value(&'vm self) -> Option> { match &*self.thunk.borrow() { _Thunk::Value(value) => Some(value.clone()), _ => None, } } } impl PartialEq for Thunk<'_> { fn eq(&self, _: &Self) -> bool { false } }