diff --git a/src/bytecode.rs b/src/bytecode.rs index 537e461..25d636e 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -71,10 +71,14 @@ pub enum OpCode { SelectDynamic, /// [ .. set sym default ] select `sym` from `set` or `default` SelectDynamicOrDefault, - /// enter the environment of the attribute set at TOS - EnterEnv, - /// exit current envrironment - LeaveEnv, + /// enter the let environment of the attribute set at TOS + EnterLetEnv, + /// exit current let envrironment + LeaveLetEnv, + /// enter the with environment of the attribute set at TOS + EnterWithEnv, + /// exit current with envrironment + LeaveWithEnv, /// illegal operation, used as termporary placeholder Illegal, diff --git a/src/compile.rs b/src/compile.rs index 1dd0df7..23b7a5d 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -364,18 +364,18 @@ impl Compile for ir::If { impl Compile for ir::Let { fn compile(self, comp: &mut Compiler) { self.attrs.compile(comp); - comp.push(OpCode::EnterEnv); + comp.push(OpCode::EnterLetEnv); self.expr.compile(comp); - comp.push(OpCode::LeaveEnv); + comp.push(OpCode::LeaveLetEnv); } } impl Compile for ir::With { fn compile(self, comp: &mut Compiler) { self.namespace.compile(comp); - comp.push(OpCode::EnterEnv); + comp.push(OpCode::EnterWithEnv); self.expr.compile(comp); - comp.push(OpCode::LeaveEnv); + comp.push(OpCode::LeaveWithEnv); } } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index e9f909d..aaa804e 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -55,8 +55,9 @@ impl<'vm, 'jit: 'vm> Func<'jit, 'vm> { pub fn call(&self, vm: &'vm VM<'jit>, arg: Value<'jit, 'vm>) -> Result> { use Param::*; - let env = match self.func.param.clone() { - Ident(ident) => self.env.clone().enter_arg(ident, arg), + let mut env = self.env.clone(); + match self.func.param.clone() { + Ident(ident) => env.enter_arg(ident, arg), Formals { formals, ellipsis, @@ -86,7 +87,7 @@ impl<'vm, 'jit: 'vm> Func<'jit, 'vm> { if let Some(alias) = alias { new.insert(alias, Value::AttrSet(arg)); } - self.env.clone().enter_attrs(new.into()) + env.enter_let(new.into()) } }; diff --git a/src/vm/env.rs b/src/vm/env.rs index a15009b..48981a2 100644 --- a/src/vm/env.rs +++ b/src/vm/env.rs @@ -3,23 +3,30 @@ use std::{hash::Hash, rc::Rc}; use hashbrown::HashMap; -use crate::ty::internal::{AttrSet, Value}; +use crate::ty::internal::Value; -pub struct Env { - map: Node, - last: Option>>, +#[derive(Clone)] +pub struct Env { + let_: Rc>, + with: Rc>, } -impl Clone for Env { - fn clone(&self) -> Self { - Self { - map: self.map.clone(), - last: self.last.clone(), - } +#[derive(Clone)] +pub struct LetEnv { + map: Let, + last: Option>>, +} + +impl Debug for Env { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Env") + .field("let_", &self.let_) + .field("with", &self.with) + .finish() } } -impl Debug for Env { +impl Debug for LetEnv { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Env") .field("map", &self.map) @@ -31,13 +38,13 @@ impl Debug for Env { pub type VmEnv<'jit, 'vm> = Env>; #[derive(Debug, Default, Clone)] -pub struct WithEnv<'jit, 'vm> { - map: Rc>, - last: Option>>, +pub struct With { + map: Option>>, + last: Option>>, } #[derive(Debug, Clone)] -enum Node { +enum Let { Let(Rc>), SingleArg(K, V), MultiArg(Rc>), @@ -50,16 +57,56 @@ pub enum Type { With, } -impl Env { +impl Env { pub fn new(map: Rc>) -> Self { Self { - map: Node::Let(map), + let_: LetEnv::new(map).into(), + with: With { + map: None, + last: None, + } + .into(), + } + } + + pub fn lookup(&self, symbol: &K) -> Option<&V> { + if let Some(val) = self.let_.lookup(symbol) { + return Some(val); + } + self.with.lookup(symbol) + } + + pub fn enter_arg(self: &mut Rc, ident: K, val: V) { + Rc::make_mut(self).let_.enter_arg(ident, val); + } + + pub fn enter_let(self: &mut Rc, map: Rc>) { + Rc::make_mut(self).let_.enter_let(map); + } + + pub fn enter_with(self: &mut Rc, map: Rc>) { + Rc::make_mut(self).with.enter(map); + } + + pub fn leave_let(self: &mut Rc) { + Rc::make_mut(self).let_.leave(); + } + + pub fn leave_with(self: &mut Rc) { + Rc::make_mut(self).with.leave(); + } +} + +impl LetEnv { + pub fn new(map: Rc>) -> Self { + Self { + map: Let::Let(map), last: None, } } pub fn lookup(&self, symbol: &K) -> Option<&V> { - use Node::*; + use self::Let::*; match &self.map { Let(map) | MultiArg(map) => { if let Some(val) = map.get(symbol) { @@ -75,59 +122,45 @@ impl Env { self.last.as_ref().and_then(|env| env.lookup(symbol)) } - pub fn enter_arg(self: Rc, ident: K, val: V) -> Rc { - let last = Some(self); - let map = Node::SingleArg(ident, val); - Env { last, map }.into() + pub fn enter_arg(self: &mut Rc, ident: K, val: V) { + let cloned = self.clone(); + let mutref = Rc::make_mut(self); + mutref.last = Some(cloned); + mutref.map = Let::SingleArg(ident, val); } - pub fn enter_attrs(self: Rc, map: Rc>) -> Rc { - let last = Some(self); - let map = Node::Let(map); - Env { last, map }.into() + pub fn enter_let(self: &mut Rc, map: Rc>) { + let cloned = self.clone(); + let mutref = Rc::make_mut(self); + mutref.last = Some(cloned); + mutref.map = Let::Let(map); } - pub fn enter_with(self: Rc, map: Rc>) -> Rc { - let map = Node::Let(map); - let last = Some(self); - Env { last, map }.into() - } - - pub fn leave(self: Rc) -> Rc { - self.last.clone().unwrap() + pub fn leave(self: &mut Rc) { + let refmut = Rc::make_mut(self); + let last = refmut.last.take().unwrap(); + *self = last; } } -impl<'jit, 'vm> WithEnv<'jit, 'vm> { - pub fn lookup(&self, symbol: usize) -> Option> { - if let Some(val) = self.map.select(symbol) { +impl With { + pub fn lookup(&self, symbol: &K) -> Option<&V> { + if let Some(val) = self.map.as_ref()?.get(symbol) { return Some(val); } self.last.as_ref().and_then(|env| env.lookup(symbol)) } - pub fn enter_with(self, new: Rc>) -> Self { - let map = Rc::new( - new.as_inner() - .iter() - .map(|(&k, v)| { - ( - k, - if let Value::Builtins(weak) = v { - Value::AttrSet(weak.upgrade().unwrap()) - } else { - v.clone() - }, - ) - }) - .collect::>() - .into(), - ); - let last = Some(self.into()); - WithEnv { last, map } + pub fn enter(self: &mut Rc, map: Rc>) { + let cloned = self.clone(); + let mutref = Rc::make_mut(self); + mutref.last = Some(cloned); + mutref.map = Some(map); } - pub fn leave(self) -> Self { - self.last.unwrap().as_ref().clone() + pub fn leave(self: &mut Rc) { + let refmut = Rc::make_mut(self); + let last = refmut.last.take().unwrap(); + *self = last; } } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 68354d2..c16524e 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -189,9 +189,7 @@ impl<'vm, 'jit: 'vm> VM<'jit> { stack.push(Value::AttrSet(AttrSet::with_capacity(cap).into()))?; } OpCode::FinalizeRec => { - let env = env - .clone() - .enter_attrs(stack.tos()?.clone().unwrap_attr_set().into_inner()); + env.enter_let(stack.tos()?.clone().unwrap_attr_set().into_inner()); stack.tos_mut()?.as_mut().unwrap_attr_set().capture(&env); } OpCode::PushStaticAttr { name } => { @@ -251,12 +249,18 @@ impl<'vm, 'jit: 'vm> VM<'jit> { .clone(), )?; } - OpCode::EnterEnv => match stack.pop() { - Value::AttrSet(attrs) => *env = env.clone().enter_with(attrs.into_inner()), + OpCode::EnterLetEnv => match stack.pop() { + Value::AttrSet(attrs) => env.enter_let(attrs.into_inner()), _ => unreachable!(), }, - OpCode::LeaveEnv => *env = env.clone().leave(), + OpCode::LeaveLetEnv => env.leave_let(), + OpCode::EnterWithEnv => match stack.pop() { + Value::AttrSet(attrs) => env.enter_with(attrs.into_inner()), + + _ => unreachable!(), + }, + OpCode::LeaveWithEnv => env.leave_with(), OpCode::Assert => { if !stack.pop().unwrap_const().unwrap_bool() { todo!()