From 550ad54f303ad2850821b1a8144d1bdc64afecf4 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 5 May 2025 16:43:14 +0800 Subject: [PATCH] feat: recursive builtins --- src/bin/repl.rs | 2 +- src/builtins/mod.rs | 14 +++-- src/compile/ir.rs | 21 -------- src/ty/internal/attrset.rs | 4 ++ src/ty/internal/func.rs | 8 +-- src/ty/internal/mod.rs | 105 ++++++++++++++++++++++++++++++++++--- src/ty/public/mod.rs | 7 ++- src/vm/mod.rs | 1 - src/vm/test.rs | 2 +- src/vm/vm.rs | 22 ++++---- src/vm/vmthunk.rs | 74 -------------------------- 11 files changed, 134 insertions(+), 126 deletions(-) delete mode 100644 src/vm/vmthunk.rs diff --git a/src/bin/repl.rs b/src/bin/repl.rs index 81baea6..9e3b0e7 100644 --- a/src/bin/repl.rs +++ b/src/bin/repl.rs @@ -6,7 +6,7 @@ use nixjit::*; fn main() -> Result<()> { let mut rl = DefaultEditor::new()?; loop { - let readline = rl.readline(">> "); + let readline = rl.readline("nixjit-repl> "); match readline { Ok(expr) => { if expr.trim().is_empty() { diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 6dc6a82..826df36 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,7 +1,8 @@ +use std::cell::RefCell; use std::sync::Arc; use crate::ty::common::Symbol; -use crate::ty::internal::{AttrSet, Const, PrimOp, Value}; +use crate::ty::internal::{Const, PrimOp, RecAttrSet, Thunk, Value, _Thunk}; use crate::vm::Env; pub fn env() -> Arc { @@ -42,15 +43,20 @@ pub fn env() -> Arc { }), ]; - let mut builtins = AttrSet::empty(); + let builtins_env = Env::empty(); + let map = builtins_env.enter_rec(); for primop in primops { env.insert( Symbol::from(format!("__{}", primop.name)), Value::PrimOp(primop.clone()), ); - builtins.push_attr(Symbol::from(primop.name), Value::PrimOp(primop)); + map.borrow_mut().insert_mut(Symbol::from(primop.name), Value::PrimOp(primop)); } + let builtins = Value::RecAttrSet(RecAttrSet::from_inner(map.clone())); + let thunk= Thunk { thunk: RefCell::new(_Thunk::Value(Box::new(builtins.clone()))), env: RefCell::default() }; + thunk.capture(Arc::new(builtins_env)); + map.borrow_mut().insert_mut(Symbol::from("builtins"), Value::Thunk(thunk)); - env.insert(Symbol::from("builtins"), Value::AttrSet(builtins)); + env.insert(Symbol::from("builtins"), builtins); env } diff --git a/src/compile/ir.rs b/src/compile/ir.rs index 7ddbe18..47cf446 100644 --- a/src/compile/ir.rs +++ b/src/compile/ir.rs @@ -337,27 +337,6 @@ pub enum Param { }, } -impl Into for Param { - fn into(self) -> i::Param { - use i::Param::*; - match self { - Param::Ident(ident) => Ident(ident), - Param::Formals { - formals, - ellipsis, - alias, - } => Formals { - formals: formals - .into_iter() - .map(|(ident, default)| (ident, default.map(|thunk| thunk.idx))) - .collect(), - ellipsis, - alias, - }, - } - } -} - trait Downgrade where Self: Sized, diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index faf255c..28e27d8 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -146,6 +146,10 @@ impl RecAttrSet { self.data.borrow().clone() } + pub fn from_inner(data: Arc>>) -> Self { + RecAttrSet { data } + } + pub fn force_deep(&mut self, vm: &VM) -> Result<()> { let mut map: Vec<_> = self .data diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index b5c2a26..6cb7a82 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -2,7 +2,7 @@ use ecow::EcoString; use itertools::Itertools; use rpds::HashTrieMap; -use crate::bytecode::{OpCodes, ThunkIdx}; +use crate::bytecode::OpCodes; use crate::ty::internal::{Thunk, Value}; use crate::vm::{CapturedEnv, VM}; @@ -11,7 +11,7 @@ use crate::vm::{CapturedEnv, VM}; pub enum Param { Ident(EcoString), Formals { - formals: Vec<(EcoString, Option)>, + formals: Vec<(EcoString, Option)>, ellipsis: bool, alias: Option, }, @@ -60,7 +60,7 @@ impl Func { for (formal, default) in formals { let arg = arg .select(formal.clone().into()) - .or_else(|| default.map(|idx| Value::Thunk(Thunk(idx)))) + .or_else(|| default.map(|thunk| Value::Thunk(thunk))) .unwrap(); new.insert_mut(formal.into(), arg); } @@ -89,7 +89,7 @@ impl Func { formals.push((param, None)); } - pub fn push_default_param(&mut self, default: ThunkIdx) { + pub fn push_default_param(&mut self, default: Thunk) { let Param::Formals { formals, .. } = self.param.as_mut().unwrap() else { panic!() }; diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index c4d5605..9df5f29 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,12 +1,17 @@ -use anyhow::Result; -use derive_more::{Constructor, IsVariant, Unwrap}; +use std::cell::RefCell; +use std::sync::Arc; +use anyhow::{anyhow, Result}; +use derive_more::{IsVariant, Unwrap}; + +use super::common::Catchable; use super::common as c; use super::public as p; use c::Symbol; -use crate::vm::VM; +use crate::vm::{VM, Env}; +use crate::bytecode::OpCodes; mod attrset; mod cnst; @@ -26,8 +31,75 @@ pub trait ToPublic { fn to_public(self, vm: &VM) -> p::Value; } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Constructor)] -pub struct Thunk(usize); +#[derive(Debug, Clone)] +pub struct Thunk { + pub thunk: RefCell<_Thunk>, + pub env: RefCell>>, +} + +#[derive(Debug, IsVariant, Unwrap, Clone)] +pub enum _Thunk { + Code(OpCodes), + SuspendedFrom(*const Thunk), + Value(Box), +} + +impl Thunk { + pub fn new(opcodes: OpCodes) -> Thunk { + Thunk { + thunk: RefCell::new(_Thunk::Code(opcodes)), + env: RefCell::new(None), + } + } + + pub fn unwrap_code(&self) -> OpCodes { + self.thunk.borrow().clone().unwrap_code() + } + + pub fn capture(&self, env: Arc) { + *self.env.borrow_mut() = Some(env); + } + + pub fn force(&self, vm: &VM) -> Result { + { + match &*self.thunk.borrow() { + _Thunk::Value(value) => return Ok(value.as_ref().clone()), + _Thunk::SuspendedFrom(from) => { + return Err(anyhow!( + "already suspended from {from:p} (infinite recursion encountered)" + )); + } + _Thunk::Code(_) => (), + } + } + let opcodes = std::mem::replace( + &mut *self.thunk.borrow_mut(), + _Thunk::SuspendedFrom(self as *const Thunk), + ) + .unwrap_code(); + let value = vm + .eval(opcodes, self.env.borrow().clone().unwrap()) + .unwrap(); + let _ = std::mem::replace( + &mut *self.thunk.borrow_mut(), + _Thunk::Value(value.clone().into()), + ); + Ok(value) + } + + pub fn value(&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 + } +} #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] pub enum Value { @@ -104,6 +176,21 @@ impl Value { use Value::Const as VmConst; impl Value { + pub fn typename(&self) -> &'static str { + use Value::*; + match self { + Const(_) => todo!(), + Thunk(_) => "thunk", + AttrSet(_) => "set", + RecAttrSet(_) => "set", + List(_) => "list", + Catchable(_) => todo!(), + PrimOp(_) => "lambda", + PartialPrimOp(_) => "lambda", + Func(_) => "lambda" + } + } + pub fn callable(&self) -> bool { match self { Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, @@ -133,6 +220,7 @@ impl Value { } func } + x @ Catchable(_) => x, _ => todo!(), } } @@ -141,6 +229,7 @@ impl Value { use Const::*; match self { VmConst(Bool(bool)) => VmConst(Bool(!bool)), + x @ Value::Catchable(_) => x, _ => todo!(), } } @@ -291,7 +380,7 @@ impl Value { ))))); *self = val; } else { - todo!() + *self = Value::Catchable(Catchable::new(Some(format!("cannot select from {:?}", self.typename())))) } self } @@ -333,7 +422,7 @@ impl Value { pub fn force(&mut self, vm: &VM) -> Result<&mut Self> { if let Value::Thunk(thunk) = self { - let value = vm.get_thunk_value(thunk.0)?; + let value = thunk.force(vm)?; *self = value } Ok(self) @@ -342,7 +431,7 @@ impl Value { pub fn force_deep(&mut self, vm: &VM) -> Result<&mut Self> { match self { Value::Thunk(thunk) => { - let mut value = vm.get_thunk_value(thunk.0)?; + let mut value = thunk.force(vm)?; value.force_deep(vm)?; *self = value; } diff --git a/src/ty/public/mod.rs b/src/ty/public/mod.rs index e5346b3..e298f50 100644 --- a/src/ty/public/mod.rs +++ b/src/ty/public/mod.rs @@ -16,9 +16,14 @@ pub struct AttrSet { impl Debug for AttrSet { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use Value::*; write!(f, "{{ ")?; for (k, v) in self.data.iter() { - write!(f, "{k:?} = {v:?}; ")?; + match v { + List(_) => write!(f, "{k:?} = [ ... ]; ")?, + AttrSet(_) => write!(f, "{k:?} = {{ ... }}; ")?, + v => write!(f, "{k:?} = {v:?}; ")? + } } write!(f, "}}") } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index d667f5b..9d423a7 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,7 +1,6 @@ mod env; mod stack; mod vm; -mod vmthunk; #[cfg(test)] mod test; diff --git a/src/vm/test.rs b/src/vm/test.rs index 2e5bce4..f6cd871 100644 --- a/src/vm/test.rs +++ b/src/vm/test.rs @@ -149,7 +149,7 @@ fn test_attrs() { ); test_expr("{ a.b = 1; } ? a.b", boolean!(true)); test_expr( - "{ a.b = 1; } // { a.c = 2 }", + "{ a.b = 1; } // { a.c = 2; }", attrs! { symbol!("a") => attrs!{ symbol!("c") => thunk!() } }, ); } diff --git a/src/vm/vm.rs b/src/vm/vm.rs index 9a2463e..b71e334 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -10,7 +10,6 @@ use crate::ty::public as p; use super::env::Env; use super::stack::{STACK_SIZE, Stack}; -use super::vmthunk::*; pub fn run(prog: Program) -> Result { let vm = VM::new(prog.thunks); @@ -18,20 +17,20 @@ pub fn run(prog: Program) -> Result { } pub struct VM { - thunks: Box<[VmThunk]>, + thunks: Box<[Thunk]>, } impl VM { fn new(thunks: Thunks) -> Self { let thunks = thunks .into_iter() - .map(|bytecode::Thunk { opcodes }| VmThunk::new(opcodes)) + .map(|bytecode::Thunk { opcodes }| Thunk::new(opcodes)) .collect(); VM { thunks } } - pub fn get_thunk_value(&self, idx: usize) -> Result { - self.thunks.get(idx).unwrap().force(self) + pub fn get_thunk(&self, idx: usize) -> Thunk { + self.thunks[idx].clone() } pub fn eval(&self, opcodes: OpCodes, env: Arc) -> Result { @@ -63,10 +62,10 @@ impl VM { OpCode::Const { value } => stack.push(Value::Const(value))?, OpCode::LoadThunk { idx } => { self.thunks[idx].capture(env); - stack.push(Value::Thunk(Thunk::new(idx)))? + stack.push(Value::Thunk(self.thunks[idx].clone()))? } OpCode::LoadValue { idx } => { - stack.push(self.get_thunk_value(idx)?)?; + stack.push(self.get_thunk(idx).force(self)?)?; } OpCode::ForceValue => { stack.tos_mut()?.force(self)?; @@ -116,7 +115,7 @@ impl VM { .tos_mut()? .as_mut() .unwrap_func() - .push_default_param(idx); + .push_default_param(self.get_thunk(idx)); } OpCode::SetEllipsis => { stack.tos_mut()?.as_mut().unwrap_func().set_ellipsis(); @@ -187,6 +186,7 @@ impl VM { let default = stack.pop()?; stack .tos_mut()? + .force(self)? .select_with_default(Symbol::new(sym), default); } OpCode::SelectDynamic => { @@ -202,16 +202,16 @@ impl VM { val.force(self)?; val.coerce_to_string(); let sym = val.unwrap_const().unwrap_string().into(); - stack.tos_mut()?.select_with_default(sym, default); + stack.tos_mut()?.force(self)?.select_with_default(sym, default); } OpCode::HasAttr { sym } => { - stack.tos_mut()?.has_attr(Symbol::new(sym)); + stack.tos_mut()?.force(self)?.has_attr(Symbol::new(sym)); } OpCode::HasDynamicAttr => { let mut val = stack.pop().unwrap(); val.coerce_to_string(); let sym = val.unwrap_const().unwrap_string().into(); - stack.tos_mut()?.has_attr(sym); + stack.tos_mut()?.force(self)?.has_attr(sym); } OpCode::LookUp { sym } => { stack.push( diff --git a/src/vm/vmthunk.rs b/src/vm/vmthunk.rs deleted file mode 100644 index 92e9e58..0000000 --- a/src/vm/vmthunk.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::cell::RefCell; -use std::sync::Arc; - -use anyhow::{Result, anyhow}; -use derive_more::{IsVariant, Unwrap}; - -use crate::bytecode::OpCodes; - -use super::env::Env; -use super::vm::VM; -use crate::ty::internal::Value; - -pub struct VmThunk { - thunk: RefCell<_VmThunk>, - env: RefCell>>, -} - -#[derive(IsVariant, Unwrap, Clone)] -enum _VmThunk { - Code(OpCodes), - SuspendedFrom(*const VmThunk), - Value(Value), -} - -impl VmThunk { - pub fn new(opcodes: OpCodes) -> VmThunk { - VmThunk { - thunk: RefCell::new(_VmThunk::Code(opcodes)), - env: RefCell::new(None), - } - } - - pub fn unwrap_code(&self) -> OpCodes { - self.thunk.borrow().clone().unwrap_code() - } - - pub fn capture(&self, env: Arc) { - *self.env.borrow_mut() = Some(env); - } - - pub fn force(&self, vm: &VM) -> Result { - { - match &*self.thunk.borrow() { - _VmThunk::Value(value) => return Ok(value.clone()), - _VmThunk::SuspendedFrom(from) => { - return Err(anyhow!( - "already suspended from {from:p} (infinite recursion encountered)" - )); - } - _VmThunk::Code(_) => (), - } - } - let opcodes = std::mem::replace( - &mut *self.thunk.borrow_mut(), - _VmThunk::SuspendedFrom(self as *const VmThunk), - ) - .unwrap_code(); - let value = vm - .eval(opcodes, self.env.borrow().clone().unwrap()) - .unwrap(); - let _ = std::mem::replace( - &mut *self.thunk.borrow_mut(), - _VmThunk::Value(value.clone()), - ); - Ok(value) - } - - pub fn value(&self) -> Option { - match &*self.thunk.borrow() { - _VmThunk::Value(value) => Some(value.clone()), - _ => None, - } - } -}