feat: initial parallel impl

This commit is contained in:
2025-06-08 17:27:43 +08:00
parent 3797544fc2
commit 7293cb9f75
18 changed files with 529 additions and 934 deletions

71
Cargo.lock generated
View File

@@ -80,26 +80,6 @@ version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "dashmap"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
"hashbrown 0.14.3",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "2.0.1" version = "2.0.1"
@@ -205,6 +185,16 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.3",
]
[[package]] [[package]]
name = "inkwell" name = "inkwell"
version = "0.6.0" version = "0.6.0"
@@ -271,16 +261,6 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.27"
@@ -327,12 +307,12 @@ dependencies = [
name = "nixjit" name = "nixjit"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"dashmap",
"derive_more", "derive_more",
"ecow", "ecow",
"hashbrown 0.15.3", "hashbrown 0.15.3",
"inkwell", "inkwell",
"itertools", "itertools",
"priority-queue",
"regex", "regex",
"replace_with", "replace_with",
"rnix", "rnix",
@@ -347,16 +327,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]] [[package]]
name = "parking_lot_core" name = "priority-queue"
version = "0.9.11" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970"
dependencies = [ dependencies = [
"cfg-if", "autocfg",
"libc", "equivalent",
"redox_syscall", "indexmap",
"smallvec",
"windows-targets",
] ]
[[package]] [[package]]
@@ -387,15 +365,6 @@ dependencies = [
"nibble_vec", "nibble_vec",
] ]
[[package]]
name = "redox_syscall"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@@ -500,12 +469,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.26"

View File

@@ -27,7 +27,7 @@ derive_more = { version = "2.0", features = ["full"] }
ecow = "0.2" ecow = "0.2"
regex = "1.11" regex = "1.11"
hashbrown = "0.15" hashbrown = "0.15"
dashmap = "6.1" priority-queue = "2.5"
replace_with = "0.1" replace_with = "0.1"
inkwell = { version = "0.6.0", features = ["llvm18-1"] } inkwell = { version = "0.6.0", features = ["llvm18-1"] }

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
extern crate test; extern crate test;
use hashbrown::HashMap; use std::collections::BTreeMap;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
@@ -23,12 +23,7 @@ fn test_expr(expr: &str, expected: Value) {
macro_rules! map { macro_rules! map {
($($k:expr => $v:expr),*) => { ($($k:expr => $v:expr),*) => {
{ {
#[allow(unused_mut)] BTreeMap::from([$(($k, $v),)*])
let mut m = HashMap::new();
$(
m.insert($k, $v);
)*
m
} }
}; };
} }

View File

@@ -1,15 +1,15 @@
use std::hash::Hash; use std::hash::Hash;
use std::rc::Rc; use std::rc::Rc;
use ecow::EcoString;
use hashbrown::HashMap; use hashbrown::HashMap;
use crate::{ir::Ir, ty::internal::Value}; use crate::ty::internal::Value;
pub struct Env<K: Hash + Eq, V> { pub struct Env<K: Hash + Eq, V> {
let_: Rc<LetEnv<V>>, let_: Rc<LetEnv<V>>,
with: Rc<With<K, V>>, with: Rc<With<K, V>>,
args: Rc<Vec<V>>, args: Rc<Vec<V>>,
last: Option<Rc<Env<K, V>>>,
} }
pub struct LetEnv<V> { pub struct LetEnv<V> {
@@ -17,8 +17,7 @@ pub struct LetEnv<V> {
last: Option<Rc<LetEnv<V>>>, last: Option<Rc<LetEnv<V>>>,
} }
pub type VmEnv<'gc> = Env<usize, Value<'gc>>; pub type VmEnv = Env<EcoString, Value>;
pub type IrEnv<'gc> = Env<usize, Ir>;
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct With<K: Hash + Eq, V> { pub struct With<K: Hash + Eq, V> {
@@ -39,16 +38,15 @@ pub enum Type {
} }
impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> { impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
pub fn new(map: Vec<V>) -> Rc<Self> { pub fn new(map: Vec<V>) -> Self {
Rc::new(Self { Self {
let_: LetEnv::new(map), let_: LetEnv::new(map),
with: With { with: With {
map: None, map: None,
last: None, last: None,
}.into(), }.into(),
args: Vec::new().into(), args: Vec::new().into(),
last: None, }
})
} }
pub fn lookup_arg(&self, level: usize) -> &V { pub fn lookup_arg(&self, level: usize) -> &V {
@@ -68,39 +66,32 @@ impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
} }
#[must_use] #[must_use]
pub fn enter_arg(self: Rc<Self>, val: V) -> Rc<Self> { pub fn enter_arg(&self, val: V) -> Self {
let mut args = self.args.clone(); let mut args = self.args.clone();
Rc::make_mut(&mut args).push(val); Rc::make_mut(&mut args).push(val);
Rc::new(Self { Self {
let_: self.let_.clone(), let_: self.let_.clone(),
with: self.with.clone(), with: self.with.clone(),
last: Some(self),
args, args,
}) }
} }
#[must_use] #[must_use]
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> { pub fn enter_let(&self, map: Vec<V>) -> Self {
Rc::new(Self { Self {
let_: self.let_.clone().enter_let(map), let_: self.let_.clone().enter_let(map),
with: self.with.clone(), with: self.with.clone(),
args: self.args.clone(), args: self.args.clone(),
last: Some(self), }
})
} }
#[must_use] #[must_use]
pub fn enter_with(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> { pub fn enter_with(&self, map: Rc<HashMap<K, V>>) -> Self {
Rc::new(Self { Self {
let_: self.let_.clone(), let_: self.let_.clone(),
with: self.with.clone().enter(map), with: self.with.clone().enter(map),
args: self.args.clone(), args: self.args.clone(),
last: Some(self), }
})
}
pub fn leave(&self) -> Rc<Self> {
self.last.clone().unwrap()
} }
} }

View File

@@ -1,5 +1,4 @@
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc;
use inkwell::AddressSpace; use inkwell::AddressSpace;
use inkwell::context::Context; use inkwell::context::Context;
@@ -10,7 +9,6 @@ use inkwell::values::{BasicValueEnum, FunctionValue};
use crate::env::VmEnv; use crate::env::VmEnv;
use crate::eval::Engine; use crate::eval::Engine;
use crate::ty::internal::{Thunk, Value};
use super::{JITContext, JITValue, ValueTag, JITValueData}; use super::{JITContext, JITValue, ValueTag, JITValueData};
@@ -228,11 +226,8 @@ extern "C" fn helper_debug(value: JITValue) {
dbg!(value.tag); dbg!(value.tag);
} }
extern "C" fn helper_capture_env<'gc>(thunk: JITValue, env: *const VmEnv<'gc>) { extern "C" fn helper_capture_env(thunk: JITValue, env: *const VmEnv) {
let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() }; todo!()
let env = unsafe { Rc::from_raw(env) };
thunk.capture_env(env.clone());
std::mem::forget(env);
} }
extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue { extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue {
@@ -335,7 +330,7 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue {
} }
} }
extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue { extern "C" fn helper_call(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue {
todo!() todo!()
} }
@@ -352,15 +347,13 @@ extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) ->
} }
extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap(); todo!()
let val: JITValue = env.lookup_with(&sym).unwrap().clone().into();
val
} }
extern "C" fn helper_force<'gc>( extern "C" fn helper_force(
thunk: JITValue, thunk: JITValue,
vm: NonNull<Engine>, vm: NonNull<Engine>,
jit: *const JITContext<'gc>, jit: *const JITContext,
) -> JITValue { ) -> JITValue {
if !matches!(thunk.tag, ValueTag::Thunk) { if !matches!(thunk.tag, ValueTag::Thunk) {
return thunk; return thunk;

View File

@@ -1,5 +1,4 @@
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc;
use std::marker::PhantomData; use std::marker::PhantomData;
use inkwell::OptimizationLevel; use inkwell::OptimizationLevel;
@@ -9,7 +8,7 @@ use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module; use inkwell::module::Module;
use crate::env::VmEnv; use crate::env::VmEnv;
use crate::ty::internal::{Value, Thunk}; use crate::ty::internal::Value;
mod helpers; mod helpers;
mod compile; mod compile;
@@ -51,29 +50,29 @@ pub union JITValueData {
ptr: *const (), ptr: *const (),
} }
impl<'gc> From<JITValue> for Value<'gc> { impl From<JITValue> for Value {
fn from(value: JITValue) -> Self { fn from(value: JITValue) -> Self {
use ValueTag::*; use ValueTag::*;
match value.tag { match value.tag {
Int => Value::Int(unsafe { value.data.int }), Int => Value::Int(unsafe { value.data.int }),
Null => Value::Null, Null => Value::Null,
Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }), /* Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }),
Thunk => Value::Thunk(self::Thunk { Thunk => Value::Thunk(self::Thunk {
thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) }, thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) },
}), }), */
_ => todo!("not implemented for {:?}", value.tag), _ => todo!("not implemented for {:?}", value.tag),
} }
} }
} }
impl From<Value<'_>> for JITValue { impl From<Value> for JITValue {
fn from(value: Value) -> Self { fn from(value: Value) -> Self {
match value { match value {
Value::Int(int) => JITValue { Value::Int(int) => JITValue {
tag: ValueTag::Int, tag: ValueTag::Int,
data: JITValueData { int }, data: JITValueData { int },
}, },
Value::Func(func) => JITValue { /* Value::Func(func) => JITValue {
tag: ValueTag::Function, tag: ValueTag::Function,
data: JITValueData { data: JITValueData {
ptr: Rc::into_raw(func) as *const _, ptr: Rc::into_raw(func) as *const _,
@@ -84,23 +83,23 @@ impl From<Value<'_>> for JITValue {
data: JITValueData { data: JITValueData {
ptr: Rc::into_raw(thunk.thunk) as *const _, ptr: Rc::into_raw(thunk.thunk) as *const _,
}, },
}, }, */
_ => todo!(), _ => todo!(),
} }
} }
} }
pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>); pub struct JITFunc<'ctx>(F, PhantomData<&'ctx mut ()>);
type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue; type F = unsafe extern "C" fn(*const VmEnv) -> JITValue;
impl<'gc> From<F<'gc>> for JITFunc<'gc> { impl From<F> for JITFunc<'_> {
fn from(value: F<'gc>) -> Self { fn from(value: F) -> Self {
Self(value, PhantomData) Self(value, PhantomData)
} }
} }
impl<'gc> Deref for JITFunc<'gc> { impl Deref for JITFunc<'_> {
type Target = F<'gc>; type Target = F;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }

View File

@@ -1,141 +1,285 @@
use crate::ty::common::Const; use std::rc::Rc;
use crate::ty::internal::Value;
use crate::ir::{self, Downgraded}; use ecow::EcoVec;
use crate::ty::public as p;
use crate::error::Result;
use crate::engine::Engine; use crate::engine::Engine;
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::public::Symbol;
pub mod jit; pub mod jit;
pub trait Evaluate { pub trait Evaluate {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>>; fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value>;
} }
impl Evaluate for ir::Attrs { impl Evaluate for ir::Attrs {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() let mut attrs = AttrSet::new(
self.stcs
.into_iter()
.map(|(k, v)| Ok((k, v.eval(engine, env)?)))
.collect::<Result<_>>()?,
);
for DynamicAttrPair(k, v) in self.dyns {
let mut k = k.eval(engine, env)?;
k.coerce_to_string();
attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?);
}
Value::AttrSet(attrs.into()).ok()
} }
} }
impl Evaluate for ir::List { impl Evaluate for ir::List {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() Value::List(List::from(
self.items
.into_iter()
.map(|val| val.eval(engine, env))
.collect::<Result<EcoVec<_>>>()?,
))
.ok()
} }
} }
impl Evaluate for ir::HasAttr { impl Evaluate for ir::HasAttr {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() use ir::Attr::*;
let mut val = self.lhs.eval(engine, env)?;
val.has_attr(self.rhs.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.unwrap_string()
}
})
}))?;
val.ok()
} }
} }
impl Evaluate for ir::BinOp { impl Evaluate for ir::BinOp {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() use ir::BinOpKind::*;
let mut lhs = self.lhs.eval(engine, env)?;
let mut rhs = self.rhs.eval(engine, env)?;
match self.kind {
Add => lhs.add(rhs),
Sub => {
rhs.neg();
lhs.add(rhs);
}
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
Eq => Value::eq(&mut lhs, rhs),
Neq => {
Value::eq(&mut lhs, rhs);
lhs.not();
}
Lt => lhs.lt(rhs),
Gt => {
rhs.lt(lhs);
lhs = rhs;
}
Leq => {
rhs.lt(lhs);
rhs.not();
lhs = rhs;
}
Geq => {
lhs.lt(rhs);
lhs.not();
}
And => lhs.and(rhs),
Or => lhs.or(rhs),
Impl => {
lhs.not();
lhs.or(rhs);
}
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
PipeL => lhs.call(rhs, engine)?,
PipeR => {
rhs.call(lhs, engine)?;
lhs = rhs;
}
}
Ok(lhs)
} }
} }
impl Evaluate for ir::UnOp { impl Evaluate for ir::UnOp {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() use ir::UnOpKind::*;
let mut rhs = self.rhs.eval(engine, env)?;
match self.kind {
Neg => rhs.neg(),
Not => rhs.not(),
}
Ok(rhs)
} }
} }
impl Evaluate for ir::Select { impl Evaluate for ir::Select {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() use ir::Attr::*;
let mut val = self.expr.eval(engine, env)?;
if let Some(default) = self.default {
val.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.unwrap_string()
}
})
}),
default.eval(engine, env)?,
)?;
} else {
val.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.unwrap_string()
}
})
}))?;
}
val.ok()
} }
} }
impl Evaluate for ir::If { impl Evaluate for ir::If {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() // TODO: Error Handling
let cond = self.cond.eval(engine, env)?.unwrap_bool();
if cond {
self.consq.eval(engine, env)
} else {
self.alter.eval(engine, env)
}
} }
} }
impl Evaluate for ir::LoadFunc { impl Evaluate for ir::LoadFunc {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
todo!() Value::Func(Rc::new(ThunkRef::new(self.idx).into())).ok()
} }
} }
impl Evaluate for ir::Call { impl Evaluate for ir::Call {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() let mut func = self.func.eval(engine, env)?;
// FIXME: Modify Value::call
for arg in self.args {
func.call(arg.eval(engine, env)?, engine)?;
}
func.ok()
} }
} }
impl Evaluate for ir::Let { impl Evaluate for ir::Let {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() let bindings = self.bindings.into_iter().map(|(_, v)| Ok(v.eval(engine, env)?)).collect::<Result<Vec<_>>>()?;
self.expr.eval(engine, &env.enter_let(bindings))
} }
} }
impl Evaluate for ir::With { impl Evaluate for ir::With {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() let namespace = self.namespace.eval(engine, env)?;
// TODO: Error Handling
self.expr.eval(engine, &env.enter_with(namespace.unwrap_attr_set().into_inner()))
} }
} }
impl Evaluate for ir::Assert { impl Evaluate for ir::Assert {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() todo!()
} }
} }
impl Evaluate for ir::ConcatStrings { impl Evaluate for ir::ConcatStrings {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() let mut parts = self
.parts
.into_iter()
.map(|part| {
let mut part = part.eval(engine, env)?;
part.coerce_to_string();
part.ok()
})
.collect::<Result<Vec<_>>>()?
.into_iter();
let init = parts.next().unwrap();
parts
.fold(init, |mut a, b| {
a.concat_string(b);
a
})
.ok()
} }
} }
impl Evaluate for ir::String { impl Evaluate for ir::String {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
todo!() Value::String(self.val).ok()
} }
} }
impl Evaluate for ir::Const { impl Evaluate for ir::Const {
fn eval<'a>(self, _: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
match self.val { match self.val {
Const::Null => Value::Null, Const::Null => Value::Null,
Const::Int(x) => Value::Int(x), Const::Int(x) => Value::Int(x),
Const::Float(x) => Value::Float(x), Const::Float(x) => Value::Float(x),
Const::Bool(x) => Value::Bool(x), Const::Bool(x) => Value::Bool(x),
}.ok() }
.ok()
} }
} }
impl Evaluate for ir::Var { impl Evaluate for ir::Var {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
todo!() env.lookup_with(&self.sym)
.cloned()
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym))))
} }
} }
impl Evaluate for ir::Arg { impl Evaluate for ir::Arg {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
todo!() env.lookup_arg(self.level).clone().ok()
} }
} }
impl Evaluate for ir::LetVar { impl Evaluate for ir::LetVar {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
todo!() env.lookup_let(self.level, self.idx).clone().ok()
} }
} }
impl Evaluate for ir::Thunk { impl Evaluate for ir::Thunk {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
todo!() Value::Thunk(Rc::new(Thunk::new(self.idx).into())).ok()
} }
} }
impl Evaluate for ir::Path { impl Evaluate for ir::Path {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
todo!() todo!()
} }
} }
pub fn eval(expr: Downgraded) -> Result<p::Value> {
todo!()
}

View File

@@ -7,13 +7,14 @@ use rnix::ast::HasEntry;
use rnix::ast::{self, Expr}; use rnix::ast::{self, Expr};
use crate::builtins::ir_env; use crate::builtins::ir_env;
use crate::engine::Engine;
use crate::env::VmEnv;
use crate::error::*; use crate::error::*;
use crate::ty::public::Symbol; use crate::eval::Evaluate;
use crate::eval::jit::{JITCompile, JITContext};
use crate::ty::common as c; use crate::ty::common as c;
use crate::ty::internal::Value; use crate::ty::internal::Value;
use crate::eval::jit::{JITContext, JITCompile}; use crate::ty::public::Symbol;
use crate::eval::Evaluate;
use crate::engine::Engine;
mod utils; mod utils;
use utils::*; use utils::*;
@@ -26,8 +27,12 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let ir = ir.resolve(&mut ctx, &env)?; let ir = ir.resolve(&mut ctx, &env)?;
Ok(Downgraded { Ok(Downgraded {
top_level: ir, top_level: ir,
thunks: ctx.thunks.into(), func_offset: ctx.thunks.len(),
funcs: ctx.funcs.into(), thunks: ctx
.thunks
.into_iter()
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
.collect(),
}) })
} }
@@ -42,7 +47,7 @@ macro_rules! ir {
,*$(,)? ,*$(,)?
) => { ) => {
#[derive(Clone, Debug, IsVariant, Unwrap)] #[derive(Clone, Debug, IsVariant, Unwrap)]
pub enum Ir { pub enum Ir {
$( $(
$ty($ty), $ty($ty),
)* )*
@@ -63,16 +68,6 @@ macro_rules! ir {
} }
impl Ir { impl Ir {
#[inline]
fn boxed(self) -> Box<Self> {
Box::new(self)
}
#[inline]
fn ok(self) -> Result<Self> {
Ok(self)
}
#[inline] #[inline]
fn as_ref(&self) -> IrRef { fn as_ref(&self) -> IrRef {
match self { match self {
@@ -104,9 +99,9 @@ macro_rules! ir {
} }
impl Evaluate for Ir { impl Evaluate for Ir {
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> { fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
match self { match self {
$(Ir::$ty(ir) => ir.eval(engine),)* $(Ir::$ty(ir) => ir.eval(engine, env),)*
} }
} }
} }
@@ -158,6 +153,18 @@ ir! {
Path => { expr: Box<Ir> }, Path => { expr: Box<Ir> },
} }
impl Ir {
#[inline]
fn boxed(self) -> Box<Self> {
Box::new(self)
}
#[inline]
fn ok(self) -> Result<Self> {
Ok(self)
}
}
impl<T: Into<c::Const>> From<T> for Const { impl<T: Into<c::Const>> From<T> for Const {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Const { val: value.into() } Const { val: value.into() }
@@ -215,7 +222,11 @@ impl<'a, 'env> Env<'a, 'env> {
} }
} }
fn enter_multi_arg(&'env self, map: HashMap<EcoString, Option<Ir>>, alias: Option<EcoString>) -> Self { fn enter_multi_arg(
&'env self,
map: HashMap<EcoString, Option<Ir>>,
alias: Option<EcoString>,
) -> Self {
Self { Self {
env: EnvNode::MultiArg(map, alias), env: EnvNode::MultiArg(map, alias),
prev: Some(self), prev: Some(self),
@@ -301,7 +312,7 @@ impl DowngradeContext {
pub struct Downgraded { pub struct Downgraded {
pub top_level: Ir, pub top_level: Ir,
pub thunks: Box<[Ir]>, pub thunks: Box<[Ir]>,
pub funcs: Box<[Func]>, pub func_offset: usize,
} }
impl DowngradeContext { impl DowngradeContext {
@@ -362,10 +373,13 @@ impl Attrs {
.get_mut(&ident) .get_mut(&ident)
.unwrap() .unwrap()
.as_mut() .as_mut()
.try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!( .try_unwrap_attrs()
.map_err(|_| {
Error::DowngradeError(format!(
r#"attribute '{}' already defined"#, r#"attribute '{}' already defined"#,
Symbol::from(ident) Symbol::from(ident)
))) ))
})
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
} else { } else {
let mut attrs = Attrs { let mut attrs = Attrs {
@@ -641,7 +655,11 @@ impl Downgrade for ast::Path {
let parts = self let parts = self
.parts() .parts()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => String { val: lit.to_string().into() }.ir().ok(), ast::InterpolPart::Literal(lit) => String {
val: lit.to_string().into(),
}
.ir()
.ok(),
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(ctx) interpol.expr().unwrap().downgrade(ctx)
} }
@@ -706,11 +724,14 @@ impl ConcatStrings {
} }
impl Downgrade for ast::Literal { impl Downgrade for ast::Literal {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, _: &mut DowngradeContext) -> Result<Ir> {
match self.kind() { match self.kind() {
ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(), ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(),
ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(), ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(),
ast::LiteralKind::Uri(uri) => String { val: uri.to_string().into() }.ir() ast::LiteralKind::Uri(uri) => String {
val: uri.to_string().into(),
}
.ir(),
} }
.ok() .ok()
} }
@@ -729,17 +750,20 @@ impl String {
} }
impl Downgrade for ast::Ident { impl Downgrade for ast::Ident {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, _: &mut DowngradeContext) -> Result<Ir> {
let sym = self.ident_token().unwrap().to_string().into(); let sym = self.ident_token().unwrap().to_string().into();
Var { sym }.ir().ok() Var { sym }.ir().ok()
} }
} }
impl Var { impl Var {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> { fn resolve<'a, 'env>(self, _: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
use LookupResult::*; use LookupResult::*;
let Ok(res) = env.lookup(&self.sym) else { let Ok(res) = env.lookup(&self.sym) else {
return Err(Error::DowngradeError(format!("{} not found", Symbol::from(self.sym)))) return Err(Error::DowngradeError(format!(
"{} not found",
Symbol::from(self.sym)
)));
}; };
match res { match res {
Builtin(ir) => ir, Builtin(ir) => ir,
@@ -1021,14 +1045,22 @@ impl Downgrade for ast::LetIn {
} }
} }
} }
let bindings = bindings.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)).collect(); let bindings = bindings
.into_iter()
.sorted_by(|(a, _), (b, _)| a.cmp(b))
.collect();
Let { bindings, expr }.ir().ok() Let { bindings, expr }.ir().ok()
} }
} }
impl Let { impl Let {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> { fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
let map = self.bindings.iter().map(|(sym, _)| sym.clone()).sorted().collect(); let map = self
.bindings
.iter()
.map(|(sym, _)| sym.clone())
.sorted()
.collect();
let env = env.enter_let(&map); let env = env.enter_let(&map);
let bindings = self let bindings = self
.bindings .bindings

View File

@@ -7,74 +7,94 @@ use hashbrown::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
use crate::engine::Engine; use crate::engine::Engine;
use crate::error::{Error, Result};
use crate::ty::public::Symbol;
use super::super::public as p; use super::super::public as p;
use super::Value; use super::Value;
#[repr(transparent)] #[repr(transparent)]
#[derive(Constructor, Clone, PartialEq)] #[derive(Constructor, Clone, PartialEq)]
pub struct AttrSet<'gc> { pub struct AttrSet {
data: HashMap<EcoString, Value<'gc>>, data: HashMap<EcoString, Value>,
} }
impl<'gc> From<HashMap<EcoString, Value<'gc>>> for AttrSet<'gc> { impl From<HashMap<EcoString, Value>> for AttrSet {
fn from(data: HashMap<EcoString, Value<'gc>>) -> Self { fn from(data: HashMap<EcoString, Value>) -> Self {
Self { data } Self { data }
} }
} }
impl<'gc> Deref for AttrSet<'gc> { impl Deref for AttrSet {
type Target = HashMap<EcoString, Value<'gc>>; type Target = HashMap<EcoString, Value>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.data &self.data
} }
} }
impl<'gc> AttrSet<'gc> { impl AttrSet {
pub fn with_capacity(cap: usize) -> Self { pub fn with_capacity(cap: usize) -> Self {
AttrSet { AttrSet {
data: HashMap::with_capacity(cap), data: HashMap::with_capacity(cap),
} }
} }
pub fn push_attr_force(&mut self, sym: EcoString, val: Value<'gc>) { pub fn push_attr_force(&mut self, sym: EcoString, val: Value) {
self.data.insert(sym, val); self.data.insert(sym, val);
} }
pub fn push_attr(&mut self, sym: EcoString, val: Value<'gc>) { pub fn push_attr(&mut self, sym: EcoString, val: Value) {
if self.data.get(&sym).is_some() { if self.data.get(&sym).is_some() {
todo!() todo!()
} }
self.data.insert(sym, val); self.data.insert(sym, val);
} }
pub fn select(&self, sym: &EcoString) -> Option<Value<'gc>> { pub fn select(&self, mut path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<Value> {
self.data.get(sym).cloned() // .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))))
};
data = attrs.as_inner();
}
let last = last?;
data.get(&last).cloned().ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
} }
pub fn has_attr(&self, sym: &EcoString) -> bool { pub fn has_attr(&self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<bool> {
self.data.get(sym).is_some() let mut data = &self.data;
for item in path {
let Some(Value::AttrSet(attrs)) = data.get(&item?) else {
return Ok(false)
};
data = attrs.as_inner();
}
Ok(true)
} }
pub fn update(&mut self, other: &AttrSet<'gc>) { pub fn update(&mut self, other: &AttrSet) {
for (k, v) in other.data.iter() { for (k, v) in other.data.iter() {
self.push_attr_force(k.clone(), v.clone()) self.push_attr_force(k.clone(), v.clone())
} }
} }
pub fn as_inner(&self) -> &HashMap<EcoString, Value<'gc>> { pub fn as_inner(&self) -> &HashMap<EcoString, Value> {
&self.data &self.data
} }
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<EcoString, Value<'gc>>> { pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<EcoString, Value>> {
unsafe { std::mem::transmute(self) } unsafe { std::mem::transmute(self) }
} }
pub fn from_inner(data: HashMap<EcoString, Value<'gc>>) -> Self { pub fn from_inner(data: HashMap<EcoString, Value>) -> Self {
Self { data } Self { data }
} }
pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool { pub fn eq_impl(&self, other: &AttrSet) -> bool {
self.data.iter().len() == other.data.iter().len() self.data.iter().len() == other.data.iter().len()
&& std::iter::zip( && std::iter::zip(
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
@@ -83,7 +103,7 @@ impl<'gc> AttrSet<'gc> {
.all(|((_, v1), (_, v2))| v1.eq_impl(v2)) .all(|((_, v1), (_, v2))| v1.eq_impl(v2))
} }
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value { pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
p::Value::AttrSet(p::AttrSet::new( p::Value::AttrSet(p::AttrSet::new(
self.data self.data
.iter() .iter()

View File

@@ -5,11 +5,11 @@ use crate::env::VmEnv;
use crate::ir; use crate::ir;
pub struct Func<'gc> { pub struct Func<'gc> {
pub func: &'gc ir::Func, pub func: &'gc ir::Func,
pub env: Rc<VmEnv<'gc>>, pub env: Rc<VmEnv>,
} }
impl<'gc> Func<'gc> { impl<'gc> Func<'gc> {
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv<'gc>>) -> Self { pub fn new(func: &'gc ir::Func, env: Rc<VmEnv>) -> Self {
Self { Self {
func, func,
env, env,

View File

@@ -1,5 +1,7 @@
use std::ops::Deref;
use std::rc::Weak; use std::rc::Weak;
use ecow::EcoVec;
use hashbrown::HashSet; use hashbrown::HashSet;
use crate::env::VmEnv; use crate::env::VmEnv;
@@ -9,50 +11,64 @@ use crate::engine::Engine;
use super::Value; use super::Value;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct List<'gc> { pub struct List {
data: Vec<Value<'gc>>, data: EcoVec<Value>,
} }
impl<'gc> Default for List<'gc> { impl Default for List {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl<'gc> List<'gc> { impl<'gc, T: Into<EcoVec<Value>>> From<T> for List {
fn from(value: T) -> Self {
Self { data: value.into() }
}
}
impl Deref for List {
type Target = [Value];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl List {
pub fn new() -> Self { pub fn new() -> Self {
List { data: Vec::new() } List { data: EcoVec::new() }
} }
pub fn with_capacity(cap: usize) -> Self { pub fn with_capacity(cap: usize) -> Self {
List { List {
data: Vec::with_capacity(cap), data: EcoVec::with_capacity(cap),
} }
} }
pub fn push(&mut self, elem: Value<'gc>) { pub fn push(&mut self, elem: Value) {
self.data.push(elem); self.data.push(elem);
} }
pub fn concat(&mut self, other: &List<'gc>) { pub fn concat(&mut self, other: &List) {
for elem in other.data.iter() { for elem in other.data.iter() {
self.data.push(elem.clone()); self.data.push(elem.clone());
} }
} }
pub fn capture(&mut self, env: &Weak<VmEnv<'gc>>) { pub fn capture(&mut self, env: &Weak<VmEnv>) {
self.data.iter().for_each(|v| { self.data.iter().for_each(|v| {
if let Value::Thunk(ref thunk) = v.clone() { if let Value::Thunk(ref thunk) = v.clone() {
thunk.capture_env_weak(env.clone()); todo!()
// thunk.capture_env_weak(env.clone());
} }
}) })
} }
pub fn into_inner(self) -> Vec<Value<'gc>> { pub fn into_inner(self) -> EcoVec<Value> {
self.data self.data
} }
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value { pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
p::Value::List(p::List::new( p::Value::List(p::List::new(
self.data self.data
.iter() .iter()

View File

@@ -1,6 +1,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::hash::Hash; use std::hash::Hash;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::RwLock;
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
@@ -21,39 +22,84 @@ mod primop;
mod string; mod string;
pub use attrset::*; pub use attrset::*;
pub use func::*;
pub use list::List; pub use list::List;
pub use primop::*; pub use primop::*;
#[derive(Clone)]
pub enum EnvRef {
Strong(Rc<VmEnv>),
Weak(Weak<VmEnv>)
}
#[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!()
}
}
impl ThunkRef {
pub fn new(idx: usize) -> Self {
ThunkRef { idx, env: None }
}
pub fn capture(&mut self, env: EnvRef) {
let _ = self.env.insert(env);
}
}
impl EnvRef {
pub fn upgrade(&mut self) {
if let EnvRef::Weak(weak) = &*self {
*self = EnvRef::Strong(weak.upgrade().unwrap())
}
}
}
#[derive(IsVariant, Unwrap, Clone)] #[derive(IsVariant, Unwrap, Clone)]
pub enum Value<'gc> { pub enum Value {
Int(i64), Int(i64),
Float(f64), Float(f64),
Bool(bool), Bool(bool),
String(EcoString), String(EcoString),
Null, Null,
Thunk(Thunk<'gc>), Thunk(Rc<RwLock<Thunk>>),
AttrSet(Rc<AttrSet<'gc>>), AttrSet(Rc<AttrSet>),
List(Rc<List<'gc>>), List(List),
Catchable(EcoString), Catchable(EcoString),
PrimOp(Rc<PrimOp>), PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp<'gc>>), PartialPrimOp(Rc<PartialPrimOp>),
Func(Rc<Func<'gc>>), Func(Rc<RwLock<ThunkRef>>),
} }
impl Hash for Value<'_> { impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Value::*; use Value::*;
std::mem::discriminant(self).hash(state); std::mem::discriminant(self).hash(state);
match self { match self {
AttrSet(x) => Rc::as_ptr(x).hash(state), AttrSet(x) => Rc::as_ptr(x).hash(state),
List(x) => Rc::as_ptr(x).hash(state), List(x) => x.as_ptr().hash(state),
_ => 0.hash(state), _ => 0.hash(state),
} }
} }
} }
impl<'gc> Value<'gc> { impl Value {
fn eq_impl(&self, other: &Self) -> bool { fn eq_impl(&self, other: &Self) -> bool {
use Value::*; use Value::*;
match (self, other) { match (self, other) {
@@ -71,37 +117,37 @@ impl<'gc> Value<'gc> {
} }
} }
impl<'gc> PartialEq for Value<'gc> { impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use Value::*; use Value::*;
match (self, other) { match (self, other) {
(AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)), (AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)), (List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()),
_ => false, _ => false,
} }
} }
} }
impl Eq for Value<'_> {} impl Eq for Value {}
#[derive(IsVariant, Unwrap, Clone)] #[derive(IsVariant, Unwrap, Clone)]
pub enum ValueAsRef<'v, 'gc> { pub enum ValueAsRef<'v> {
Int(i64), Int(i64),
Float(f64), Float(f64),
Bool(bool), Bool(bool),
String(&'v str), String(&'v EcoString),
Null, Null,
Thunk(&'v Thunk<'gc>), Thunk(&'v RwLock<Thunk>),
AttrSet(&'v AttrSet<'gc>), AttrSet(&'v AttrSet),
List(&'v List<'gc>), List(&'v List),
Catchable(&'v str), Catchable(&'v str),
PrimOp(&'v PrimOp), PrimOp(&'v PrimOp),
PartialPrimOp(&'v PartialPrimOp<'gc>), PartialPrimOp(&'v PartialPrimOp),
Func(&'v Func<'gc>), Func(&'v RwLock<ThunkRef>),
} }
impl<'gc, 'v> Value<'gc> { impl Value {
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> { pub fn as_ref(&self) -> ValueAsRef {
use Value::*; use Value::*;
use ValueAsRef as R; use ValueAsRef as R;
match self { match self {
@@ -120,7 +166,7 @@ impl<'gc, 'v> Value<'gc> {
} }
} }
} }
impl<'gc> Value<'gc> { impl Value {
pub fn ok(self) -> Result<Self> { pub fn ok(self) -> Result<Self> {
Ok(self) Ok(self)
} }
@@ -151,15 +197,15 @@ impl<'gc> Value<'gc> {
} }
} }
pub fn call(&mut self, arg: Self, vm: &Engine) -> Result<()> { pub fn call(&mut self, arg: Self, engine: &Engine) -> Result<()> {
use Value::*; use Value::*;
if matches!(arg, Value::Catchable(_)) { if matches!(arg, Value::Catchable(_)) {
*self = arg; *self = arg;
return Ok(()); return Ok(());
} }
*self = match self { *self = match self {
PrimOp(func) => func.call(arg, vm), PrimOp(func) => func.call(arg, engine),
PartialPrimOp(func) => func.call(arg, vm), PartialPrimOp(func) => func.call(arg, engine),
Catchable(_) => return Ok(()), Catchable(_) => return Ok(()),
_ => todo!(), _ => todo!(),
}?; }?;
@@ -293,7 +339,7 @@ impl<'gc> Value<'gc> {
pub fn push(&mut self, elem: Self) -> &mut Self { pub fn push(&mut self, elem: Self) -> &mut Self {
if let Value::List(list) = self { if let Value::List(list) = self {
Rc::make_mut(list).push(elem); list.push(elem);
} else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = self {
} else if let Value::Catchable(_) = elem { } else if let Value::Catchable(_) = elem {
*self = elem; *self = elem;
@@ -310,7 +356,7 @@ impl<'gc> Value<'gc> {
} }
match (self, other) { match (self, other) {
(Value::List(a), Value::List(b)) => { (Value::List(a), Value::List(b)) => {
Rc::make_mut(a).concat(&b); a.concat(&b);
} }
(Value::Catchable(_), _) => (), (Value::Catchable(_), _) => (),
_ => todo!(), _ => todo!(),
@@ -343,11 +389,10 @@ impl<'gc> Value<'gc> {
} }
} }
pub fn select(&mut self, sym: &EcoString) -> Result<&mut Self> { pub fn select(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<&mut Self> {
let val = match self { let val = match self {
Value::AttrSet(attrs) => attrs Value::AttrSet(attrs) => attrs
.select(sym) .select(path),
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))),
Value::Catchable(_) => return Ok(self), Value::Catchable(_) => return Ok(self),
_ => Err(Error::EvalError(format!( _ => Err(Error::EvalError(format!(
"cannot select from {:?}", "cannot select from {:?}",
@@ -358,9 +403,9 @@ impl<'gc> Value<'gc> {
Ok(self) Ok(self)
} }
pub fn select_with_default(&mut self, sym: &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 { let val = match self {
Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default), Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default),
Value::Catchable(_) => return Ok(self), Value::Catchable(_) => return Ok(self),
_ => { _ => {
return Err(Error::EvalError(format!( return Err(Error::EvalError(format!(
@@ -373,15 +418,15 @@ impl<'gc> Value<'gc> {
Ok(self) Ok(self)
} }
pub fn has_attr(&mut self, sym: &EcoString) -> &mut Self { pub fn has_attr(&mut self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<()> {
if let Value::AttrSet(attrs) = self { if let Value::AttrSet(attrs) = self {
let val = Value::Bool(attrs.has_attr(sym)); let val = Value::Bool(attrs.has_attr(path)?);
*self = val; *self = val;
} else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = self {
} else { } else {
*self = Value::Bool(false); *self = Value::Bool(false);
} }
self Ok(())
} }
pub fn coerce_to_string(&mut self) -> &mut Self { pub fn coerce_to_string(&mut self) -> &mut Self {
@@ -393,7 +438,18 @@ impl<'gc> Value<'gc> {
self self
} }
pub fn to_public(&self, vm: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value { 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;
}
Ok(self)
}
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
use self::Value::*; use self::Value::*;
use p::Value; use p::Value;
if seen.contains(self) { if seen.contains(self) {
@@ -402,11 +458,11 @@ impl<'gc> Value<'gc> {
match self { match self {
AttrSet(attrs) => { AttrSet(attrs) => {
seen.insert(self.clone()); seen.insert(self.clone());
attrs.to_public(vm, seen) attrs.to_public(engine, seen)
} }
List(list) => { List(list) => {
seen.insert(self.clone()); seen.insert(self.clone());
list.to_public(vm, seen) list.to_public(engine, seen)
} }
Catchable(catchable) => Value::Catchable(catchable.clone().into()), Catchable(catchable) => Value::Catchable(catchable.clone().into()),
Int(x) => Value::Const(Const::Int(*x)), Int(x) => Value::Const(Const::Int(*x)),
@@ -422,90 +478,3 @@ impl<'gc> Value<'gc> {
} }
} }
#[repr(transparent)]
#[derive(Clone)]
pub struct Thunk<'gc> {
pub thunk: Rc<RefCell<_Thunk<'gc>>>,
}
// TODO: impl
type OpCodes = ();
#[derive(IsVariant, Unwrap)]
pub enum _Thunk<'gc> {
Code(&'gc OpCodes, Option<Env<'gc>>),
Suspended,
Value(Value<'gc>),
}
#[derive(Unwrap)]
pub enum Env<'gc> {
Strong(Rc<VmEnv<'gc>>),
Weak(Weak<VmEnv<'gc>>),
}
impl Env<'_> {
fn upgrade(self) -> Self {
if let Self::Weak(weak) = self {
Env::Strong(weak.upgrade().unwrap())
} else {
self
}
}
}
impl<'gc> Thunk<'gc> {
pub fn new(opcodes: &'gc OpCodes) -> Self {
Thunk {
thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(),
}
}
pub fn capture_env(&self, env: Rc<VmEnv<'gc>>) {
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
let _ = envcell.insert(Env::Strong(env));
}
}
pub fn capture_env_weak(&self, env: Weak<VmEnv<'gc>>) {
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
let _ = envcell.insert(Env::Weak(env));
}
}
pub fn upgrade(&self) {
replace_with_or_abort(&mut *self.thunk.borrow_mut(), |this| {
if let _Thunk::Code(opcodes, envcell) = this {
_Thunk::Code(opcodes, envcell.map(Env::upgrade))
} else {
this
}
});
}
pub fn suspend(&self) -> Result<(&'gc OpCodes, Rc<VmEnv<'gc>>)> {
use _Thunk::*;
match std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Suspended) {
Code(opcodes, env) => Ok((opcodes, env.unwrap().upgrade().unwrap_strong())),
_ => Err(Error::EvalError("infinite recursion encountered".into())),
}
}
pub fn insert_value(&self, value: Value<'gc>) {
*self.thunk.borrow_mut() = _Thunk::Value(value);
}
pub fn get_value(&self) -> Option<&Value<'gc>> {
if let _Thunk::Value(val) = unsafe { &*self.thunk.as_ptr() } {
Some(val)
} else {
None
}
}
}
impl PartialEq for Thunk<'_> {
fn eq(&self, _: &Self) -> bool {
false
}
}

View File

@@ -11,7 +11,7 @@ use super::Value;
pub struct PrimOp { pub struct PrimOp {
pub name: &'static str, pub name: &'static str,
arity: usize, arity: usize,
func: for<'gc> fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>, func: fn(Vec<Value>, &Engine) -> Result<Value>,
} }
impl PartialEq for PrimOp { impl PartialEq for PrimOp {
@@ -21,7 +21,7 @@ impl PartialEq for PrimOp {
} }
impl PrimOp { impl PrimOp {
pub fn call<'gc>(&self, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> { pub fn call(&self, arg: Value, ctx: &Engine) -> Result<Value> {
let mut args = Vec::with_capacity(self.arity); let mut args = Vec::with_capacity(self.arity);
args.push(arg); args.push(arg);
if self.arity > 1 { if self.arity > 1 {
@@ -39,21 +39,21 @@ impl PrimOp {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct PartialPrimOp<'gc> { pub struct PartialPrimOp {
pub name: &'static str, pub name: &'static str,
arity: usize, arity: usize,
args: Vec<Value<'gc>>, args: Vec<Value>,
func: fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>, func: fn(Vec<Value>, &Engine) -> Result<Value>,
} }
impl PartialEq for PartialPrimOp<'_> { impl PartialEq for PartialPrimOp {
fn eq(&self, _: &Self) -> bool { fn eq(&self, _: &Self) -> bool {
false false
} }
} }
impl<'gc> PartialPrimOp<'gc> { impl PartialPrimOp {
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> { pub fn call(self: &mut Rc<Self>, arg: Value, ctx: &Engine) -> Result<Value> {
let func = self.func; let func = self.func;
let Some(ret) = ({ let Some(ret) = ({
let self_mut = Rc::make_mut(self); let self_mut = Rc::make_mut(self);

View File

@@ -1,7 +1,7 @@
use hashbrown::HashMap;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ops::Deref; use std::ops::Deref;
use std::sync::LazyLock; use std::sync::LazyLock;
use std::collections::BTreeMap;
use derive_more::{Constructor, IsVariant, Unwrap}; use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
@@ -55,7 +55,7 @@ impl Symbol {
#[derive(Constructor, Clone, PartialEq)] #[derive(Constructor, Clone, PartialEq)]
pub struct AttrSet { pub struct AttrSet {
data: HashMap<Symbol, Value>, data: BTreeMap<Symbol, Value>,
} }
impl Debug for AttrSet { impl Debug for AttrSet {

View File

@@ -1,328 +0,0 @@
use std::cell::RefCell;
use std::rc::Rc;
use hashbrown::{HashMap, HashSet};
use inkwell::context::Context;
use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp};
use crate::env::VmEnv;
use crate::error::*;
use crate::jit::{JITContext, JITFunc};
use crate::stack::Stack;
use crate::ty::common::Const;
use crate::ty::internal::*;
use crate::ty::public::{self as p, Symbol};
use derive_more::Constructor;
use ecow::EcoString;
#[cfg(test)]
mod test;
const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
const COLLECTOR_GRANULARITY: f64 = 1024.0;
struct ContextWrapper(Context);
pub fn run(mut prog: Program) -> Result<p::Value> {
let jit = Context::create();
prog.thunks.iter_mut().for_each(|code| code.reverse());
prog.funcs
.iter_mut()
.for_each(|F { opcodes, .. }| opcodes.reverse());
let vm = VM {
thunks: prog.thunks,
funcs: prog.funcs,
consts: prog.consts,
symbols: RefCell::new(prog.symbols),
symmap: RefCell::new(prog.symmap),
jit: JITContext::new(&jit),
};
prog.top_level.reverse();
Ok(eval(prog.top_level, &vm, VmEnv::new(vec![]))?.to_public(&vm, &mut HashSet::new()))
}
pub fn eval<'gc>(opcodes: Box<[OpCode]>, vm: &VM<'gc>, env: Rc<VmEnv<'gc>>) -> Result<Value<'gc>> {
let mut opcodes = opcodes.into_vec();
let mut envs = vec![env];
let mut stack = Stack::<_, STACK_SIZE>::new();
while let Some(opcode) = opcodes.pop() {
let consq = single_op(vm, opcode, &mut stack, envs.last_mut().unwrap())?;
match consq {
Consq::NoOp => (),
Consq::Jmp(step) => opcodes.resize_with(opcodes.len() - step, || unreachable!()),
Consq::Force => {
let thunk = stack.tos().as_ref().unwrap_thunk();
let (code, env) = thunk.suspend()?;
opcodes.push(OpCode::InsertValue);
opcodes.push(OpCode::PopEnv);
opcodes.extend(code);
envs.push(env);
}
Consq::PopEnv => {
let _ = envs.pop().unwrap();
}
Consq::Call => {
let arg = stack.pop();
let func = stack.pop().unwrap_func();
let env = func.env.clone().enter_arg(arg);
let count = func.count.get();
func.count.set(count + 1);
if count > 1 {
let compiled = func.compiled.get_or_init(|| vm.compile_func(func.func));
let ret = unsafe { compiled(env.as_ref() as *const VmEnv) };
stack.push(ret.into())?;
} else {
envs.push(env);
opcodes.push(OpCode::PopEnv);
opcodes.extend(&func.func.opcodes);
}
}
}
}
stack.pop().ok()
}
enum Consq {
Jmp(usize),
Call,
Force,
PopEnv,
NoOp,
}
#[inline(always)]
fn single_op<'gc, const CAP: usize>(
vm: &VM<'gc>,
opcode: OpCode,
stack: &mut Stack<Value<'gc>, CAP>,
env: &mut Rc<VmEnv<'gc>>,
) -> Result<Consq> {
match opcode {
OpCode::Illegal => panic!("illegal opcode"),
OpCode::Const { idx } => stack.push(match vm.get_const(idx) {
Const::Int(x) => Value::Int(x),
Const::Float(x) => Value::Float(x),
Const::Bool(x) => Value::Bool(x),
Const::String(x) => Value::String(Rc::new(x.into())),
Const::Null => Value::Null,
})?,
OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?,
OpCode::LoadValue { idx } => {
stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?;
stack.tos().as_ref().unwrap_thunk().capture_env(env.clone());
return Ok(Consq::Force);
}
OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture_env(env.clone()),
OpCode::ForceValue => {
if !stack.tos().is_thunk() {
return Ok(Consq::NoOp);
}
let Some(val) = stack.tos().as_ref().unwrap_thunk().get_value() else {
return Ok(Consq::Force);
};
*stack.tos_mut() = val.clone();
}
OpCode::InsertValue => {
let val = stack.pop();
stack
.tos()
.as_ref()
.unwrap_thunk()
.insert_value(val.clone());
*stack.tos_mut() = val;
}
OpCode::Jmp { step } => return Ok(Consq::Jmp(step)),
OpCode::JmpIfFalse { step } => {
if let Value::Bool(false) = stack.pop() {
return Ok(Consq::Jmp(step));
}
}
OpCode::Call => {
let arg = stack.pop();
let func = stack.tos_mut();
if func.is_func() {
let _ = stack.push(arg);
return Ok(Consq::Call);
}
func.call(arg, vm)?;
}
OpCode::Func { idx } => {
let func = vm.get_func(idx);
stack.push(Value::Func(Rc::new(Func::new(func, env.clone()))))?;
}
OpCode::Arg { level } => {
stack.push(env.lookup_arg(level).clone())?;
}
OpCode::UnOp { op } => {
use UnOp::*;
let value = stack.tos_mut();
match op {
Neg => value.neg(),
Not => value.not(),
}
}
OpCode::BinOp { op } => {
use BinOp::*;
let mut rhs = stack.pop();
let lhs = stack.tos_mut();
match op {
Add => lhs.add(rhs),
Sub => {
rhs.neg();
lhs.add(rhs);
}
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
And => lhs.and(rhs),
Or => lhs.or(rhs),
Eq => Value::eq(lhs, rhs),
Lt => lhs.lt(rhs),
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
}
}
OpCode::ConcatString => {
let rhs = stack.pop();
stack.tos_mut().concat_string(rhs);
}
OpCode::Path => {
todo!()
}
OpCode::List { cap } => {
stack.push(Value::List(Rc::new(List::with_capacity(cap))))?;
}
OpCode::PushElem => {
let elem = stack.pop();
stack.tos_mut().push(elem);
}
OpCode::AttrSet { cap } => {
stack.push(Value::AttrSet(Rc::new(AttrSet::with_capacity(cap))))?;
}
OpCode::FinalizeLet => {
let mut list = stack.pop().unwrap_list();
let map = list.as_ref().clone().into_inner();
*env = env.clone().enter_let(map);
Rc::make_mut(&mut list).capture(&Rc::downgrade(env));
}
OpCode::PushStaticAttr { name } => {
let val = stack.pop();
stack.tos_mut().push_attr(name, val);
}
OpCode::PushDynamicAttr => {
let val = stack.pop();
let sym = stack.pop();
let sym = vm.new_sym::<&str>(&sym.unwrap_string());
stack.tos_mut().push_attr(sym, val);
}
OpCode::Select { sym } => {
stack.tos_mut().select(sym, vm)?;
}
OpCode::SelectOrDefault { sym } => {
let default = stack.pop();
stack.tos_mut().select_with_default(sym, default)?;
}
OpCode::SelectDynamic => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select(sym, vm)?;
}
OpCode::SelectDynamicOrDefault => {
let default = stack.pop();
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select_with_default(sym, default)?;
}
OpCode::HasAttr { sym } => {
stack.tos_mut().has_attr(sym);
}
OpCode::HasDynamicAttr => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().has_attr(sym);
}
OpCode::LookUp { sym } => {
stack.push(
env.lookup_with(&sym)
.ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym))))?
.clone(),
)?;
}
OpCode::LookUpLet { level, idx } => {
let val = env.lookup_let(level, idx);
if let Value::Thunk(thunk) = val {
thunk.upgrade();
}
stack.push(val.clone())?;
}
OpCode::LeaveEnv => *env = env.leave(),
OpCode::EnterWithEnv => {
let mut new = HashMap::new();
stack
.pop()
.unwrap_attr_set()
.as_inner()
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect_into(&mut new);
*env = env.clone().enter_with(new.into());
}
OpCode::PopEnv => return Ok(Consq::PopEnv),
OpCode::Assert => {
if !stack.pop().unwrap_bool() {
todo!()
}
}
}
Ok(Consq::NoOp)
}
#[derive(Constructor)]
pub struct VM<'gc> {
thunks: Box<[OpCodes]>,
funcs: Box<[F]>,
symbols: RefCell<Vec<EcoString>>,
symmap: RefCell<HashMap<EcoString, usize>>,
consts: Box<[Const]>,
jit: JITContext<'gc>,
}
impl<'gc> VM<'gc> {
pub fn get_thunk(&self, idx: usize) -> &'gc OpCodes {
unsafe { &*(&self.thunks[idx] as *const _) }
}
pub fn get_func(&self, idx: usize) -> &'gc F {
unsafe { &*(&self.funcs[idx] as *const _) }
}
pub fn get_sym(&self, idx: usize) -> Symbol {
self.symbols.borrow()[idx].clone().into()
}
pub fn new_sym<T: Into<EcoString>>(&self, sym: T) -> usize {
let sym = sym.into();
if let Some(&idx) = self.symmap.borrow().get(&sym) {
idx
} else {
self.symmap
.borrow_mut()
.insert(sym.clone(), self.symbols.borrow().len());
self.symbols.borrow_mut().push(sym);
self.symbols.borrow().len() - 1
}
}
pub fn get_const(&self, idx: usize) -> Const {
self.consts[idx].clone()
}
pub fn compile_func(&self, func: &'gc F) -> JITFunc<'gc> {
self.jit
.compile_seq(func.opcodes.iter().copied().rev(), self)
.unwrap()
}
}

View File

@@ -1,231 +0,0 @@
#![allow(unused_macros)]
extern crate test;
use hashbrown::HashMap;
use test::{Bencher, black_box};
use ecow::EcoString;
use crate::compile::compile;
use crate::ir::downgrade;
use crate::ty::common::Const;
use crate::ty::public::*;
use super::run;
#[inline]
fn test_expr(expr: &str, expected: Value) {
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
let prog = compile(downgraded);
dbg!(&prog);
assert_eq!(run(prog).unwrap(), expected);
}
macro_rules! map {
($($k:expr => $v:expr),*) => {
{
#[allow(unused_mut)]
let mut m = HashMap::new();
$(
m.insert($k, $v);
)*
m
}
};
}
macro_rules! thunk {
() => {
Value::Thunk
};
}
macro_rules! int {
($e:expr) => {
Value::Const(Const::Int($e))
};
}
macro_rules! float {
($e:expr) => {
Value::Const(Const::Float($e as f64))
};
}
macro_rules! boolean {
($e:expr) => {
Value::Const(Const::Bool($e))
};
}
macro_rules! string {
($e:expr) => {
Value::Const(Const::String(EcoString::from($e)))
};
}
macro_rules! symbol {
($e:expr) => {
Symbol::from($e.to_string())
};
}
macro_rules! list {
($($x:tt)*) => (
Value::List(List::new(vec![$($x)*]))
);
}
macro_rules! attrs {
($($x:tt)*) => (
Value::AttrSet(AttrSet::new(map!{$($x)*}))
)
}
#[test]
fn test_arith() {
test_expr("1", int!(1));
test_expr("1.", float!(1));
test_expr("-1", int!(-1));
test_expr("-1.", float!(-1));
test_expr("1 + 1", int!(2));
test_expr("1 + 1.", float!(2));
test_expr("1. + 1", float!(2));
test_expr("1. + 1.", float!(2));
test_expr("1 - 1", int!(0));
test_expr("1 - 1.", float!(0));
test_expr("1. - 1", float!(0));
test_expr("1. - 1.", float!(0));
test_expr("1 * 1", int!(1));
test_expr("1 * 1.", float!(1));
test_expr("1. * 1", float!(1));
test_expr("1. * 1.", float!(1));
test_expr("1 / 1", int!(1));
test_expr("1 / 1.", float!(1));
test_expr("1. / 1", float!(1));
test_expr("1. / 1.", float!(1));
}
#[test]
fn test_cmp() {
test_expr("1 < 2", boolean!(true));
test_expr("1 < 1", boolean!(false));
test_expr("1 > 0", boolean!(true));
test_expr("1 > 1", boolean!(false));
test_expr("1 <= 1", boolean!(true));
test_expr("1 <= 0", boolean!(false));
test_expr("1 >= 1", boolean!(true));
test_expr("1 >= 2", boolean!(false));
}
#[test]
fn test_string() {
test_expr(r#""test""#, string!("test"));
test_expr(r#""hello" + " world""#, string!("hello world"));
}
#[test]
fn test_bool() {
test_expr("true", boolean!(true));
test_expr("false", boolean!(false));
test_expr("!false", boolean!(true));
test_expr("true && false", boolean!(false));
test_expr("true || false", boolean!(true));
test_expr("true -> false", boolean!(false));
}
#[test]
fn test_list() {
test_expr(
"[ 1 2 3 true ]",
list![int!(1), int!(2), int!(3), boolean!(true)],
);
test_expr(
"[ 1 2 ] ++ [ 3 4 ]",
list![int!(1), int!(2), int!(3), int!(4)],
);
}
#[test]
fn test_attrs() {
test_expr(
"{ a = 1; }",
attrs! {
symbol!("a") => int!(1)
},
);
test_expr("{ a = 1; }.a", int!(1));
test_expr("{ a = 1; }.b or 1", int!(1));
test_expr(
"{ a = { a = 1; }; }.a",
attrs! {
symbol!("a") => int!(1)
},
);
test_expr("{ a.b = 1; }.a.b", int!(1));
test_expr(
"{ a.b = 1; a.c = 2; }",
attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } },
);
test_expr("{ a.b = 1; } ? a.b", boolean!(true));
test_expr(
"{ a.b = 1; } // { a.c = 2; }",
attrs! { symbol!("a") => attrs!{ symbol!("c") => int!(2) } },
);
}
#[test]
fn test_if() {
test_expr("if true || false then 1 else 2", int!(1));
}
#[test]
fn test_with() {
test_expr(r#"with { a = 1; }; a"#, int!(1));
}
#[test]
fn test_let() {
test_expr(r#"let a = 1; in a"#, int!(1));
test_expr(r#"let a = 1; b = a; in b"#, int!(1));
test_expr(r#"let a = { a = 1; }; b = "a"; in a.${b}"#, int!(1));
test_expr(
r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#,
attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } },
);
}
#[test]
fn test_func() {
test_expr("(x: x) 1", int!(1));
test_expr("(x: x) (x: x) 1", int!(1));
test_expr("(x: y: x + y) 1 1", int!(2));
test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3));
test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3));
test_expr(
"(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }",
int!(3),
);
}
#[test]
#[ignore]
fn test_fib() {
test_expr(
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
int!(832040),
)
}
#[bench]
fn bench_fib(b: &mut Bencher) {
b.iter(|| {
test_expr(
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
int!(832040),
);
black_box(())
})
}