feat: recursive builtins
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
mod env;
|
||||
mod stack;
|
||||
mod vm;
|
||||
mod vmthunk;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
@@ -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!() } },
|
||||
);
|
||||
}
|
||||
|
||||
22
src/vm/vm.rs
22
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<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(
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user