feat: at least it compiles, right?

This commit is contained in:
2025-06-12 20:12:31 +08:00
parent 7293cb9f75
commit 49255948ff
22 changed files with 383 additions and 251 deletions

10
Cargo.lock generated
View File

@@ -267,6 +267,15 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lru"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198"
dependencies = [
"hashbrown 0.15.3",
]
[[package]]
name = "memchr"
version = "2.7.4"
@@ -312,6 +321,7 @@ dependencies = [
"hashbrown 0.15.3",
"inkwell",
"itertools",
"lru",
"priority-queue",
"regex",
"replace_with",

View File

@@ -28,6 +28,7 @@ ecow = "0.2"
regex = "1.11"
hashbrown = "0.15"
priority-queue = "2.5"
lru = "0.14"
replace_with = "0.1"
inkwell = { version = "0.6.0", features = ["llvm18-1"] }

View File

@@ -2,10 +2,10 @@ use std::process::exit;
use itertools::Itertools;
use nixjit::engine::eval;
use nixjit::error::Error;
use nixjit::error::Result;
use nixjit::ir::downgrade;
use nixjit::engine::eval;
fn main() -> Result<()> {
let mut args = std::env::args();

View File

@@ -1,7 +1,7 @@
use ecow::EcoString;
use hashbrown::HashMap;
use crate::ir::{DowngradeContext, Ir, Const};
use crate::ir::{Const, DowngradeContext, Ir};
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<EcoString, Ir> {
let mut map = HashMap::new();

View File

@@ -1,52 +1,63 @@
use std::sync::RwLock;
use std::rc::Rc;
use hashbrown::HashSet;
use lru::LruCache;
use priority_queue::PriorityQueue;
use crate::env::VmEnv;
use crate::error::Result;
use crate::eval::Evaluate;
use crate::ir::{Downgraded, Ir};
use crate::ty::public::Value;
use crate::ty::internal as i;
use crate::error::Result;
use crate::ty::public::Value;
#[cfg(test)]
mod test;
type ThunkIdx = usize;
type EnvIdx = usize;
pub struct Engine {
thunks: Box<[RwLock<Thunk>]>,
func_offset: usize,
tasks: PriorityQueue<CompileTask, usize>
thunks: Box<[Ir]>,
pub func_offset: usize,
tasks: PriorityQueue<CompileTask, usize>,
lru: LruCache<(ThunkIdx, EnvIdx), i::Value>,
}
pub fn eval(downgraded: Downgraded) -> Result<Value> {
let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset);
engine.eval(downgraded.top_level, &VmEnv::new(vec![])).map(|mut val| Ok(val.force(&engine)?.to_public(&engine, &mut HashSet::new())))?
engine
.eval(downgraded.top_level, &mut VmEnv::new())
.map(|mut val| {
Ok(val
.force(&mut engine)?
.to_public(&engine, &mut HashSet::new()))
})?
}
impl Engine {
pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self {
Self {
thunks: thunks.into_iter().map(Thunk::new).map(RwLock::new).collect(),
lru: LruCache::new(thunks.len().clamp(1, usize::MAX).try_into().unwrap()),
thunks,
func_offset,
tasks: PriorityQueue::new()
tasks: PriorityQueue::new(),
}
}
pub fn eval(&mut self, expr: Ir, env: &VmEnv) -> Result<i::Value> {
pub fn eval(&mut self, expr: Ir, env: &mut VmEnv) -> Result<i::Value> {
expr.eval(self, env)
}
pub fn eval_thunk(&self, idx: usize, env: &VmEnv) -> Result<i::Value> {
todo!()
pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result<i::Value> {
self.thunks[idx].clone().eval(self, env)
}
}
enum Thunk {
Expr(Ir),
Compiling,
Compiled(fn(Rc<VmEnv>) -> Value)
Compiled(fn(Rc<VmEnv>) -> Value),
}
impl Thunk {
@@ -57,5 +68,5 @@ impl Thunk {
#[derive(Hash, PartialEq, Eq)]
struct CompileTask {
idx: usize
idx: usize,
}

View File

@@ -221,4 +221,3 @@ fn bench_fib(b: &mut Bencher) {
black_box(())
})
}

View File

@@ -1,33 +1,34 @@
use std::hash::Hash;
use std::rc::Rc;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use ecow::EcoString;
use hashbrown::HashMap;
use crate::ty::internal::Value;
use crate::stack::Stack;
use crate::ty::internal::{EnvRef, Value};
pub struct Env<K: Hash + Eq, V> {
let_: Rc<LetEnv<V>>,
with: Rc<With<K, V>>,
args: Rc<Vec<V>>,
#[derive(Clone)]
pub struct VmEnv {
let_: Rc<RefCell<Stack<Vec<Value>, 1000>>>,
with: Rc<With>,
args: Rc<RefCell<Stack<Value, 1000>>>,
new: bool,
pub id: usize,
}
pub struct LetEnv<V> {
map: Vec<V>,
last: Option<Rc<LetEnv<V>>>,
#[derive(Clone)]
pub struct VmEnvWeak {
let_: Weak<RefCell<Stack<Vec<Value>, 1000>>>,
with: Weak<With>,
args: Weak<RefCell<Stack<Value, 1000>>>,
new: bool,
pub id: usize,
}
pub type VmEnv = Env<EcoString, Value>;
#[derive(Default, Clone)]
pub struct With<K: Hash + Eq, V> {
map: Option<Rc<HashMap<K, V>>>,
last: Option<Rc<With<K, V>>>,
}
enum LetNode<K: Hash + Eq, V> {
Let(Rc<HashMap<K, V>>),
MultiArg(Rc<HashMap<K, V>>),
pub struct With {
map: Option<Rc<HashMap<EcoString, Value>>>,
last: Option<Rc<With>>,
}
#[derive(Clone, Copy)]
@@ -37,27 +38,30 @@ pub enum Type {
With,
}
impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
pub fn new(map: Vec<V>) -> Self {
impl VmEnv {
pub fn new() -> Self {
Self {
let_: LetEnv::new(map),
let_: Rc::default(),
with: With {
map: None,
last: None,
}.into(),
args: Vec::new().into(),
}
.into(),
args: Rc::default(),
new: false,
id: 0,
}
}
pub fn lookup_arg(&self, level: usize) -> &V {
&self.args[self.args.len() - level - 1]
pub fn lookup_let(&self, level: usize, idx: usize) -> Value {
self.let_.borrow()[level][idx].clone()
}
pub fn lookup_let(&self, level: usize, idx: usize) -> &V {
self.let_.lookup(level, idx)
pub fn lookup_arg(&self, level: usize) -> Value {
self.args.borrow()[level].clone()
}
pub fn lookup_with(&self, symbol: &K) -> Option<&V> {
pub fn lookup_with(&self, symbol: &EcoString) -> Option<Value> {
self.with.lookup(symbol)
}
@@ -65,69 +69,103 @@ impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
self.with.map.is_some()
}
#[must_use]
pub fn enter_arg(&self, val: V) -> Self {
let mut args = self.args.clone();
Rc::make_mut(&mut args).push(val);
Self {
let_: self.let_.clone(),
with: self.with.clone(),
args,
pub fn enter_let(&mut self, mut map: Vec<Value>) {
if Rc::strong_count(&self.let_) > 1 {
self.let_ = Rc::new_cyclic(|weak| {
let weak = VmEnvWeak {
let_: weak.clone(),
with: Rc::downgrade(&self.with),
args: Rc::downgrade(&self.args),
new: self.new,
id: self.id,
};
map.iter_mut().for_each(|val| {
if let Value::Thunk(thunk) = val {
thunk.capture(EnvRef::Weak(weak.clone()));
}
});
let new = self.let_.as_ref().clone();
new.borrow_mut().push(map).unwrap();
new
})
} else {
let weak = self.downgrade();
map.iter_mut().for_each(|val| {
if let Value::Thunk(thunk) = val {
thunk.capture(EnvRef::Weak(weak.clone()));
}
});
let _ = self.let_.borrow_mut().push(map);
}
}
#[must_use]
pub fn enter_let(&self, map: Vec<V>) -> Self {
Self {
let_: self.let_.clone().enter_let(map),
with: self.with.clone(),
args: self.args.clone(),
pub fn enter_arg(&mut self, val: Value) {
if Rc::strong_count(&self.args) > 1 {
self.args = Rc::new(self.args.as_ref().clone());
}
self.args.borrow_mut().push(val).unwrap()
}
pub fn pop_let(&mut self) {
if Rc::strong_count(&self.let_) > 1 {
self.let_ = Rc::new(self.let_.as_ref().clone());
}
self.let_.borrow_mut().pop();
}
pub fn pop_arg(&mut self) {
if Rc::strong_count(&self.args) > 1 {
self.args = Rc::new(self.args.as_ref().clone());
}
self.args.borrow_mut().pop();
}
#[must_use]
pub fn enter_with(&self, map: Rc<HashMap<K, V>>) -> Self {
pub fn enter_with(&self, map: Rc<HashMap<EcoString, Value>>) -> Self {
Self {
let_: self.let_.clone(),
with: self.with.clone().enter(map),
args: self.args.clone(),
new: self.new,
id: self.id + 1,
}
}
pub fn downgrade(&self) -> VmEnvWeak {
VmEnvWeak {
let_: Rc::downgrade(&self.let_),
with: Rc::downgrade(&self.with),
args: Rc::downgrade(&self.args),
id: self.id,
new: self.new,
}
}
}
impl<V: Clone> LetEnv<V> {
pub fn new(map: Vec<V>) -> Rc<Self> {
Rc::new(Self { map, last: None })
}
pub fn lookup(&self, level: usize, idx: usize) -> &V {
let mut cur = self;
for _ in 0..level {
cur = cur.last.as_ref().unwrap();
}
&cur.map[idx]
}
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> {
Rc::new(Self {
map,
last: Some(self),
})
}
}
impl<K: Hash + Eq + Clone, V: Clone> With<K, V> {
pub fn lookup(&self, symbol: &K) -> Option<&V> {
impl With {
pub fn lookup(&self, symbol: &EcoString) -> Option<Value> {
if let Some(val) = self.map.as_ref()?.get(symbol) {
return Some(val);
return Some(val.clone());
}
self.last.as_ref().and_then(|env| env.lookup(symbol))
}
pub fn enter(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> {
pub fn enter(self: Rc<Self>, map: Rc<HashMap<EcoString, Value>>) -> Rc<Self> {
Rc::new(Self {
map: Some(map),
last: Some(self),
})
}
}
impl VmEnvWeak {
pub fn upgrade(&self) -> VmEnv {
VmEnv {
let_: self.let_.upgrade().unwrap(),
with: self.with.upgrade().unwrap(),
args: self.args.upgrade().unwrap(),
id: self.id,
new: self.new,
}
}
}

View File

@@ -10,7 +10,7 @@ use inkwell::values::{BasicValueEnum, FunctionValue};
use crate::env::VmEnv;
use crate::eval::Engine;
use super::{JITContext, JITValue, ValueTag, JITValueData};
use super::{JITContext, JITValue, JITValueData, ValueTag};
pub struct Helpers<'ctx> {
pub int_type: IntType<'ctx>,
@@ -341,9 +341,7 @@ extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue {
}
extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_let(level, idx).clone().into();
val
todo!()
}
extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {

View File

@@ -1,5 +1,5 @@
use std::ops::Deref;
use std::marker::PhantomData;
use std::ops::Deref;
use inkwell::OptimizationLevel;
use inkwell::builder::Builder;
@@ -10,8 +10,8 @@ use inkwell::module::Module;
use crate::env::VmEnv;
use crate::ty::internal::Value;
mod helpers;
mod compile;
mod helpers;
pub use compile::JITCompile;
use helpers::Helpers;
@@ -105,7 +105,6 @@ impl Deref for JITFunc<'_> {
}
}
pub struct JITContext<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,

View File

@@ -8,8 +8,8 @@ use inkwell::context::Context;
use ecow::EcoString;
use crate::ir::downgrade;
use super::JITContext;
use crate::ir::downgrade;
use crate::ty::common::Const;
use crate::ty::public::*;

View File

@@ -7,26 +7,34 @@ use crate::env::VmEnv;
use crate::error::{Error, Result};
use crate::ir::{self, DynamicAttrPair};
use crate::ty::common::Const;
use crate::ty::internal::{AttrSet, List, Thunk, ThunkRef, Value};
use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value};
use crate::ty::public::Symbol;
pub mod jit;
pub trait Evaluate {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value>;
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value>;
}
impl Evaluate for ir::Attrs {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut attrs = AttrSet::new(
self.stcs
.into_iter()
.map(|(k, v)| Ok((k, v.eval(engine, env)?)))
.map(|(k, v)| {
Ok((k, {
let mut val = v.eval(engine, env)?;
if let Value::Thunk(thunk) = &mut val {
thunk.capture(EnvRef::Strong(env.clone()));
}
val
}))
})
.collect::<Result<_>>()?,
);
for DynamicAttrPair(k, v) in self.dyns {
let mut k = k.eval(engine, env)?;
k.coerce_to_string();
k.force(engine)?.coerce_to_string();
attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?);
}
Value::AttrSet(attrs.into()).ok()
@@ -34,11 +42,17 @@ impl Evaluate for ir::Attrs {
}
impl Evaluate for ir::List {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
Value::List(List::from(
self.items
.into_iter()
.map(|val| val.eval(engine, env))
.map(|val| {
let mut val = val.eval(engine, env)?;
if let Value::Thunk(thunk) = &mut val {
thunk.capture(EnvRef::Strong(env.clone()));
}
val.ok()
})
.collect::<Result<EcoVec<_>>>()?,
))
.ok()
@@ -46,7 +60,7 @@ impl Evaluate for ir::List {
}
impl Evaluate for ir::HasAttr {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
use ir::Attr::*;
let mut val = self.lhs.eval(engine, env)?;
val.has_attr(self.rhs.into_iter().map(|attr| {
@@ -55,7 +69,7 @@ impl Evaluate for ir::HasAttr {
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.coerce_to_string();
val.force(engine)?.coerce_to_string();
val.unwrap_string()
}
})
@@ -65,10 +79,12 @@ impl Evaluate for ir::HasAttr {
}
impl Evaluate for ir::BinOp {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
use ir::BinOpKind::*;
let mut lhs = self.lhs.eval(engine, env)?;
let mut rhs = self.rhs.eval(engine, env)?;
lhs.force(engine)?;
rhs.force(engine)?;
match self.kind {
Add => lhs.add(rhs),
Sub => {
@@ -115,9 +131,10 @@ impl Evaluate for ir::BinOp {
}
impl Evaluate for ir::UnOp {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
use ir::UnOpKind::*;
let mut rhs = self.rhs.eval(engine, env)?;
rhs.force(engine)?;
match self.kind {
Neg => rhs.neg(),
Not => rhs.not(),
@@ -127,32 +144,34 @@ impl Evaluate for ir::UnOp {
}
impl Evaluate for ir::Select {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
use ir::Attr::*;
let mut val = self.expr.eval(engine, env)?;
if let Some(default) = self.default {
val.select_with_default(
let default = default.eval(engine, env)?;
val.force(engine)?.select_with_default(
self.attrpath.into_iter().map(|attr| {
Ok(match attr {
Str(ident) => ident,
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.coerce_to_string();
val.force(engine)?.coerce_to_string();
val.unwrap_string()
}
})
}),
default.eval(engine, env)?,
default,
)?;
} else {
val.select(self.attrpath.into_iter().map(|attr| {
val.force(engine)?
.select(self.attrpath.into_iter().map(|attr| {
Ok(match attr {
Str(ident) => ident,
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.coerce_to_string();
val.force(engine)?.coerce_to_string();
val.unwrap_string()
}
})
@@ -163,7 +182,7 @@ impl Evaluate for ir::Select {
}
impl Evaluate for ir::If {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
// TODO: Error Handling
let cond = self.cond.eval(engine, env)?.unwrap_bool();
if cond {
@@ -175,14 +194,19 @@ impl Evaluate for ir::If {
}
impl Evaluate for ir::LoadFunc {
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
Value::Func(Rc::new(ThunkRef::new(self.idx).into())).ok()
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
Value::Func(ThunkRef {
idx: engine.func_offset + self.idx,
env: Some(EnvRef::Strong(env.clone())).into(),
})
.ok()
}
}
impl Evaluate for ir::Call {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut func = self.func.eval(engine, env)?;
func.force(engine)?;
// FIXME: Modify Value::call
for arg in self.args {
func.call(arg.eval(engine, env)?, engine)?;
@@ -192,34 +216,44 @@ impl Evaluate for ir::Call {
}
impl Evaluate for ir::Let {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
let bindings = self.bindings.into_iter().map(|(_, v)| Ok(v.eval(engine, env)?)).collect::<Result<Vec<_>>>()?;
self.expr.eval(engine, &env.enter_let(bindings))
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let bindings = self
.bindings
.into_iter()
.map(|(_, v)| v.eval(engine, env))
.collect::<Result<Vec<_>>>()?;
env.enter_let(bindings);
let val = self.expr.eval(engine, env)?;
env.pop_let();
Ok(val)
}
}
impl Evaluate for ir::With {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let namespace = self.namespace.eval(engine, env)?;
// TODO: Error Handling
self.expr.eval(engine, &env.enter_with(namespace.unwrap_attr_set().into_inner()))
self.expr.eval(
engine,
&mut env.enter_with(namespace.unwrap_attr_set().into_inner()),
)
}
}
impl Evaluate for ir::Assert {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
todo!()
}
}
impl Evaluate for ir::ConcatStrings {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut parts = self
.parts
.into_iter()
.map(|part| {
let mut part = part.eval(engine, env)?;
part.coerce_to_string();
part.force(engine)?.coerce_to_string();
part.ok()
})
.collect::<Result<Vec<_>>>()?
@@ -235,13 +269,13 @@ impl Evaluate for ir::ConcatStrings {
}
impl Evaluate for ir::String {
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
Value::String(self.val).ok()
}
}
impl Evaluate for ir::Const {
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
match self.val {
Const::Null => Value::Null,
Const::Int(x) => Value::Int(x),
@@ -253,33 +287,36 @@ impl Evaluate for ir::Const {
}
impl Evaluate for ir::Var {
fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
env.lookup_with(&self.sym)
.cloned()
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym))))
}
}
impl Evaluate for ir::Arg {
fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
env.lookup_arg(self.level).clone().ok()
}
}
impl Evaluate for ir::LetVar {
fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
env.lookup_let(self.level, self.idx).clone().ok()
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut ret = env.lookup_let(self.level, self.idx);
if let Value::Thunk(thunk) = &mut ret {
thunk.upgrade();
}
ret.ok()
}
}
impl Evaluate for ir::Thunk {
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
Value::Thunk(Rc::new(Thunk::new(self.idx).into())).ok()
fn eval(self, _: &mut Engine, _: &mut VmEnv) -> Result<Value> {
Value::Thunk(ThunkRef::new(self.idx)).ok()
}
}
impl Evaluate for ir::Path {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
todo!()
}
}

View File

@@ -99,7 +99,7 @@ macro_rules! ir {
}
impl Evaluate for Ir {
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
match self {
$(Ir::$ty(ir) => ir.eval(engine, env),)*
}
@@ -182,6 +182,8 @@ pub struct DowngradeContext {
struct Env<'a, 'env> {
env: EnvNode<'a>,
prev: Option<&'env Env<'a, 'env>>,
arg_level: usize,
let_level: usize
}
enum EnvNode<'a> {
@@ -205,6 +207,8 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Builtins(base),
prev: None,
arg_level: 0,
let_level: 0
}
}
@@ -212,6 +216,8 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Let(map),
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level + 1
}
}
@@ -219,6 +225,8 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::SingleArg(ident),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: self.let_level
}
}
@@ -230,6 +238,8 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::MultiArg(map, alias),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: 0
}
}
@@ -237,6 +247,8 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::With,
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level
}
}
@@ -262,30 +274,30 @@ impl<'a, 'env> Env<'a, 'env> {
Let(map) => {
if let Ok(idx) = map.binary_search(ident) {
return Ok(LookupResult::Let {
level: let_level,
level: let_level - 1,
idx,
});
} else {
let_level += 1;
let_level -= 1;
}
}
SingleArg(arg) => {
if arg == ident {
return Ok(LookupResult::SingleArg { level: arg_level });
return Ok(LookupResult::SingleArg { level: arg_level - 1 });
} else {
arg_level += 1;
arg_level -= 1;
}
}
MultiArg(set, alias) => {
if let Some(default) = set.get(ident) {
return Ok(LookupResult::MultiArg {
level: arg_level,
level: arg_level - 1,
default: default.clone(),
});
} else if alias.as_ref() == Some(ident) {
return Ok(LookupResult::SingleArg { level: arg_level });
return Ok(LookupResult::SingleArg { level: arg_level - 1 });
} else {
arg_level += 1;
arg_level -= 1;
}
}
With => has_with = true,
@@ -296,7 +308,7 @@ impl<'a, 'env> Env<'a, 'env> {
}
fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
self._lookup(ident, 0, 0, false)
self._lookup(ident, self.arg_level, self.let_level, false)
}
}
@@ -367,11 +379,16 @@ impl Attrs {
) -> Result<()> {
if let Some(attr) = path.next() {
match attr {
Attr::Str(ident) => {
if self.stcs.get(&ident).is_some() {
self.stcs
.get_mut(&ident)
.unwrap()
Attr::Str(ident) => self
.stcs
.entry(ident.clone())
.or_insert_with(|| {
Attrs {
stcs: HashMap::new(),
dyns: Vec::new(),
}
.ir()
})
.as_mut()
.try_unwrap_attrs()
.map_err(|_| {
@@ -380,17 +397,7 @@ impl Attrs {
Symbol::from(ident)
))
})
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
} else {
let mut attrs = Attrs {
stcs: HashMap::new(),
dyns: Vec::new(),
};
attrs._insert(path, name, value, ctx)?;
assert!(self.stcs.insert(ident, attrs.ir()).is_none());
Ok(())
}
}
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)),
Attr::Strs(string) => {
let mut attrs = Attrs {
stcs: HashMap::new(),
@@ -413,13 +420,12 @@ impl Attrs {
} else {
match name {
Attr::Str(ident) => {
if self.stcs.get(&ident).is_some() {
if self.stcs.insert(ident.clone(), value).is_some() {
return Err(Error::DowngradeError(format!(
r#"attribute '{}' already defined"#,
Symbol::from(ident)
)));
}
self.stcs.insert(ident, value);
}
Attr::Strs(string) => {
self.dyns.push(DynamicAttrPair(string.ir(), value));

View File

@@ -2,7 +2,6 @@ use rnix::ast;
use super::*;
pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
match param {
ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())),
@@ -122,7 +121,10 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Att
}
}
pub fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result<Vec<Attr>> {
pub fn downgrade_attrpath(
attrpath: ast::Attrpath,
ctx: &mut DowngradeContext,
) -> Result<Vec<Attr>> {
attrpath
.attrs()
.map(|attr| downgrade_attr(attr, ctx))

View File

@@ -6,8 +6,8 @@ mod env;
mod stack;
mod ty;
pub mod error;
pub mod ir;
pub mod eval;
pub mod engine;
pub mod error;
pub mod eval;
pub mod ir;
pub use ty::public::Value;

View File

@@ -1,5 +1,5 @@
use std::mem::{MaybeUninit, replace, transmute};
use std::ops::Deref;
use std::ops::{Deref, DerefMut};
use crate::error::*;
@@ -18,13 +18,25 @@ pub struct Stack<T, const CAP: usize> {
top: usize,
}
impl<T, const CAP: usize> Default for Stack<T, CAP> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone, const CAP: usize> Clone for Stack<T, CAP> {
fn clone(&self) -> Self {
let mut items = [const { MaybeUninit::uninit() }; CAP];
for (i, item) in self.items.iter().take(self.top).enumerate() {
items[i].write(unsafe { item.assume_init_ref() }.clone());
}
Self {
items,
top: self.top,
}
}
}
impl<T, const CAP: usize> Stack<T, CAP> {
pub fn new() -> Self {
Stack {
@@ -68,6 +80,12 @@ impl<T, const CAP: usize> Deref for Stack<T, CAP> {
}
}
impl<T, const CAP: usize> DerefMut for Stack<T, CAP> {
fn deref_mut(&mut self) -> &mut Self::Target {
into!(&mut self.items[0..self.top])
}
}
impl<T, const CAP: usize> Drop for Stack<T, CAP> {
fn drop(&mut self) {
self.items.as_mut_slice()[0..self.top]

View File

@@ -1,8 +1,8 @@
use std::ops::Deref;
use std::rc::Rc;
use ecow::EcoString;
use derive_more::Constructor;
use ecow::EcoString;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
@@ -50,30 +50,39 @@ impl AttrSet {
self.data.insert(sym, val);
}
pub fn select(&self, mut path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<Value> {
pub fn select(
&self,
mut path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> Result<Value> {
// .ok_or_else(|| Error::EvalError())),
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {
let item = item?;
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
return Err(Error::EvalError(format!("{} not found", Symbol::from(item))))
return Err(Error::EvalError(format!(
"{} not found",
Symbol::from(item)
)));
};
data = attrs.as_inner();
}
let last = last?;
data.get(&last).cloned().ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
data.get(&last)
.cloned()
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
}
pub fn has_attr(&self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<bool> {
pub fn has_attr(&self, mut path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<bool> {
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {
let Some(Value::AttrSet(attrs)) = data.get(&item?) else {
return Ok(false)
return Ok(false);
};
data = attrs.as_inner();
}
Ok(true)
Ok(data.get(&last?).is_some())
}
pub fn update(&mut self, other: &AttrSet) {

View File

@@ -10,10 +10,7 @@ pub struct Func<'gc> {
impl<'gc> Func<'gc> {
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv>) -> Self {
Self {
func,
env,
}
Self { func, env }
}
}

View File

@@ -4,9 +4,9 @@ use std::rc::Weak;
use ecow::EcoVec;
use hashbrown::HashSet;
use crate::engine::Engine;
use crate::env::VmEnv;
use crate::ty::public as p;
use crate::engine::Engine;
use super::Value;
@@ -36,7 +36,9 @@ impl Deref for List {
impl List {
pub fn new() -> Self {
List { data: EcoVec::new() }
List {
data: EcoVec::new(),
}
}
pub fn with_capacity(cap: usize) -> Self {

View File

@@ -1,7 +1,6 @@
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::{Rc, Weak};
use std::sync::RwLock;
use std::rc::Rc;
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
@@ -9,11 +8,11 @@ use hashbrown::HashSet;
use replace_with::replace_with_or_abort;
use super::common::*;
use super::public::{self as p, Symbol};
use super::public as p;
use crate::env::VmEnv;
use crate::error::*;
use crate::engine::Engine;
use crate::env::{VmEnv, VmEnvWeak};
use crate::error::*;
mod attrset;
mod func;
@@ -27,30 +26,14 @@ pub use primop::*;
#[derive(Clone)]
pub enum EnvRef {
Strong(Rc<VmEnv>),
Weak(Weak<VmEnv>)
Strong(VmEnv),
Weak(VmEnvWeak),
}
#[derive(Clone)]
pub struct ThunkRef {
pub idx: usize,
pub env: Option<EnvRef>
}
pub enum Thunk {
Expr(ThunkRef),
Suspended,
Value(Value)
}
impl Thunk {
pub fn new(idx: usize) -> Self {
Thunk::Expr(ThunkRef::new(idx))
}
pub fn force(&mut self, engine: &Engine) -> Result<Value> {
todo!()
}
pub env: Option<EnvRef>,
}
impl ThunkRef {
@@ -61,12 +44,19 @@ impl ThunkRef {
pub fn capture(&mut self, env: EnvRef) {
let _ = self.env.insert(env);
}
pub fn upgrade(&mut self) {
replace_with_or_abort(&mut self.env, |env| {
env.map(|env| EnvRef::Strong(env.upgraded()))
});
}
}
impl EnvRef {
pub fn upgrade(&mut self) {
if let EnvRef::Weak(weak) = &*self {
*self = EnvRef::Strong(weak.upgrade().unwrap())
pub fn upgraded(self) -> VmEnv {
match self {
EnvRef::Weak(weak) => weak.upgrade(),
EnvRef::Strong(strong) => strong,
}
}
}
@@ -78,13 +68,13 @@ pub enum Value {
Bool(bool),
String(EcoString),
Null,
Thunk(Rc<RwLock<Thunk>>),
Thunk(ThunkRef),
AttrSet(Rc<AttrSet>),
List(List),
Catchable(EcoString),
PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp>),
Func(Rc<RwLock<ThunkRef>>),
Func(ThunkRef),
}
impl Hash for Value {
@@ -137,13 +127,13 @@ pub enum ValueAsRef<'v> {
Bool(bool),
String(&'v EcoString),
Null,
Thunk(&'v RwLock<Thunk>),
Thunk(&'v ThunkRef),
AttrSet(&'v AttrSet),
List(&'v List),
Catchable(&'v str),
PrimOp(&'v PrimOp),
PartialPrimOp(&'v PartialPrimOp),
Func(&'v RwLock<ThunkRef>),
Func(&'v ThunkRef),
}
impl Value {
@@ -197,7 +187,7 @@ impl Value {
}
}
pub fn call(&mut self, arg: Self, engine: &Engine) -> Result<()> {
pub fn call(&mut self, arg: Self, engine: &mut Engine) -> Result<()> {
use Value::*;
if matches!(arg, Value::Catchable(_)) {
*self = arg;
@@ -206,6 +196,13 @@ impl Value {
*self = match self {
PrimOp(func) => func.call(arg, engine),
PartialPrimOp(func) => func.call(arg, engine),
Func(func) => {
let mut env = func.env.take().unwrap().upgraded().clone();
env.enter_arg(arg);
let val = engine.eval_thunk(func.idx, &mut env);
env.pop_arg();
val
}
Catchable(_) => return Ok(()),
_ => todo!(),
}?;
@@ -389,10 +386,12 @@ impl Value {
}
}
pub fn select(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<&mut Self> {
pub fn select(
&mut self,
path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> Result<&mut Self> {
let val = match self {
Value::AttrSet(attrs) => attrs
.select(path),
Value::AttrSet(attrs) => attrs.select(path),
Value::Catchable(_) => return Ok(self),
_ => Err(Error::EvalError(format!(
"cannot select from {:?}",
@@ -403,7 +402,11 @@ impl Value {
Ok(self)
}
pub fn select_with_default(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>, default: Self) -> Result<&mut Self> {
pub fn select_with_default(
&mut self,
path: impl DoubleEndedIterator<Item = Result<EcoString>>,
default: Self,
) -> Result<&mut Self> {
let val = match self {
Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default),
Value::Catchable(_) => return Ok(self),
@@ -418,7 +421,7 @@ impl Value {
Ok(self)
}
pub fn has_attr(&mut self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<()> {
pub fn has_attr(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<()> {
if let Value::AttrSet(attrs) = self {
let val = Value::Bool(attrs.has_attr(path)?);
*self = val;
@@ -438,13 +441,16 @@ impl Value {
self
}
pub fn force(&mut self, engine: &Engine) -> Result<&mut Self> {
if let Value::Thunk(thunk) = &*self {
let val = {
let mut thunk = thunk.write().unwrap();
thunk.force(engine)
}?;
*self = val;
pub fn force(&mut self, engine: &mut Engine) -> Result<&mut Self> {
if let Value::Thunk(thunk) = self {
unsafe {
let old = std::ptr::read(thunk);
let mut env = old.env.unwrap().upgraded();
std::ptr::write(
self,
engine.eval_thunk(old.idx, &mut env)?,
);
}
}
Ok(self)
}
@@ -477,4 +483,3 @@ impl Value {
}
}
}

View File

@@ -2,8 +2,8 @@ use std::rc::Rc;
use derive_more::Constructor;
use crate::error::Result;
use crate::engine::Engine;
use crate::error::Result;
use super::Value;

View File

@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ops::Deref;
use std::sync::LazyLock;
use std::collections::BTreeMap;
use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString;