feat: recursive builtins

This commit is contained in:
2025-05-05 16:43:14 +08:00
parent bd783f1b96
commit 550ad54f30
11 changed files with 134 additions and 126 deletions

View File

@@ -1,7 +1,6 @@
mod env;
mod stack;
mod vm;
mod vmthunk;
#[cfg(test)]
mod test;

View File

@@ -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!() } },
);
}

View File

@@ -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<p::Value> {
let vm = VM::new(prog.thunks);
@@ -18,20 +17,20 @@ pub fn run(prog: Program) -> Result<p::Value> {
}
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<Value> {
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<Env>) -> Result<Value> {
@@ -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(

View File

@@ -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<Option<Arc<Env>>>,
}
#[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<Env>) {
*self.env.borrow_mut() = Some(env);
}
pub fn force(&self, vm: &VM) -> Result<Value> {
{
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<Value> {
match &*self.thunk.borrow() {
_VmThunk::Value(value) => Some(value.clone()),
_ => None,
}
}
}