feat: refactor

This commit is contained in:
2025-05-03 16:22:05 +08:00
parent 43cfb9be62
commit f78c516d17
19 changed files with 361 additions and 345 deletions

5
Cargo.lock generated
View File

@@ -228,15 +228,14 @@ dependencies = [
] ]
[[package]] [[package]]
name = "nix-rs" name = "nixjit"
version = "0.1.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"crossbeam-channel", "crossbeam-channel",
"derive_more", "derive_more",
"ecow", "ecow",
"itertools", "itertools",
"num_cpus",
"once_cell", "once_cell",
"rayon", "rayon",
"rnix", "rnix",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "nix-rs" name = "nixjit"
version = "0.1.0" version = "0.0.0"
edition = "2024" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,6 +14,5 @@ tokio = { version = "1.38", features = [ "full" ] }
rpds = "1.1" rpds = "1.1"
derive_more = "0.99" derive_more = "0.99"
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
num_cpus = "1.0"
ecow = "0.2" ecow = "0.2"
once_cell = "1.19" once_cell = "1.19"

View File

@@ -3,39 +3,55 @@ use derive_more::Constructor;
use super::vm::Env; use super::vm::Env;
use super::vm::Symbol; use super::vm::Symbol;
use super::vm::Value; use super::vm::Value;
use crate::bytecode::Const; use crate::value::Const;
pub fn env() -> Env { pub fn env() -> Env {
let mut env = Env::empty(); let mut env = Env::empty();
env.insert(Symbol::from("true"), Value::Const(Const::Bool(true))); env.insert(Symbol::from("true"), Value::Const(Const::Bool(true)));
env.insert(Symbol::from("false"), Value::Const(Const::Bool(false))); env.insert(Symbol::from("false"), Value::Const(Const::Bool(false)));
env.insert(Symbol::from("__add"), Value::PrimOp(PrimOp::new(2, |args| { env.insert(
let [first, second]: [Value; 2] = args.try_into().unwrap(); Symbol::from("__add"),
first.add(second) Value::PrimOp(PrimOp::new("add", 2, |args| {
}))); let [first, second]: [Value; 2] = args.try_into().unwrap();
env.insert(Symbol::from("__sub"), Value::PrimOp(PrimOp::new(2, |args| { first.add(second)
let [first, second]: [Value; 2] = args.try_into().unwrap(); })),
first.add(second.neg()) );
}))); env.insert(
env.insert(Symbol::from("__mul"), Value::PrimOp(PrimOp::new(2, |args| { Symbol::from("__sub"),
let [first, second]: [Value; 2] = args.try_into().unwrap(); Value::PrimOp(PrimOp::new("sub", 2, |args| {
first.mul(second) let [first, second]: [Value; 2] = args.try_into().unwrap();
}))); first.add(second.neg())
env.insert(Symbol::from("__div"), Value::PrimOp(PrimOp::new(2, |args| { })),
let [first, second]: [Value; 2] = args.try_into().unwrap(); );
first.div(second) env.insert(
}))); Symbol::from("__mul"),
env.insert(Symbol::from("__lessThan"), Value::PrimOp(PrimOp::new(2, |args| { Value::PrimOp(PrimOp::new("mul", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap(); let [first, second]: [Value; 2] = args.try_into().unwrap();
first.lt(second) first.mul(second)
}))); })),
);
env.insert(
Symbol::from("__div"),
Value::PrimOp(PrimOp::new("div", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.div(second)
})),
);
env.insert(
Symbol::from("__lessThan"),
Value::PrimOp(PrimOp::new("lessThan", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.lt(second)
})),
);
env env
} }
#[derive(Debug, Clone, Constructor)] #[derive(Debug, Clone, Constructor)]
pub struct PrimOp { pub struct PrimOp {
name: &'static str,
arity: u8, arity: u8,
func: fn(Vec<Value>) -> Value, func: fn(Vec<Value>) -> Value,
} }
@@ -49,7 +65,12 @@ impl PartialEq for PrimOp {
impl PrimOp { impl PrimOp {
pub fn call(self, args: Vec<Value>) -> Value { pub fn call(self, args: Vec<Value>) -> Value {
if (args.len() as u8) < self.arity { if (args.len() as u8) < self.arity {
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - args.len() as u8, args, func: self.func }) Value::PartialPrimOp(PartialPrimOp {
name: self.name,
arity: self.arity - args.len() as u8,
args,
func: self.func,
})
} else if args.len() as u8 == self.arity { } else if args.len() as u8 == self.arity {
(self.func)(args) (self.func)(args)
} else { } else {
@@ -58,11 +79,12 @@ impl PrimOp {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PartialPrimOp { pub struct PartialPrimOp {
name: &'static str,
arity: u8, arity: u8,
args: Vec<Value>, args: Vec<Value>,
func: fn(Vec<Value>) -> Value func: fn(Vec<Value>) -> Value,
} }
impl PartialEq for PartialPrimOp { impl PartialEq for PartialPrimOp {
@@ -76,7 +98,12 @@ impl PartialPrimOp {
let len = args.len() as u8; let len = args.len() as u8;
self.args.extend(args); self.args.extend(args);
if len < self.arity { if len < self.arity {
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - len, args: self.args, func: self.func }) Value::PartialPrimOp(PartialPrimOp {
name: self.name,
arity: self.arity - len,
args: self.args,
func: self.func,
})
} else if len == self.arity { } else if len == self.arity {
(self.func)(self.args) (self.func)(self.args)
} else { } else {

View File

@@ -1,11 +1,10 @@
use std::hash::{Hash, Hasher}; use std::hash::Hash;
use ecow::EcoString; use ecow::EcoString;
use anyhow::Error;
use derive_more::{IsVariant, Unwrap};
use crate::slice::Slice; use crate::value::Const;
use crate::value::Func;
type Slice<T> = Box<[T]>;
pub type ThunkIdx = usize; pub type ThunkIdx = usize;
pub type ConstIdx = usize; pub type ConstIdx = usize;
@@ -23,122 +22,6 @@ pub struct Thunk {
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Arg {} pub enum Arg {}
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Func(Func),
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}
impl Hash for Const {
fn hash<H: Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Bool(b) => b.hash(state),
Int(int) => int.hash(state),
Float(float) => float.to_bits().hash(state),
String(string) => string.hash(state),
Func(func) => func.hash(state),
}
}
}
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum OpCode { pub enum OpCode {
/// load a constant onto stack /// load a constant onto stack
@@ -151,60 +34,58 @@ pub enum OpCode {
LoadValue { idx: ThunkIdx }, LoadValue { idx: ThunkIdx },
/// force TOS to value /// force TOS to value
ForceValue, ForceValue,
/// [ ... func, args @ .. ] call func with `arity` numbers of arg /// [ .. func args @ .. ] consume (`arity` + 2) elements, call `func` with args` of length `arity`
/// Example: __add 1 2 => [ LookUp("__add") Const(1) Const(2) Call(2) ]
Call { arity: usize }, Call { arity: usize },
/// assert TOS is true then consume it /// consume 1 element, assert TOS is true
Assert, Assert,
/// jump forward /// jump forward
Jmp { step: usize }, Jmp { step: usize },
/// [ ... cond ] if (cond) is true, then jump forward /// [ .. cond ] consume 1 element, if `cond`` is true, then jump forward
JmpIfTrue { step: usize }, JmpIfTrue { step: usize },
/// [ ... cond ] if (cond) is false, then jump forward /// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
JmpIfFalse { step: usize }, JmpIfFalse { step: usize },
/// push an empty attribute set onto stack /// push an empty attribute set onto stack
AttrSet, AttrSet,
/// push an empty recursive attribute set onto stack /// push an empty recursive attribute set onto stack
RecAttrSet, RecAttrSet,
/// [ ... set, value ] push the static kv pair (name, (value)) into (set) /// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
PushStaticAttr { name: EcoString }, PushStaticAttr { name: EcoString },
/// [ ... set, name, value ] push the dynamic kv pair ((name), (value)) in to (set) /// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
PushDynamicAttr, PushDynamicAttr,
/// push an empty list onto stack /// push an empty list onto stack
List, List,
/// [ ... list, elem ] push (elem) into (list) /// [ .. list elem ] consume 1 element, push `elem` into `list`
PushElem, PushElem,
/// [ ... a, b ] perform a binary operation ((a) `op` (b)) /// [ .. a b ] consume 2 elements, perform a string concatenation `a` + `b`
BinOp { op: BinOp },
/// [ ... a ] perform a unary operation (`op` (a))
UnOp { op: UnOp },
/// TODO:
ConcatString, ConcatString,
/// TODO: /// [ .. a b ] consume 2 elements, perform a binary operation `a` `op` `b`
BinOp { op: BinOp },
/// [ .. a ] consume 1 element, perform a unary operation `op` `a`
UnOp { op: UnOp },
/// set TOS to the bool value of whether TOS contains `sym`
HasAttr { sym: EcoString }, HasAttr { sym: EcoString },
/// TODO: /// [ .. set sym ] consume 2 elements, set TOS to the bool value of whether `set` contains `sym`
HasDynamicAttr, HasDynamicAttr,
// HasAttr { arity: usize }, /// [ .. set ] select `sym` from `set`
/// TODO:
Select { sym: EcoString }, Select { sym: EcoString },
// Select { arity: usize },
/// TODO: /// TODO:
SelectDynamic, SelectWithDefault { sym: EcoString },
// SelectDynamic { arity: usize },
/// TODO: /// TODO:
SelectOrEmpty { sym: EcoString }, SelectOrEmpty { sym: EcoString },
/// TODO: /// TODO:
SelectDynamicOrEmpty, SelectDynamic,
/// TODO: /// TODO:
SelectWithDefault { sym: EcoString }, SelectDynamicOrEmpty,
/// TODO: /// TODO:
SelectDynamicWithDefault, SelectDynamicWithDefault,
/// enter the environment of the attribute set at TOS /// enter the environment of the attribute set at TOS
EnterEnv, EnterEnv,
/// exit the envrironment /// exit current envrironment
LeaveEnv, LeaveEnv,
/// return a value /// return a value
Ret, Ret,
/// no-op /// no operation
NoOp, NoOp,
} }

View File

@@ -1,4 +1,5 @@
use crate::bytecode::*; use crate::bytecode::*;
use crate::value::*;
use super::ir; use super::ir;
@@ -93,6 +94,27 @@ impl Compile for ir::Thunk {
impl Compile for ir::Attrs { impl Compile for ir::Attrs {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
if self.rec {
comp.push(OpCode::AttrSet);
for dynamic in self.dyns.clone() {
dynamic.0.compile(comp);
dynamic.1.compile(comp);
comp.push(OpCode::PushDynamicAttr)
}
comp.push(OpCode::EnterEnv);
comp.push(OpCode::AttrSet);
for stc in self.stcs {
stc.1.compile(comp);
comp.push(OpCode::PushStaticAttr { name: stc.0 });
}
for dynamic in self.dyns {
dynamic.0.compile(comp);
dynamic.1.compile(comp);
comp.push(OpCode::PushDynamicAttr)
}
comp.push(OpCode::LeaveEnv);
return
}
comp.push(OpCode::AttrSet); comp.push(OpCode::AttrSet);
for stc in self.stcs { for stc in self.stcs {
stc.1.compile(comp); stc.1.compile(comp);
@@ -106,29 +128,6 @@ impl Compile for ir::Attrs {
} }
} }
impl Compile for ir::RecAttrs {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::AttrSet);
for dynamic in self.dyns.clone() {
dynamic.0.compile(comp);
dynamic.1.compile(comp);
comp.push(OpCode::PushDynamicAttr)
}
comp.push(OpCode::EnterEnv);
comp.push(OpCode::AttrSet);
for stc in self.stcs {
stc.1.compile(comp);
comp.push(OpCode::PushStaticAttr { name: stc.0 });
}
for dynamic in self.dyns {
dynamic.0.compile(comp);
dynamic.1.compile(comp);
comp.push(OpCode::PushDynamicAttr)
}
comp.push(OpCode::LeaveEnv);
}
}
impl Compile for ir::List { impl Compile for ir::List {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::List); comp.push(OpCode::List);
@@ -144,8 +143,12 @@ impl Compile for ir::UnOp {
use ir::UnOpKind::*; use ir::UnOpKind::*;
match self.kind { match self.kind {
Neg => { Neg => {
comp.push(OpCode::LookUp { sym: "__sub".into() }); comp.push(OpCode::LookUp {
comp.push(OpCode::Const { value: Const::Int(0) }); sym: "__sub".into(),
});
comp.push(OpCode::Const {
value: Const::Int(0),
});
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
} }
@@ -167,13 +170,17 @@ impl Compile for ir::BinOp {
comp.push(OpCode::BinOp { op: BinOp::Add }); comp.push(OpCode::BinOp { op: BinOp::Add });
} }
Mul => { Mul => {
comp.push(OpCode::LookUp { sym: "__mul".into() }); comp.push(OpCode::LookUp {
sym: "__mul".into(),
});
self.lhs.compile(comp); self.lhs.compile(comp);
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
} }
Div => { Div => {
comp.push(OpCode::LookUp { sym: "__div".into() }); comp.push(OpCode::LookUp {
sym: "__div".into(),
});
self.lhs.compile(comp); self.lhs.compile(comp);
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
@@ -194,7 +201,9 @@ impl Compile for ir::BinOp {
comp.push(OpCode::BinOp { op: BinOp::Eq }); comp.push(OpCode::BinOp { op: BinOp::Eq });
} }
Lt => { Lt => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() }); comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.lhs.compile(comp); self.lhs.compile(comp);
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
@@ -211,7 +220,9 @@ impl Compile for ir::BinOp {
} }
Sub => { Sub => {
comp.push(OpCode::LookUp { sym: "__sub".into() }); comp.push(OpCode::LookUp {
sym: "__sub".into(),
});
self.lhs.compile(comp); self.lhs.compile(comp);
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
@@ -229,20 +240,26 @@ impl Compile for ir::BinOp {
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
Gt => { Gt => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() }); comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.rhs.compile(comp); self.rhs.compile(comp);
self.lhs.compile(comp); self.lhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
} }
Leq => { Leq => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() }); comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.rhs.compile(comp); self.rhs.compile(comp);
self.lhs.compile(comp); self.lhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
Geq => { Geq => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() }); comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.lhs.compile(comp); self.lhs.compile(comp);
self.rhs.compile(comp); self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 }); comp.push(OpCode::Call { arity: 2 });
@@ -372,19 +389,19 @@ impl Compile for ir::Assert {
} }
impl Compile for ir::Func { impl Compile for ir::Func {
fn compile(self, comp: &mut Compiler) { fn compile(self, _comp: &mut Compiler) {
todo!() todo!()
} }
} }
impl Compile for ir::Call { impl Compile for ir::Call {
fn compile(self, comp: &mut Compiler) { fn compile(self, _comp: &mut Compiler) {
todo!() todo!()
} }
} }
impl Compile for ir::Path { impl Compile for ir::Path {
fn compile(self, comp: &mut Compiler) { fn compile(self, _comp: &mut Compiler) {
todo!() todo!()
} }
} }

View File

@@ -1,17 +1,14 @@
// TODO: Error Handling
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use rnix::ast::{self, Expr};
use ecow::EcoString; use ecow::EcoString;
use rnix::ast::{self, Expr};
use crate::bytecode::{Const as ByteCodeConst, ConstIdx, Consts, ThunkIdx}; use crate::bytecode::{ConstIdx, Consts, ThunkIdx};
use crate::slice::Slice; use crate::value::Const as Const_;
use super::compile::*; use super::compile::*;
use super::env::IrEnv; use super::env::IrEnv;
use super::symtable::*;
pub fn downgrade(expr: Expr) -> Result<Downgraded> { pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let mut state = DowngradeState::new(); let mut state = DowngradeState::new();
@@ -112,8 +109,7 @@ macro_rules! ir {
} }
ir! { ir! {
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> }, Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair>, rec: bool },
RecAttrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
List => { items: Vec<Ir> }, List => { items: Vec<Ir> },
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> }, HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind }, BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
@@ -127,7 +123,7 @@ ir! {
With => { namespace: Box<Ir>, expr: Box<Ir> }, With => { namespace: Box<Ir>, expr: Box<Ir> },
Assert => { assertion: Box<Ir>, expr: Box<Ir> }, Assert => { assertion: Box<Ir>, expr: Box<Ir> },
ConcatStrings => { parts: Vec<Ir> }, ConcatStrings => { parts: Vec<Ir> },
Const => { value: ByteCodeConst }, Const => { value: Const_ },
Var => { sym: EcoString }, Var => { sym: EcoString },
#[derive(Copy)] #[derive(Copy)]
Thunk => { idx: ThunkIdx }, Thunk => { idx: ThunkIdx },
@@ -165,23 +161,21 @@ pub struct DowngradeError {
} }
pub struct DowngradeState { pub struct DowngradeState {
sym_table: SymTable,
envs: Vec<Env>, envs: Vec<Env>,
thunks: Vec<Ir>, thunks: Vec<Ir>,
consts: Vec<ByteCodeConst>, consts: Vec<Const_>,
consts_table: HashMap<ByteCodeConst, ConstIdx>, consts_table: HashMap<Const_, ConstIdx>,
} }
pub struct Downgraded { pub struct Downgraded {
pub top_level: Ir, pub top_level: Ir,
pub consts: Consts, pub consts: Consts,
pub thunks: Slice<Ir>, pub thunks: Box<[Ir]>,
} }
impl DowngradeState { impl DowngradeState {
fn new() -> DowngradeState { fn new() -> DowngradeState {
DowngradeState { DowngradeState {
sym_table: SymTable::new(),
envs: Vec::new(), envs: Vec::new(),
thunks: Vec::new(), thunks: Vec::new(),
consts: Vec::new(), consts: Vec::new(),
@@ -214,6 +208,7 @@ impl Attrs {
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value)) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
} else { } else {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
@@ -224,6 +219,7 @@ impl Attrs {
} }
Attr::Strs(string) => { Attr::Strs(string) => {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
@@ -233,6 +229,7 @@ impl Attrs {
} }
Attr::Dynamic(dynamic) => { Attr::Dynamic(dynamic) => {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
@@ -317,8 +314,8 @@ pub enum BinOpKind {
impl From<ast::BinOpKind> for BinOpKind { impl From<ast::BinOpKind> for BinOpKind {
fn from(op: ast::BinOpKind) -> Self { fn from(op: ast::BinOpKind) -> Self {
use ast::BinOpKind as astkind;
use BinOpKind::*; use BinOpKind::*;
use ast::BinOpKind as astkind;
match op { match op {
astkind::Concat => Con, astkind::Concat => Con,
astkind::Update => Upd, astkind::Update => Upd,
@@ -366,7 +363,7 @@ pub enum Param {
trait Downgrade trait Downgrade
where where
Self: Sized Self: Sized,
{ {
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir>; fn downgrade(self, state: &mut DowngradeState) -> Result<Ir>;
} }
@@ -427,9 +424,11 @@ impl Downgrade for ast::Path {
.parts() .parts()
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => Const { value: lit.to_string().into() } ast::InterpolPart::Literal(lit) => Const {
.ir() value: lit.to_string().into(),
.ok(), }
.ir()
.ok(),
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state) interpol.expr().unwrap().downgrade(state)
} }
@@ -455,9 +454,7 @@ impl Downgrade for ast::Str {
.normalized_parts() .normalized_parts()
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => { ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(),
Const { value: lit.into() }.ir().ok()
}
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state) interpol.expr().unwrap().downgrade(state)
} }
@@ -472,11 +469,17 @@ impl Downgrade for ast::Str {
} }
impl Downgrade for ast::Literal { impl Downgrade for ast::Literal {
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> { fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> {
match self.kind() { match self.kind() {
ast::LiteralKind::Integer(int) => Const { value: int.value().unwrap().into() }, ast::LiteralKind::Integer(int) => Const {
ast::LiteralKind::Float(float) => Const { value: float.value().unwrap().into() }, value: int.value().unwrap().into(),
ast::LiteralKind::Uri(uri) => Const { value: uri.to_string().into() }, },
ast::LiteralKind::Float(float) => Const {
value: float.value().unwrap().into(),
},
ast::LiteralKind::Uri(uri) => Const {
value: uri.to_string().into(),
},
} }
.ir() .ir()
.ok() .ok()
@@ -486,7 +489,7 @@ impl Downgrade for ast::Literal {
impl Downgrade for ast::Ident { impl Downgrade for ast::Ident {
fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> { fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> {
Var { Var {
sym: self.to_string().into() sym: self.to_string().into(),
} }
.ir() .ir()
.ok() .ok()
@@ -584,12 +587,7 @@ impl Downgrade for ast::LetIn {
let body = self.body().unwrap(); let body = self.body().unwrap();
let attrs = downgrade_has_entry(self, true, state)?; let attrs = downgrade_has_entry(self, true, state)?;
let expr = body.downgrade(state)?.boxed(); let expr = body.downgrade(state)?.boxed();
Let { Let { attrs, expr }.ir().ok()
attrs,
expr
}
.ir()
.ok()
} }
} }
@@ -636,9 +634,7 @@ impl Downgrade for ast::Apply {
fn downgrade_param(param: ast::Param, state: &mut DowngradeState) -> Result<Param> { fn downgrade_param(param: ast::Param, state: &mut DowngradeState) -> Result<Param> {
match param { match param {
ast::Param::IdentParam(ident) => { ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())),
Ok(Param::Ident(ident.to_string().into()))
}
ast::Param::Pattern(pattern) => downgrade_pattern(pattern, state), ast::Param::Pattern(pattern) => downgrade_pattern(pattern, state),
} }
} }
@@ -677,6 +673,7 @@ fn downgrade_has_entry(
) -> Result<Attrs> { ) -> Result<Attrs> {
let entires = has_entry.entries(); let entires = has_entry.entries();
let mut attrs = Attrs { let mut attrs = Attrs {
rec,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
@@ -684,9 +681,7 @@ fn downgrade_has_entry(
for entry in entires { for entry in entires {
match entry { match entry {
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, state)?, ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, state)?,
ast::Entry::AttrpathValue(value) => { ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, state)?,
downgrade_attrpathvalue(value, &mut attrs, state)?
}
} }
} }
@@ -710,7 +705,7 @@ fn downgrade_inherit(
_ => return Err(anyhow!("dynamic attributes not allowed in inherit")), _ => return Err(anyhow!("dynamic attributes not allowed in inherit")),
}; };
let expr = from.map_or_else( let expr = from.map_or_else(
|| Var { sym: ident.clone() }.ir().ok(), || Var { sym: ident.clone() }.ir().ok(),
|from| { |from| {
Ok(Select { Ok(Select {
expr: from.ir().boxed(), expr: from.ir().boxed(),
@@ -739,9 +734,7 @@ fn downgrade_attr(attr: ast::Attr, state: &mut DowngradeState) -> Result<Attr> {
let parts = parts let parts = parts
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => { ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(),
Const { value: lit.into() }.ir().ok()
}
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state) interpol.expr().unwrap().downgrade(state)
} }

View File

@@ -1,7 +1,6 @@
mod compile; mod compile;
mod env; mod env;
mod ir; mod ir;
mod symtable;
pub fn compile(expr: &str) -> anyhow::Result<crate::bytecode::Program> { pub fn compile(expr: &str) -> anyhow::Result<crate::bytecode::Program> {
let expr = rnix::Root::parse(expr).tree().expr().unwrap(); let expr = rnix::Root::parse(expr).tree().expr().unwrap();

View File

@@ -1,35 +0,0 @@
use std::collections::HashMap;
use crate::bytecode::SymIdx;
use crate::slice::Slice;
// FIXME: don't store syms twice to make it more memory efficient?
pub struct SymTable {
syms: Vec<String>,
syms_table: HashMap<String, SymIdx>,
}
impl SymTable {
pub fn new() -> SymTable {
SymTable {
syms: Vec::new(),
syms_table: HashMap::new(),
}
}
pub fn lookup(&mut self, name: String) -> SymIdx {
if let Some(sym) = self.syms_table.get(&name) {
*sym
} else {
let sym = self.syms.len();
self.syms.push(name.clone());
self.syms_table.insert(name, sym);
sym
}
}
pub fn syms(self) -> Slice<String> {
self.syms.into()
}
}

View File

@@ -4,6 +4,5 @@ mod builtins;
mod bytecode; mod bytecode;
mod compile; mod compile;
mod downcast; mod downcast;
mod slice;
mod value; mod value;
mod vm; mod vm;

View File

@@ -1,2 +0,0 @@
pub type Slice<T> = Box<[T]>;

124
src/value/cnst.rs Normal file
View File

@@ -0,0 +1,124 @@
use std::hash::{Hash, Hasher};
use anyhow::Error;
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
use super::Func;
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Func(Func),
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}
impl Hash for Const {
fn hash<H: Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Bool(b) => b.hash(state),
Int(int) => int.hash(state),
Float(float) => float.to_bits().hash(state),
String(string) => string.hash(state),
Func(func) => func.hash(state),
}
}
}

View File

@@ -1,12 +1,15 @@
use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc;
use derive_more::{Constructor, IsVariant, Unwrap}; use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
use rpds::{HashTrieMapSync, VectorSync}; use rpds::{HashTrieMapSync, VectorSync};
use crate::bytecode::{Args, Const, OpCodes}; use crate::bytecode::{Args, OpCodes};
mod cnst;
pub use cnst::Const;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)]
pub struct Symbol(EcoString); pub struct Symbol(EcoString);
@@ -93,7 +96,7 @@ pub struct List {
#[derive(Clone, Debug, PartialEq, Constructor)] #[derive(Clone, Debug, PartialEq, Constructor)]
pub struct Catchable { pub struct Catchable {
msg: Option<String> msg: Option<String>,
} }
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]

View File

@@ -3,7 +3,7 @@ use rpds::HashTrieMapSync;
use super::value::{Symbol, VmValue}; use super::value::{Symbol, VmValue};
pub struct Env { pub struct Env {
last: Option<*mut Env>, last: Option<Box<Env>>,
map: HashTrieMapSync<Symbol, VmValue>, map: HashTrieMapSync<Symbol, VmValue>,
} }
@@ -19,7 +19,7 @@ impl Env {
if let Some(value) = self.map.get(&symbol) { if let Some(value) = self.map.get(&symbol) {
value.clone() value.clone()
} else { } else {
let last = unsafe { &*self.last.unwrap() }; let last = self.last.as_ref().unwrap();
last.lookup(symbol) last.lookup(symbol)
} }
} }
@@ -29,19 +29,13 @@ impl Env {
} }
pub fn enter(&mut self, map: HashTrieMapSync<Symbol, VmValue>) { pub fn enter(&mut self, map: HashTrieMapSync<Symbol, VmValue>) {
let last = std::mem::replace( let last = std::mem::replace(self, Env { last: None, map });
self, self.last = Some(Box::new(last));
Env {
last: None,
map,
},
);
self.last = Some(Box::leak(Box::new(last)) as *mut Env);
} }
pub fn leave(&mut self) { pub fn leave(&mut self) {
let last = unsafe { &*self.last.unwrap() }; let last = std::mem::replace(&mut self.last, None).unwrap();
self.last = last.last; let _ = std::mem::replace(&mut self.last, last.last);
self.map = last.map.clone(); self.map = last.map.clone();
} }
} }

View File

@@ -1,7 +1,7 @@
use std::mem::{size_of, transmute, MaybeUninit}; use std::mem::{MaybeUninit, size_of, transmute};
use std::ops::Deref; use std::ops::Deref;
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use super::value::VmValue; use super::value::VmValue;

View File

@@ -3,7 +3,6 @@ use rpds::{ht_map_sync, vector_sync};
use crate::compile::compile; use crate::compile::compile;
use crate::value::*; use crate::value::*;
use crate::bytecode::Const;
use super::vm::run; use super::vm::run;

View File

@@ -12,7 +12,14 @@ pub struct AttrSet {
} }
impl AttrSet { impl AttrSet {
pub fn push_attr_force(&mut self, sym: Symbol, val: VmValue) {
self.data.insert_mut(sym, val);
}
pub fn push_attr(&mut self, sym: Symbol, val: VmValue) { pub fn push_attr(&mut self, sym: Symbol, val: VmValue) {
if self.data.get(&sym).is_some() {
todo!()
}
self.data.insert_mut(sym, val); self.data.insert_mut(sym, val);
} }
@@ -26,7 +33,7 @@ impl AttrSet {
pub fn update(mut self, other: AttrSet) -> AttrSet { pub fn update(mut self, other: AttrSet) -> AttrSet {
for (k, v) in other.data.iter() { for (k, v) in other.data.iter() {
self.push_attr(k.clone(), v.clone()) self.push_attr_force(k.clone(), v.clone())
} }
self self
} }
@@ -41,7 +48,12 @@ impl ToValue for AttrSet {
Value::AttrSet(value::AttrSet::new( Value::AttrSet(value::AttrSet::new(
self.data self.data
.iter() .iter()
.map(|(sym, value)| (value::Symbol::new(sym.0.clone()), value.clone().to_value(vm))) .map(|(sym, value)| {
(
value::Symbol::new(sym.0.clone()),
value.clone().to_value(vm),
)
})
.collect(), .collect(),
)) ))
} }

View File

@@ -1,12 +1,11 @@
use derive_more::{Constructor, IsVariant, Unwrap};
use anyhow::Result; use anyhow::Result;
use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
use crate::value::*; use crate::value::*;
use crate::bytecode::Const;
use super::vm::VM;
use super::env::Env; use super::env::Env;
use super::vm::VM;
mod attrset; mod attrset;
mod list; mod list;
@@ -40,7 +39,7 @@ pub enum VmValue {
List(List), List(List),
Catchable(crate::value::Catchable), Catchable(crate::value::Catchable),
PrimOp(crate::builtins::PrimOp), PrimOp(crate::builtins::PrimOp),
PartialPrimOp(crate::builtins::PartialPrimOp) PartialPrimOp(crate::builtins::PartialPrimOp),
} }
use VmValue::Const as VmConst; use VmValue::Const as VmConst;
@@ -49,7 +48,7 @@ impl VmValue {
match self { match self {
VmValue::PrimOp(func) => func.call(args), VmValue::PrimOp(func) => func.call(args),
VmValue::PartialPrimOp(func) => func.call(args), VmValue::PartialPrimOp(func) => func.call(args),
_ => todo!() _ => todo!(),
} }
} }
@@ -90,7 +89,7 @@ impl VmValue {
(VmConst(Float(a)), VmConst(Int(b))) => a < b as f64, (VmConst(Float(a)), VmConst(Int(b))) => a < b as f64,
(VmConst(Float(a)), VmConst(Float(b))) => a < b, (VmConst(Float(a)), VmConst(Float(b))) => a < b,
(VmConst(String(a)), VmConst(String(b))) => a < b, (VmConst(String(a)), VmConst(String(b))) => a < b,
_ => todo!() _ => todo!(),
})) }))
} }
@@ -144,7 +143,9 @@ impl VmValue {
} }
pub fn concat_string(&mut self, mut other: VmValue) -> &mut Self { pub fn concat_string(&mut self, mut other: VmValue) -> &mut Self {
if let (VmConst(Const::String(a)), VmConst(Const::String(b))) = (self.coerce_to_string(), other.coerce_to_string()) { if let (VmConst(Const::String(a)), VmConst(Const::String(b))) =
(self.coerce_to_string(), other.coerce_to_string())
{
a.push_str(b.as_str()); a.push_str(b.as_str());
} else { } else {
todo!() todo!()
@@ -190,7 +191,9 @@ impl VmValue {
if let VmValue::AttrSet(attrs) = self { if let VmValue::AttrSet(attrs) = self {
let val = attrs let val = attrs
.select(sym.clone()) .select(sym.clone())
.unwrap_or(VmValue::Catchable(Catchable::new(Some(format!("{sym:?} not found"))))); .unwrap_or(VmValue::Catchable(Catchable::new(Some(format!(
"{sym:?} not found"
)))));
*self = val; *self = val;
} else { } else {
todo!() todo!()

View File

@@ -1,13 +1,12 @@
use anyhow::{anyhow, Result}; use anyhow::Result;
use rpds::{HashTrieMap, HashTrieMapSync, Vector}; use rpds::{HashTrieMap, HashTrieMapSync, Vector};
use crate::bytecode::{self, *};
use crate::slice::*;
use crate::value::{self, Value};
use crate::builtins::env; use crate::builtins::env;
use crate::bytecode::{self, *};
use crate::value::{Const, Value};
use super::env::Env; use super::env::Env;
use super::stack::{Stack, STACK_SIZE}; use super::stack::{STACK_SIZE, Stack};
use super::value::{self as vmValue, *}; use super::value::{self as vmValue, *};
use super::vmthunk::*; use super::vmthunk::*;
@@ -17,7 +16,7 @@ pub fn run(prog: Program) -> Result<Value> {
} }
pub struct VM { pub struct VM {
thunks: Slice<VmThunk>, thunks: Box<[VmThunk]>,
} }
impl VM { impl VM {
@@ -26,9 +25,7 @@ impl VM {
.into_iter() .into_iter()
.map(|bytecode::Thunk { opcodes }| VmThunk::new(opcodes)) .map(|bytecode::Thunk { opcodes }| VmThunk::new(opcodes))
.collect(); .collect();
VM { VM { thunks }
thunks,
}
} }
pub fn get_thunk_value(&self, idx: usize, env: &mut Env) -> Result<VmValue> { pub fn get_thunk_value(&self, idx: usize, env: &mut Env) -> Result<VmValue> {
@@ -77,7 +74,7 @@ impl VM {
} }
} }
OpCode::Call { arity } => { OpCode::Call { arity } => {
let mut args = Vec::with_capacity(arity); let mut args = Vec::with_capacity(arity);
for _ in 0..arity { for _ in 0..arity {
args.insert(0, stack.pop()?); args.insert(0, stack.pop()?);
} }
@@ -139,9 +136,10 @@ impl VM {
.select_with_default(Symbol::new(sym), default.clone()); .select_with_default(Symbol::new(sym), default.clone());
} }
OpCode::SelectOrEmpty { sym } => { OpCode::SelectOrEmpty { sym } => {
stack stack.tos_mut()?.select_with_default(
.tos_mut()? Symbol::new(sym),
.select_with_default(Symbol::new(sym), VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync()))); VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())),
);
} }
OpCode::SelectDynamic => { OpCode::SelectDynamic => {
let mut val = stack.pop().unwrap(); let mut val = stack.pop().unwrap();
@@ -160,7 +158,10 @@ impl VM {
let mut val = stack.pop().unwrap(); let mut val = stack.pop().unwrap();
val.coerce_to_string(); val.coerce_to_string();
let sym = val.unwrap_const().unwrap_string().into(); let sym = val.unwrap_const().unwrap_string().into();
stack.tos_mut()?.select_with_default(sym, VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync()))); stack.tos_mut()?.select_with_default(
sym,
VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())),
);
} }
OpCode::HasAttr { sym } => { OpCode::HasAttr { sym } => {
stack.tos_mut()?.has_attr(Symbol::new(sym)); stack.tos_mut()?.has_attr(Symbol::new(sym));
@@ -185,4 +186,3 @@ impl VM {
Ok(0) Ok(0)
} }
} }

View File

@@ -1,18 +1,18 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::RwLock; use std::sync::RwLock;
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use crate::bytecode::OpCodes; use crate::bytecode::OpCodes;
use super::vm::VM;
use super::env::Env; use super::env::Env;
use super::value::VmValue; use super::value::VmValue;
use super::vm::VM;
pub struct VmThunk { pub struct VmThunk {
thunk: RefCell<_VmThunk>, thunk: RefCell<_VmThunk>,
lock: RwLock<()> lock: RwLock<()>,
} }
#[derive(IsVariant, Unwrap)] #[derive(IsVariant, Unwrap)]
@@ -26,7 +26,7 @@ impl VmThunk {
pub fn new(opcodes: OpCodes) -> VmThunk { pub fn new(opcodes: OpCodes) -> VmThunk {
VmThunk { VmThunk {
thunk: RefCell::new(_VmThunk::Code(opcodes)), thunk: RefCell::new(_VmThunk::Code(opcodes)),
lock: RwLock::new(()) lock: RwLock::new(()),
} }
} }
@@ -37,8 +37,8 @@ impl VmThunk {
_VmThunk::Value(value) => return Ok(value.clone()), _VmThunk::Value(value) => return Ok(value.clone()),
_VmThunk::SuspendedFrom(from) => { _VmThunk::SuspendedFrom(from) => {
return Err(anyhow!( return Err(anyhow!(
"already suspended from {from:p} (infinite recursion encountered)" "already suspended from {from:p} (infinite recursion encountered)"
)) ));
} }
_VmThunk::Code(_) => (), _VmThunk::Code(_) => (),
} }
@@ -48,9 +48,13 @@ impl VmThunk {
let opcodes = std::mem::replace( let opcodes = std::mem::replace(
&mut *self.thunk.borrow_mut(), &mut *self.thunk.borrow_mut(),
_VmThunk::SuspendedFrom(self as *const VmThunk), _VmThunk::SuspendedFrom(self as *const VmThunk),
).unwrap_code(); )
.unwrap_code();
let value = vm.eval(opcodes, env).unwrap(); let value = vm.eval(opcodes, env).unwrap();
let _ = std::mem::replace(&mut *self.thunk.borrow_mut(), _VmThunk::Value(value.clone())); let _ = std::mem::replace(
&mut *self.thunk.borrow_mut(),
_VmThunk::Value(value.clone()),
);
Ok(value) Ok(value)
} }
} }