feat: initial parallel impl
This commit is contained in:
71
Cargo.lock
generated
71
Cargo.lock
generated
@@ -80,26 +80,6 @@ version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
@@ -205,6 +185,16 @@ dependencies = [
|
||||
"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]]
|
||||
name = "inkwell"
|
||||
version = "0.6.0"
|
||||
@@ -271,16 +261,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
@@ -327,12 +307,12 @@ dependencies = [
|
||||
name = "nixjit"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"derive_more",
|
||||
"ecow",
|
||||
"hashbrown 0.15.3",
|
||||
"inkwell",
|
||||
"itertools",
|
||||
"priority-queue",
|
||||
"regex",
|
||||
"replace_with",
|
||||
"rnix",
|
||||
@@ -347,16 +327,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
name = "priority-queue"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
"autocfg",
|
||||
"equivalent",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -387,15 +365,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -500,12 +469,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
|
||||
@@ -27,7 +27,7 @@ derive_more = { version = "2.0", features = ["full"] }
|
||||
ecow = "0.2"
|
||||
regex = "1.11"
|
||||
hashbrown = "0.15"
|
||||
dashmap = "6.1"
|
||||
priority-queue = "2.5"
|
||||
replace_with = "0.1"
|
||||
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use itertools::Itertools;
|
||||
use nixjit::error::Error;
|
||||
use nixjit::error::Result;
|
||||
use nixjit::ir::downgrade;
|
||||
use nixjit::eval::eval;
|
||||
use nixjit::engine::eval;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut args = std::env::args();
|
||||
|
||||
@@ -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::ir::{Downgraded, Ir};
|
||||
use crate::ty::public::Value;
|
||||
use crate::ty::internal as i;
|
||||
use crate::error::Result;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub struct Engine {
|
||||
|
||||
thunks: Box<[RwLock<Thunk>]>,
|
||||
func_offset: usize,
|
||||
tasks: PriorityQueue<CompileTask, usize>
|
||||
}
|
||||
|
||||
pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
||||
let mut engine = Engine::new();
|
||||
engine.eval(downgraded.top_level)
|
||||
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())))?
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> 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> {
|
||||
expr.eval(self).map(|val| val.to_public(self, &mut HashSet::new()))
|
||||
pub fn eval(&mut self, expr: Ir, env: &VmEnv) -> Result<i::Value> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
@@ -23,12 +23,7 @@ fn test_expr(expr: &str, expected: Value) {
|
||||
macro_rules! map {
|
||||
($($k:expr => $v:expr),*) => {
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
let mut m = HashMap::new();
|
||||
$(
|
||||
m.insert($k, $v);
|
||||
)*
|
||||
m
|
||||
BTreeMap::from([$(($k, $v),)*])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
39
src/env.rs
39
src/env.rs
@@ -1,15 +1,15 @@
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ecow::EcoString;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::{ir::Ir, ty::internal::Value};
|
||||
use crate::ty::internal::Value;
|
||||
|
||||
pub struct Env<K: Hash + Eq, V> {
|
||||
let_: Rc<LetEnv<V>>,
|
||||
with: Rc<With<K, V>>,
|
||||
args: Rc<Vec<V>>,
|
||||
last: Option<Rc<Env<K, V>>>,
|
||||
}
|
||||
|
||||
pub struct LetEnv<V> {
|
||||
@@ -17,8 +17,7 @@ pub struct LetEnv<V> {
|
||||
last: Option<Rc<LetEnv<V>>>,
|
||||
}
|
||||
|
||||
pub type VmEnv<'gc> = Env<usize, Value<'gc>>;
|
||||
pub type IrEnv<'gc> = Env<usize, Ir>;
|
||||
pub type VmEnv = Env<EcoString, Value>;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct With<K: Hash + Eq, V> {
|
||||
@@ -39,16 +38,15 @@ pub enum Type {
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
|
||||
pub fn new(map: Vec<V>) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
pub fn new(map: Vec<V>) -> Self {
|
||||
Self {
|
||||
let_: LetEnv::new(map),
|
||||
with: With {
|
||||
map: None,
|
||||
last: None,
|
||||
}.into(),
|
||||
args: Vec::new().into(),
|
||||
last: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_arg(&self, level: usize) -> &V {
|
||||
@@ -68,39 +66,32 @@ impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
|
||||
}
|
||||
|
||||
#[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();
|
||||
Rc::make_mut(&mut args).push(val);
|
||||
Rc::new(Self {
|
||||
Self {
|
||||
let_: self.let_.clone(),
|
||||
with: self.with.clone(),
|
||||
last: Some(self),
|
||||
args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
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(),
|
||||
last: Some(self),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn enter_with(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
pub fn enter_with(&self, map: Rc<HashMap<K, V>>) -> Self {
|
||||
Self {
|
||||
let_: self.let_.clone(),
|
||||
with: self.with.clone().enter(map),
|
||||
args: self.args.clone(),
|
||||
last: Some(self),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn leave(&self) -> Rc<Self> {
|
||||
self.last.clone().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
|
||||
use inkwell::AddressSpace;
|
||||
use inkwell::context::Context;
|
||||
@@ -10,7 +9,6 @@ use inkwell::values::{BasicValueEnum, FunctionValue};
|
||||
|
||||
use crate::env::VmEnv;
|
||||
use crate::eval::Engine;
|
||||
use crate::ty::internal::{Thunk, Value};
|
||||
|
||||
use super::{JITContext, JITValue, ValueTag, JITValueData};
|
||||
|
||||
@@ -228,11 +226,8 @@ extern "C" fn helper_debug(value: JITValue) {
|
||||
dbg!(value.tag);
|
||||
}
|
||||
|
||||
extern "C" fn helper_capture_env<'gc>(thunk: JITValue, env: *const VmEnv<'gc>) {
|
||||
let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() };
|
||||
let env = unsafe { Rc::from_raw(env) };
|
||||
thunk.capture_env(env.clone());
|
||||
std::mem::forget(env);
|
||||
extern "C" fn helper_capture_env(thunk: JITValue, env: *const VmEnv) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
let env = unsafe { env.as_ref() }.unwrap();
|
||||
let val: JITValue = env.lookup_with(&sym).unwrap().clone().into();
|
||||
val
|
||||
todo!()
|
||||
}
|
||||
|
||||
extern "C" fn helper_force<'gc>(
|
||||
extern "C" fn helper_force(
|
||||
thunk: JITValue,
|
||||
vm: NonNull<Engine>,
|
||||
jit: *const JITContext<'gc>,
|
||||
jit: *const JITContext,
|
||||
) -> JITValue {
|
||||
if !matches!(thunk.tag, ValueTag::Thunk) {
|
||||
return thunk;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use inkwell::OptimizationLevel;
|
||||
@@ -9,7 +8,7 @@ use inkwell::execution_engine::ExecutionEngine;
|
||||
use inkwell::module::Module;
|
||||
|
||||
use crate::env::VmEnv;
|
||||
use crate::ty::internal::{Value, Thunk};
|
||||
use crate::ty::internal::Value;
|
||||
|
||||
mod helpers;
|
||||
mod compile;
|
||||
@@ -51,29 +50,29 @@ pub union JITValueData {
|
||||
ptr: *const (),
|
||||
}
|
||||
|
||||
impl<'gc> From<JITValue> for Value<'gc> {
|
||||
impl From<JITValue> for Value {
|
||||
fn from(value: JITValue) -> Self {
|
||||
use ValueTag::*;
|
||||
match value.tag {
|
||||
Int => Value::Int(unsafe { value.data.int }),
|
||||
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: unsafe { Rc::from_raw(value.data.ptr as *const _) },
|
||||
}),
|
||||
}), */
|
||||
_ => todo!("not implemented for {:?}", value.tag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value<'_>> for JITValue {
|
||||
impl From<Value> for JITValue {
|
||||
fn from(value: Value) -> Self {
|
||||
match value {
|
||||
Value::Int(int) => JITValue {
|
||||
tag: ValueTag::Int,
|
||||
data: JITValueData { int },
|
||||
},
|
||||
Value::Func(func) => JITValue {
|
||||
/* Value::Func(func) => JITValue {
|
||||
tag: ValueTag::Function,
|
||||
data: JITValueData {
|
||||
ptr: Rc::into_raw(func) as *const _,
|
||||
@@ -84,23 +83,23 @@ impl From<Value<'_>> for JITValue {
|
||||
data: JITValueData {
|
||||
ptr: Rc::into_raw(thunk.thunk) as *const _,
|
||||
},
|
||||
},
|
||||
}, */
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>);
|
||||
type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue;
|
||||
pub struct JITFunc<'ctx>(F, PhantomData<&'ctx mut ()>);
|
||||
type F = unsafe extern "C" fn(*const VmEnv) -> JITValue;
|
||||
|
||||
impl<'gc> From<F<'gc>> for JITFunc<'gc> {
|
||||
fn from(value: F<'gc>) -> Self {
|
||||
impl From<F> for JITFunc<'_> {
|
||||
fn from(value: F) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Deref for JITFunc<'gc> {
|
||||
type Target = F<'gc>;
|
||||
impl Deref for JITFunc<'_> {
|
||||
type Target = F;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
||||
246
src/eval/mod.rs
246
src/eval/mod.rs
@@ -1,141 +1,285 @@
|
||||
use crate::ty::common::Const;
|
||||
use crate::ty::internal::Value;
|
||||
use crate::ir::{self, Downgraded};
|
||||
use crate::ty::public as p;
|
||||
use crate::error::Result;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ecow::EcoVec;
|
||||
|
||||
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 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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
impl Evaluate for ir::List {
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
Value::List(List::from(
|
||||
self.items
|
||||
.into_iter()
|
||||
.map(|val| val.eval(engine, env))
|
||||
.collect::<Result<EcoVec<_>>>()?,
|
||||
))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::HasAttr {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
impl Evaluate for ir::UnOp {
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
// 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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
|
||||
Value::Func(Rc::new(ThunkRef::new(self.idx).into())).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Call {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::With {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &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()))
|
||||
}
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::ConcatStrings {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
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 {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
|
||||
Value::String(self.val).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Const {
|
||||
fn eval<'a>(self, _: &'a Engine) -> Result<Value<'a>> {
|
||||
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
|
||||
match self.val {
|
||||
Const::Null => Value::Null,
|
||||
Const::Int(x) => Value::Int(x),
|
||||
Const::Float(x) => Value::Float(x),
|
||||
Const::Bool(x) => Value::Bool(x),
|
||||
}.ok()
|
||||
}
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Var {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, env: &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<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
env.lookup_arg(self.level).clone().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::LetVar {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
env.lookup_let(self.level, self.idx).clone().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Thunk {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, _: &Engine, _: &VmEnv) -> Result<Value> {
|
||||
Value::Thunk(Rc::new(Thunk::new(self.idx).into())).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Path {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
fn eval(self, engine: &Engine, env: &VmEnv) -> Result<Value> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(expr: Downgraded) -> Result<p::Value> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -7,13 +7,14 @@ use rnix::ast::HasEntry;
|
||||
use rnix::ast::{self, Expr};
|
||||
|
||||
use crate::builtins::ir_env;
|
||||
use crate::engine::Engine;
|
||||
use crate::env::VmEnv;
|
||||
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::internal::Value;
|
||||
use crate::eval::jit::{JITContext, JITCompile};
|
||||
use crate::eval::Evaluate;
|
||||
use crate::engine::Engine;
|
||||
use crate::ty::public::Symbol;
|
||||
|
||||
mod utils;
|
||||
use utils::*;
|
||||
@@ -26,8 +27,12 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
||||
let ir = ir.resolve(&mut ctx, &env)?;
|
||||
Ok(Downgraded {
|
||||
top_level: ir,
|
||||
thunks: ctx.thunks.into(),
|
||||
funcs: ctx.funcs.into(),
|
||||
func_offset: ctx.thunks.len(),
|
||||
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)]
|
||||
pub enum Ir {
|
||||
pub enum Ir {
|
||||
$(
|
||||
$ty($ty),
|
||||
)*
|
||||
@@ -63,16 +68,6 @@ macro_rules! ir {
|
||||
}
|
||||
|
||||
impl Ir {
|
||||
#[inline]
|
||||
fn boxed(self) -> Box<Self> {
|
||||
Box::new(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ok(self) -> Result<Self> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ref(&self) -> IrRef {
|
||||
match self {
|
||||
@@ -104,9 +99,9 @@ macro_rules! 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 {
|
||||
$(Ir::$ty(ir) => ir.eval(engine),)*
|
||||
$(Ir::$ty(ir) => ir.eval(engine, env),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +153,18 @@ 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 {
|
||||
fn from(value: T) -> Self {
|
||||
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 {
|
||||
env: EnvNode::MultiArg(map, alias),
|
||||
prev: Some(self),
|
||||
@@ -301,7 +312,7 @@ impl DowngradeContext {
|
||||
pub struct Downgraded {
|
||||
pub top_level: Ir,
|
||||
pub thunks: Box<[Ir]>,
|
||||
pub funcs: Box<[Func]>,
|
||||
pub func_offset: usize,
|
||||
}
|
||||
|
||||
impl DowngradeContext {
|
||||
@@ -362,10 +373,13 @@ impl Attrs {
|
||||
.get_mut(&ident)
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!(
|
||||
.try_unwrap_attrs()
|
||||
.map_err(|_| {
|
||||
Error::DowngradeError(format!(
|
||||
r#"attribute '{}' already defined"#,
|
||||
Symbol::from(ident)
|
||||
)))
|
||||
))
|
||||
})
|
||||
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
|
||||
} else {
|
||||
let mut attrs = Attrs {
|
||||
@@ -641,7 +655,11 @@ impl Downgrade for ast::Path {
|
||||
let parts = self
|
||||
.parts()
|
||||
.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) => {
|
||||
interpol.expr().unwrap().downgrade(ctx)
|
||||
}
|
||||
@@ -706,11 +724,14 @@ impl ConcatStrings {
|
||||
}
|
||||
|
||||
impl Downgrade for ast::Literal {
|
||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||
fn downgrade(self, _: &mut DowngradeContext) -> Result<Ir> {
|
||||
match self.kind() {
|
||||
ast::LiteralKind::Integer(int) => Const::from(int.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()
|
||||
}
|
||||
@@ -729,17 +750,20 @@ impl String {
|
||||
}
|
||||
|
||||
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();
|
||||
Var { sym }.ir().ok()
|
||||
}
|
||||
}
|
||||
|
||||
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::*;
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl Let {
|
||||
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 bindings = self
|
||||
.bindings
|
||||
|
||||
@@ -7,74 +7,94 @@ use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::engine::Engine;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ty::public::Symbol;
|
||||
|
||||
use super::super::public as p;
|
||||
use super::Value;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet<'gc> {
|
||||
data: HashMap<EcoString, Value<'gc>>,
|
||||
pub struct AttrSet {
|
||||
data: HashMap<EcoString, Value>,
|
||||
}
|
||||
|
||||
impl<'gc> From<HashMap<EcoString, Value<'gc>>> for AttrSet<'gc> {
|
||||
fn from(data: HashMap<EcoString, Value<'gc>>) -> Self {
|
||||
impl From<HashMap<EcoString, Value>> for AttrSet {
|
||||
fn from(data: HashMap<EcoString, Value>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Deref for AttrSet<'gc> {
|
||||
type Target = HashMap<EcoString, Value<'gc>>;
|
||||
impl Deref for AttrSet {
|
||||
type Target = HashMap<EcoString, Value>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> AttrSet<'gc> {
|
||||
impl AttrSet {
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
AttrSet {
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
todo!()
|
||||
}
|
||||
self.data.insert(sym, val);
|
||||
}
|
||||
|
||||
pub fn select(&self, sym: &EcoString) -> Option<Value<'gc>> {
|
||||
self.data.get(sym).cloned()
|
||||
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))))
|
||||
};
|
||||
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 {
|
||||
self.data.get(sym).is_some()
|
||||
pub fn has_attr(&self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<bool> {
|
||||
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() {
|
||||
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
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
pub fn from_inner(data: HashMap<EcoString, Value<'gc>>) -> Self {
|
||||
pub fn from_inner(data: HashMap<EcoString, Value>) -> Self {
|
||||
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()
|
||||
&& std::iter::zip(
|
||||
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))
|
||||
}
|
||||
|
||||
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(
|
||||
self.data
|
||||
.iter()
|
||||
|
||||
@@ -5,11 +5,11 @@ use crate::env::VmEnv;
|
||||
use crate::ir;
|
||||
pub struct Func<'gc> {
|
||||
pub func: &'gc ir::Func,
|
||||
pub env: Rc<VmEnv<'gc>>,
|
||||
pub env: Rc<VmEnv>,
|
||||
}
|
||||
|
||||
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 {
|
||||
func,
|
||||
env,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Weak;
|
||||
|
||||
use ecow::EcoVec;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use crate::env::VmEnv;
|
||||
@@ -9,50 +11,64 @@ use crate::engine::Engine;
|
||||
use super::Value;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct List<'gc> {
|
||||
data: Vec<Value<'gc>>,
|
||||
pub struct List {
|
||||
data: EcoVec<Value>,
|
||||
}
|
||||
|
||||
impl<'gc> Default for List<'gc> {
|
||||
impl Default for List {
|
||||
fn default() -> Self {
|
||||
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 {
|
||||
List { data: Vec::new() }
|
||||
List { data: EcoVec::new() }
|
||||
}
|
||||
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn concat(&mut self, other: &List<'gc>) {
|
||||
pub fn concat(&mut self, other: &List) {
|
||||
for elem in other.data.iter() {
|
||||
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| {
|
||||
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
|
||||
}
|
||||
|
||||
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(
|
||||
self.data
|
||||
.iter()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::hash::Hash;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use derive_more::{IsVariant, Unwrap};
|
||||
use ecow::EcoString;
|
||||
@@ -21,39 +22,84 @@ mod primop;
|
||||
mod string;
|
||||
|
||||
pub use attrset::*;
|
||||
pub use func::*;
|
||||
pub use list::List;
|
||||
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)]
|
||||
pub enum Value<'gc> {
|
||||
pub enum Value {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(EcoString),
|
||||
Null,
|
||||
Thunk(Thunk<'gc>),
|
||||
AttrSet(Rc<AttrSet<'gc>>),
|
||||
List(Rc<List<'gc>>),
|
||||
Thunk(Rc<RwLock<Thunk>>),
|
||||
AttrSet(Rc<AttrSet>),
|
||||
List(List),
|
||||
Catchable(EcoString),
|
||||
PrimOp(Rc<PrimOp>),
|
||||
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
|
||||
Func(Rc<Func<'gc>>),
|
||||
PartialPrimOp(Rc<PartialPrimOp>),
|
||||
Func(Rc<RwLock<ThunkRef>>),
|
||||
}
|
||||
|
||||
impl Hash for Value<'_> {
|
||||
impl Hash for Value {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
use Value::*;
|
||||
std::mem::discriminant(self).hash(state);
|
||||
match self {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Value<'gc> {
|
||||
impl Value {
|
||||
fn eq_impl(&self, other: &Self) -> bool {
|
||||
use Value::*;
|
||||
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 {
|
||||
use Value::*;
|
||||
match (self, other) {
|
||||
(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Value<'_> {}
|
||||
impl Eq for Value {}
|
||||
|
||||
#[derive(IsVariant, Unwrap, Clone)]
|
||||
pub enum ValueAsRef<'v, 'gc> {
|
||||
pub enum ValueAsRef<'v> {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(&'v str),
|
||||
String(&'v EcoString),
|
||||
Null,
|
||||
Thunk(&'v Thunk<'gc>),
|
||||
AttrSet(&'v AttrSet<'gc>),
|
||||
List(&'v List<'gc>),
|
||||
Thunk(&'v RwLock<Thunk>),
|
||||
AttrSet(&'v AttrSet),
|
||||
List(&'v List),
|
||||
Catchable(&'v str),
|
||||
PrimOp(&'v PrimOp),
|
||||
PartialPrimOp(&'v PartialPrimOp<'gc>),
|
||||
Func(&'v Func<'gc>),
|
||||
PartialPrimOp(&'v PartialPrimOp),
|
||||
Func(&'v RwLock<ThunkRef>),
|
||||
}
|
||||
|
||||
impl<'gc, 'v> Value<'gc> {
|
||||
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> {
|
||||
impl Value {
|
||||
pub fn as_ref(&self) -> ValueAsRef {
|
||||
use Value::*;
|
||||
use ValueAsRef as R;
|
||||
match self {
|
||||
@@ -120,7 +166,7 @@ impl<'gc, 'v> Value<'gc> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'gc> Value<'gc> {
|
||||
impl Value {
|
||||
pub fn ok(self) -> Result<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::*;
|
||||
if matches!(arg, Value::Catchable(_)) {
|
||||
*self = arg;
|
||||
return Ok(());
|
||||
}
|
||||
*self = match self {
|
||||
PrimOp(func) => func.call(arg, vm),
|
||||
PartialPrimOp(func) => func.call(arg, vm),
|
||||
PrimOp(func) => func.call(arg, engine),
|
||||
PartialPrimOp(func) => func.call(arg, engine),
|
||||
Catchable(_) => return Ok(()),
|
||||
_ => todo!(),
|
||||
}?;
|
||||
@@ -293,7 +339,7 @@ impl<'gc> Value<'gc> {
|
||||
|
||||
pub fn push(&mut self, elem: Self) -> &mut 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(_) = elem {
|
||||
*self = elem;
|
||||
@@ -310,7 +356,7 @@ impl<'gc> Value<'gc> {
|
||||
}
|
||||
match (self, other) {
|
||||
(Value::List(a), Value::List(b)) => {
|
||||
Rc::make_mut(a).concat(&b);
|
||||
a.concat(&b);
|
||||
}
|
||||
(Value::Catchable(_), _) => (),
|
||||
_ => 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 {
|
||||
Value::AttrSet(attrs) => attrs
|
||||
.select(sym)
|
||||
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))),
|
||||
.select(path),
|
||||
Value::Catchable(_) => return Ok(self),
|
||||
_ => Err(Error::EvalError(format!(
|
||||
"cannot select from {:?}",
|
||||
@@ -358,9 +403,9 @@ impl<'gc> Value<'gc> {
|
||||
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 {
|
||||
Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default),
|
||||
Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default),
|
||||
Value::Catchable(_) => return Ok(self),
|
||||
_ => {
|
||||
return Err(Error::EvalError(format!(
|
||||
@@ -373,15 +418,15 @@ impl<'gc> Value<'gc> {
|
||||
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 {
|
||||
let val = Value::Bool(attrs.has_attr(sym));
|
||||
let val = Value::Bool(attrs.has_attr(path)?);
|
||||
*self = val;
|
||||
} else if let Value::Catchable(_) = self {
|
||||
} else {
|
||||
*self = Value::Bool(false);
|
||||
}
|
||||
self
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn coerce_to_string(&mut self) -> &mut Self {
|
||||
@@ -393,7 +438,18 @@ impl<'gc> Value<'gc> {
|
||||
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 p::Value;
|
||||
if seen.contains(self) {
|
||||
@@ -402,11 +458,11 @@ impl<'gc> Value<'gc> {
|
||||
match self {
|
||||
AttrSet(attrs) => {
|
||||
seen.insert(self.clone());
|
||||
attrs.to_public(vm, seen)
|
||||
attrs.to_public(engine, seen)
|
||||
}
|
||||
List(list) => {
|
||||
seen.insert(self.clone());
|
||||
list.to_public(vm, seen)
|
||||
list.to_public(engine, seen)
|
||||
}
|
||||
Catchable(catchable) => Value::Catchable(catchable.clone().into()),
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::Value;
|
||||
pub struct PrimOp {
|
||||
pub name: &'static str,
|
||||
arity: usize,
|
||||
func: for<'gc> fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
|
||||
func: fn(Vec<Value>, &Engine) -> Result<Value>,
|
||||
}
|
||||
|
||||
impl PartialEq for PrimOp {
|
||||
@@ -21,7 +21,7 @@ impl PartialEq for 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);
|
||||
args.push(arg);
|
||||
if self.arity > 1 {
|
||||
@@ -39,21 +39,21 @@ impl PrimOp {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PartialPrimOp<'gc> {
|
||||
pub struct PartialPrimOp {
|
||||
pub name: &'static str,
|
||||
arity: usize,
|
||||
args: Vec<Value<'gc>>,
|
||||
func: fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
|
||||
args: Vec<Value>,
|
||||
func: fn(Vec<Value>, &Engine) -> Result<Value>,
|
||||
}
|
||||
|
||||
impl PartialEq for PartialPrimOp<'_> {
|
||||
impl PartialEq for PartialPrimOp {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> PartialPrimOp<'gc> {
|
||||
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> {
|
||||
impl PartialPrimOp {
|
||||
pub fn call(self: &mut Rc<Self>, arg: Value, ctx: &Engine) -> Result<Value> {
|
||||
let func = self.func;
|
||||
let Some(ret) = ({
|
||||
let self_mut = Rc::make_mut(self);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use hashbrown::HashMap;
|
||||
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;
|
||||
@@ -55,7 +55,7 @@ impl Symbol {
|
||||
|
||||
#[derive(Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet {
|
||||
data: HashMap<Symbol, Value>,
|
||||
data: BTreeMap<Symbol, Value>,
|
||||
}
|
||||
|
||||
impl Debug for AttrSet {
|
||||
|
||||
328
src/vm/mod.rs
328
src/vm/mod.rs
@@ -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()
|
||||
}
|
||||
}
|
||||
231
src/vm/test.rs
231
src/vm/test.rs
@@ -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(())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user