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"
|
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"
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/env.rs
39
src/env.rs
@@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
250
src/eval/mod.rs
250
src/eval/mod.rs
@@ -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!()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
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