use hashbrown::HashSet; use std::cell::OnceCell; use std::cell::RefCell; use std::hash::Hash; use std::rc::{Rc, Weak}; use derive_more::{IsVariant, Unwrap}; use super::common::*; use super::public as p; use crate::bytecode::OpCodes; use crate::env::VmEnv; use crate::error::*; use crate::vm::VM; mod attrset; mod func; mod list; mod primop; mod string; pub use attrset::*; pub use func::*; pub use list::List; pub use primop::*; #[derive(Debug, IsVariant, Unwrap, Clone)] pub enum Value<'jit: 'vm, 'vm> { Const(Const), Thunk(Rc>), AttrSet(Rc>), List(Rc>), Catchable(Catchable), PrimOp(Rc>), PartialPrimOp(Rc>), Func(Rc>), Builtins(Weak>), } 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), 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(x) => (x.as_ptr()).hash(state), } } } impl<'jit: 'vm, 'vm> Value<'jit, 'vm> { fn eq_impl(&self, other: &Self) -> bool { use Value::*; match (self, other) { (Const(a), Const(b)) => a.eq(b), (AttrSet(a), AttrSet(b)) => a.eq_impl(b), (List(a), List(b)) => a.eq(b), _ => false, } } } impl<'jit: 'vm, 'vm> PartialEq for Value<'jit, 'vm> { fn eq(&self, other: &Self) -> bool { use Value::*; match (self, other) { (Const(a), Const(b)) => a.eq(b), (AttrSet(a), AttrSet(b)) => { (a.as_ref() as *const self::AttrSet).eq(&(b.as_ref() as *const _)) } (List(a), List(b)) => (a.as_ref() as *const self::List).eq(&(b.as_ref() as *const _)), (Builtins(a), Builtins(b)) => a.ptr_eq(b), _ => false, } } } impl Eq for Value<'_, '_> {} #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] pub enum ValueAsRef<'v, 'vm: 'v, 'jit: 'vm> { Const(&'v Const), Thunk(&'v Thunk<'jit, 'vm>), AttrSet(&'v AttrSet<'jit, 'vm>), List(&'v List<'jit, 'vm>), Catchable(&'v Catchable), PrimOp(&'v PrimOp<'jit, 'vm>), PartialPrimOp(&'v PartialPrimOp<'jit, 'vm>), Func(&'v Func<'jit, 'vm>), } #[derive(Debug, IsVariant, Unwrap, PartialEq)] pub enum ValueAsMut<'v, 'vm: 'v, 'jit: 'vm> { Const(&'v mut Const), Thunk(&'v Thunk<'jit, 'vm>), AttrSet(&'v mut AttrSet<'jit, 'vm>), List(&'v mut List<'jit, 'vm>), Catchable(&'v mut Catchable), PrimOp(&'v mut PrimOp<'jit, 'vm>), PartialPrimOp(&'v mut PartialPrimOp<'jit, 'vm>), Func(&'v Func<'jit, 'vm>), } impl<'v, 'vm: 'v, 'jit: 'vm> Value<'jit, 'vm> { pub fn as_ref(&'v self) -> ValueAsRef<'v, 'vm, 'jit> { use Value::*; use ValueAsRef as R; match self { Const(x) => R::Const(x), Thunk(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, 'jit> { use Value::*; use ValueAsMut as M; match self { Const(x) => M::Const(x), Thunk(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<'jit, 'vm> Value<'jit, 'vm> { pub fn ok(self) -> Result { Ok(self) } pub fn typename(&self) -> &'static str { use Value::*; match self { Const(self::Const::Int(_)) => "int", Const(self::Const::Float(_)) => "float", Const(self::Const::Bool(_)) => "bool", Const(self::Const::String(_)) => "string", Const(self::Const::Null) => "null", Thunk(_) => "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(&mut self, vm: &'vm VM<'jit>, arg: Self) -> Result<()> { use Value::*; if matches!(arg, Value::Catchable(_)) { *self = arg; return Ok(()); } *self = match self { PrimOp(func) => func.call(vm, arg), PartialPrimOp(func) => func.call(vm, arg), Value::Func(func) => func.call(vm, arg), Catchable(_) => return Ok(()), _ => todo!(), }?; Ok(()) } pub fn not(&mut self) { use Const::*; *self = match &*self { VmConst(Bool(bool)) => VmConst(Bool(!bool)), Value::Catchable(_) => return, _ => todo!(), } } pub fn and(&mut self, other: Self) { use Const::*; *self = match (&*self, other) { (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(*a && b)), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn or(&mut self, other: Self) { use Const::*; *self = match (&*self, other) { (VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(*a || b)), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn eq(&mut self, other: Self) { use Const::Bool; *self = match (&*self, other) { (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, (s, other) => VmConst(Bool(s.eq_impl(&other))), }; } pub fn lt(&mut self, other: Self) { use Const::*; *self = 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, (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => { *self = x; return; } _ => todo!(), })) } pub fn neg(&mut self) { use Const::*; *self = match &*self { VmConst(Int(int)) => VmConst(Int(-int)), VmConst(Float(float)) => VmConst(Float(-float)), Value::Catchable(_) => return, _ => todo!(), } } pub fn add(&mut self, other: Self) { use Const::*; *self = 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)) } (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn mul(&mut self, other: Self) { use Const::*; *self = 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)), (Value::Catchable(_), _) => return, (_, x @ Value::Catchable(_)) => x, _ => todo!(), } } pub fn div(&mut self, other: Self) -> Result<()> { use Const::*; *self = 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)), (Value::Catchable(_), _) => return Ok(()), (_, x @ Value::Catchable(_)) => x, _ => todo!(), }; Ok(()) } 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(&mut self, other: Self) { if let x @ Value::Catchable(_) = other { *self = x; return; } match (self, other) { (Value::List(a), Value::List(b)) => { Rc::make_mut(a).concat(b.as_ref()); } (Value::Catchable(_), _) => (), _ => 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(&mut self, other: Self) { if let x @ Value::Catchable(_) = other { *self = x; return; } match (self, other) { (Value::AttrSet(a), Value::AttrSet(b)) => { Rc::make_mut(a).update(b.as_ref()); } (Value::Catchable(_), _) => (), _ => todo!(), } } pub fn select(&mut self, sym: usize, vm: &'vm VM<'jit>) -> 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() ))), }?; *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() ))); } }; *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<'jit>) -> Result<&mut Self> { if let Value::Thunk(thunk) = self { let value = thunk.force(vm)?.clone(); *self = value } Ok(self) } pub fn force_deep(&mut self, vm: &'vm VM<'jit>) -> Result<&mut Self> { match self { Value::Thunk(thunk) => { let mut value = thunk.force(vm)?.clone(); 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()), Thunk(_) => Value::Thunk, PrimOp(primop) => Value::PrimOp(primop.name), PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), Func(_) => Value::Func, Builtins(x) => x.upgrade().unwrap().to_public(vm, seen), } } } #[derive(Debug, Clone)] pub struct Thunk<'jit, 'vm> { pub thunk: RefCell<_Thunk<'jit, 'vm>>, } #[derive(Debug, IsVariant, Unwrap, Clone)] pub enum _Thunk<'jit, 'vm> { Code(&'vm OpCodes, OnceCell<&'vm VmEnv<'jit, 'vm>>), SuspendedFrom(*const Thunk<'jit, 'vm>), Value(Value<'jit, 'vm>), } impl<'jit, 'vm> Thunk<'jit, 'vm> { pub fn new(opcodes: &'vm OpCodes) -> Self { Thunk { thunk: RefCell::new(_Thunk::Code(opcodes, OnceCell::new())), } } pub fn capture(&self, env: &'vm VmEnv<'jit, 'vm>) { if let _Thunk::Code(_, envcell) = &*self.thunk.borrow() { envcell.get_or_init(|| env); } } pub fn force(&self, vm: &'vm VM<'jit>) -> Result<&Value<'jit, 'vm>> { use _Thunk::*; match &*self.thunk.borrow() { Value(_) => { return Ok(match unsafe { &*(&*self.thunk.borrow() as *const _) } { Value(value) => value, _ => unreachable!(), }); } SuspendedFrom(from) => { return Err(Error::EvalError(format!( "thunk {:p} already suspended from {from:p} (infinite recursion encountered)", self as *const Thunk ))); } Code(..) => (), } let (opcodes, env) = std::mem::replace( &mut *self.thunk.borrow_mut(), _Thunk::SuspendedFrom(self as *const Thunk), ) .unwrap_code(); let env = env.get().unwrap(); let value = vm.eval(opcodes.iter().copied(), env)?; let _ = std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Value(value)); Ok(match unsafe { &*(&*self.thunk.borrow() as *const _) } { Value(value) => value, _ => unreachable!(), }) } 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 } }