use std::cell::OnceCell; use std::cell::RefCell; 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(Thunk<'vm>), ThunkRef(&'vm Thunk<'vm>), AttrSet(AttrSet<'vm>), RecAttrSet(RecAttrSet<'vm>), List(List<'vm>), Catchable(c::Catchable), PrimOp(PrimOp<'vm>), PartialPrimOp(PartialPrimOp<'vm>), Func(Func<'vm>), } #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] pub enum ValueAsRef<'v, 'vm: 'v> { Const(&'v Const), Thunk(&'v Thunk<'vm>), AttrSet(&'v AttrSet<'vm>), RecAttrSet(&'v RecAttrSet<'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>), RecAttrSet(&'v mut RecAttrSet<'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), RecAttrSet(x) => R::RecAttrSet(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), } } 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(x), RecAttrSet(x) => M::RecAttrSet(x), List(x) => M::List(x), Catchable(x) => M::Catchable(x), PrimOp(x) => M::PrimOp(x), PartialPrimOp(x) => M::PartialPrimOp(x), Func(x) => M::Func(x), } } } use Value::Const as VmConst; impl<'vm> Value<'vm> { pub fn typename(&self) -> &'static str { use Value::*; match self { Const(_) => unreachable!(), Thunk(_) => "thunk", ThunkRef(_) => "thunk", AttrSet(_) => "set", RecAttrSet(_) => "set", List(_) => "list", Catchable(_) => unreachable!(), PrimOp(_) => "lambda", PartialPrimOp(_) => "lambda", Func(_) => "lambda", } } pub fn callable(&self) -> bool { match self { Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, Value::AttrSet(_) | Value::RecAttrSet(_) => todo!(), _ => false, } } pub fn call(self, vm: &'vm VM<'_>, args: Vec>) -> Result> { use Value::*; Ok(match self { PrimOp(func) => func.call(vm, args), PartialPrimOp(func) => func.call(vm, args), mut func @ Value::Func(_) => { let mut iter = args.into_iter(); while let Some(arg) = iter.next() { func = match func { PrimOp(func) => { return Ok(func.call(vm, [arg].into_iter().chain(iter).collect())); } PartialPrimOp(func) => { return Ok(func.call(vm, [arg].into_iter().chain(iter).collect())); } Func(func) => func.call(vm, arg)?, _ => todo!(), } } func } x @ Catchable(_) => x, _ => todo!(), }) } pub fn not(self) -> Value<'vm> { use Const::*; match self { VmConst(Bool(bool)) => VmConst(Bool(!bool)), x @ Value::Catchable(_) => x, _ => todo!(), } } pub fn and(self, other: Value<'vm>) -> Value<'vm> { 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: Value<'vm>) -> Value<'vm> { 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: Value<'vm>) -> Value<'vm> { use Const::Bool; match (self, other) { (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, (s, other) => VmConst(Bool(s == other)), } } pub fn lt(self, other: Value<'vm>) -> Value<'vm> { 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) -> Value<'vm> { 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: Value<'vm>) -> Value<'vm> { 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: Value<'vm>) -> Value<'vm> { 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: Value<'vm>) -> Value<'vm> { use Const::*; match (self, other) { (_, VmConst(Int(0))) => todo!(), (_, VmConst(Float(0.))) => todo!(), (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: Value<'vm>) -> &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: Value<'vm>) -> &mut Self { if let Value::List(list) = self { 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: Value<'vm>) -> Value<'vm> { match (self, other) { (Value::List(a), Value::List(b)) => Value::List(a.concat(b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn push_attr(&mut self, sym: usize, val: Value<'vm>) -> &mut Self { if let Value::AttrSet(attrs) = self { attrs.push_attr(sym, val) } else if let Value::RecAttrSet(attrs) = self { 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: Value<'vm>) -> Value<'vm> { match (self, other) { (Value::AttrSet(a), Value::AttrSet(b)) => Value::AttrSet(a.update(b)), (Value::RecAttrSet(a), Value::AttrSet(b)) => Value::AttrSet(a.update_normal(b)), (Value::AttrSet(a), Value::RecAttrSet(b)) => Value::AttrSet(a.update_rec(b)), (x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn select(&mut self, sym: usize) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs .select(sym) .ok_or_else(|| Error::EvalError(format!("{sym:?} not found"))), Value::RecAttrSet(attrs) => attrs .select(sym) .ok_or_else(|| Error::EvalError(format!("{sym:?} not found"))), Value::Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", self.typename() ))), }?; *self = val; Ok(self) } pub fn select_with_default(&mut self, sym: usize, default: Value<'vm>) -> Result<&mut Self> { let val = match self { Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default), Value::RecAttrSet(attrs) => attrs.select(sym).unwrap_or(default), Value::Catchable(_) => return Ok(self), _ => { return Err(Error::EvalError(format!( "cannot select from {:?}", self.typename() ))); } }; *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::RecAttrSet(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) => list.force_deep(vm)?, Value::AttrSet(attrs) => attrs.force_deep(vm)?, Value::RecAttrSet(attrs) => attrs.force_deep(vm)?, _ => (), } Ok(self) } } impl ToPublic for Value<'_> { fn to_public(self, vm: &VM) -> p::Value { match self { Value::AttrSet(attrs) => attrs.to_public(vm), Value::RecAttrSet(attrs) => attrs.to_public(vm), Value::List(list) => list.to_public(vm), Value::Catchable(catchable) => p::Value::Catchable(catchable), Value::Const(cnst) => p::Value::Const(cnst.into()), Value::Thunk(_) => p::Value::Thunk, Value::ThunkRef(_) => p::Value::Thunk, Value::PrimOp(primop) => p::Value::PrimOp(primop.name), Value::PartialPrimOp(primop) => p::Value::PartialPrimOp(primop.name), Value::Func(_) => p::Value::Func, } } } pub trait ToPublic { fn to_public(self, vm: &VM) -> p::Value; } #[derive(Debug, Clone)] pub struct Thunk<'vm> { pub thunk: Rc>>, } #[derive(Debug, IsVariant, Unwrap, Clone)] pub enum _Thunk<'vm> { Code(&'vm OpCodes, OnceCell>>), SuspendedFrom(*const Thunk<'vm>), Value(Box>), } impl<'vm> Thunk<'vm> { pub fn new(opcodes: &'vm OpCodes) -> Self { Thunk { thunk: Rc::new(RefCell::new(_Thunk::Code(opcodes, OnceCell::new()))), } } pub fn capture(&self, env: Rc>) { 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.as_ref().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.clone(), 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.as_ref().clone()), _ => None, } } } impl PartialEq for Thunk<'_> { fn eq(&self, _: &Self) -> bool { false } }