Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0fd846e844
|
|||
|
484cfa4610
|
|||
|
51f8df9cca
|
|||
|
d3442e87e7
|
|||
|
20b2b6f1ef
|
|||
|
7d6168fdae
|
|||
|
c548c4c6ac
|
|||
|
541db02361
|
|||
|
c8276c1729
|
|||
|
f3bf44ab97
|
|||
|
99dce2e778
|
|||
|
c3ace28af1
|
|||
|
319c12c1f4
|
|||
|
cc06369c5e
|
|||
|
b41fd38bcc
|
|||
|
5291e49313
|
|||
|
a47a08b051
|
|||
|
53cbb37b00
|
|||
|
f380e5fd70
|
|||
|
b0b73439fd
|
|||
|
6bb86ca2cf
|
|||
|
c898b577b0
|
|||
|
dcd22ad1f3
|
|||
|
2a19ddb279
|
|||
|
177acfabcf
|
|||
|
36f29a9cac
|
|||
|
9b3c3d6fe9
|
|||
|
736402dc53
|
|||
|
b4249ccd11
|
|||
|
96fb6033a4
|
|||
|
d0298ce2a6
|
|||
|
b4db46d48a
|
|||
|
9e172bf013
|
|||
|
6d26716412
|
|||
|
e17c48f2d9
|
|||
|
4124156d52
|
|||
|
af5a312e1e
|
|||
|
f98d623c13
|
|||
|
29e959894d
|
|||
|
95ebddf272
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
|||||||
target/
|
target/
|
||||||
|
|
||||||
/.direnv/
|
/.direnv/
|
||||||
|
.env
|
||||||
|
|
||||||
|
/flamegraph.svg
|
||||||
|
/perf.data*
|
||||||
|
|||||||
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -23,16 +23,6 @@ version = "1.0.98"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "archery"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8967cd1cc9e9e1954f644e14fbd6042fe9a37da96c52a67e44a2ac18261f8561"
|
|
||||||
dependencies = [
|
|
||||||
"static_assertions",
|
|
||||||
"triomphe",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -313,8 +303,8 @@ dependencies = [
|
|||||||
"inkwell",
|
"inkwell",
|
||||||
"itertools",
|
"itertools",
|
||||||
"regex",
|
"regex",
|
||||||
|
"replace_with",
|
||||||
"rnix",
|
"rnix",
|
||||||
"rpds",
|
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
@@ -388,6 +378,12 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "replace_with"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rnix"
|
name = "rnix"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -410,15 +406,6 @@ dependencies = [
|
|||||||
"text-size",
|
"text-size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rpds"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0e15515d3ce3313324d842629ea4905c25a13f81953eadb88f85516f59290a4"
|
|
||||||
dependencies = [
|
|
||||||
"archery",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -478,12 +465,6 @@ version = "1.13.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_assertions"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.101"
|
||||||
@@ -541,12 +522,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "triomphe"
|
|
||||||
version = "0.1.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
|||||||
12
Cargo.toml
12
Cargo.toml
@@ -11,19 +11,23 @@ name = "repl"
|
|||||||
required-features = ["repl"]
|
required-features = ["repl"]
|
||||||
|
|
||||||
[profile.perf]
|
[profile.perf]
|
||||||
debug = 1
|
debug = 2
|
||||||
|
strip = false
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
strip = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rnix = "0.12"
|
rnix = "0.12"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
rpds = "1.1"
|
derive_more = { version = "2.0", features = ["full"] }
|
||||||
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"
|
||||||
|
replace_with = "0.1"
|
||||||
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
|
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
|
||||||
|
|
||||||
rustyline = { version = "15.0", optional = true }
|
rustyline = { version = "15.0", optional = true }
|
||||||
|
|||||||
30
src/bin/compile.rs
Normal file
30
src/bin/compile.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use nixjit::compile::compile;
|
||||||
|
use nixjit::error::Error;
|
||||||
|
use nixjit::error::Result;
|
||||||
|
use nixjit::ir::downgrade;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let mut args = std::env::args();
|
||||||
|
if args.len() != 2 {
|
||||||
|
eprintln!("Usage: {} expr", args.next().unwrap());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
args.next();
|
||||||
|
let expr = args.next().unwrap();
|
||||||
|
let root = rnix::Root::parse(&expr);
|
||||||
|
if !root.errors().is_empty() {
|
||||||
|
return Err(Error::ParseError(
|
||||||
|
root.errors().iter().map(|err| err.to_string()).join(";"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let expr = root.tree().expr().unwrap();
|
||||||
|
let downgraded = downgrade(expr)?;
|
||||||
|
let prog = compile(downgraded);
|
||||||
|
println!("{prog:?}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
31
src/bin/eval.rs
Normal file
31
src/bin/eval.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use nixjit::compile::compile;
|
||||||
|
use nixjit::error::Error;
|
||||||
|
use nixjit::error::Result;
|
||||||
|
use nixjit::ir::downgrade;
|
||||||
|
use nixjit::vm::run;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let mut args = std::env::args();
|
||||||
|
if args.len() != 2 {
|
||||||
|
eprintln!("Usage: {} expr", args.next().unwrap());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
args.next();
|
||||||
|
let expr = args.next().unwrap();
|
||||||
|
let root = rnix::Root::parse(&expr);
|
||||||
|
if !root.errors().is_empty() {
|
||||||
|
return Err(Error::ParseError(
|
||||||
|
root.errors().iter().map(|err| err.to_string()).join(";"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let expr = root.tree().expr().unwrap();
|
||||||
|
let downgraded = downgrade(expr)?;
|
||||||
|
let prog = compile(downgraded);
|
||||||
|
println!("{}", run(prog)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
use inkwell::context::Context;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::{DefaultEditor, Result};
|
use rustyline::{DefaultEditor, Result};
|
||||||
@@ -6,7 +5,7 @@ use rustyline::{DefaultEditor, Result};
|
|||||||
use nixjit::compile::compile;
|
use nixjit::compile::compile;
|
||||||
use nixjit::error::Error;
|
use nixjit::error::Error;
|
||||||
use nixjit::ir::downgrade;
|
use nixjit::ir::downgrade;
|
||||||
use nixjit::vm::{JITContext, run};
|
use nixjit::vm::run;
|
||||||
|
|
||||||
macro_rules! unwrap {
|
macro_rules! unwrap {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
@@ -43,9 +42,7 @@ fn main() -> Result<()> {
|
|||||||
let expr = root.tree().expr().unwrap();
|
let expr = root.tree().expr().unwrap();
|
||||||
let downgraded = unwrap!(downgrade(expr));
|
let downgraded = unwrap!(downgrade(expr));
|
||||||
let prog = compile(downgraded);
|
let prog = compile(downgraded);
|
||||||
let ctx = Context::create();
|
println!("{}", unwrap!(run(prog)));
|
||||||
let jit = JITContext::new(&ctx);
|
|
||||||
println!("{}", unwrap!(run(prog, jit)));
|
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
println!("CTRL-C");
|
println!("CTRL-C");
|
||||||
|
|||||||
@@ -1,60 +1,12 @@
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crate::ty::internal::{AttrSet, Const, PrimOp, Value};
|
use crate::ty::common::Const;
|
||||||
use crate::vm::{Env, VM};
|
use crate::ir::{DowngradeContext, Ir};
|
||||||
|
|
||||||
pub fn env<'vm>(vm: &'vm VM) -> Env<'vm> {
|
|
||||||
let mut env = Env::empty();
|
|
||||||
env.insert(vm.new_sym("true"), Value::Const(Const::Bool(true)));
|
|
||||||
env.insert(vm.new_sym("false"), Value::Const(Const::Bool(false)));
|
|
||||||
|
|
||||||
let primops = [
|
|
||||||
PrimOp::new("add", 2, |_, args| {
|
|
||||||
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.add(second).ok()
|
|
||||||
}),
|
|
||||||
PrimOp::new("sub", 2, |_, args| {
|
|
||||||
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.add(second.neg()).ok()
|
|
||||||
}),
|
|
||||||
PrimOp::new("mul", 2, |_, args| {
|
|
||||||
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.mul(second).ok()
|
|
||||||
}),
|
|
||||||
PrimOp::new("div", 2, |_, args| {
|
|
||||||
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.div(second)
|
|
||||||
}),
|
|
||||||
PrimOp::new("lessThan", 2, |_, args| {
|
|
||||||
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.lt(second).ok()
|
|
||||||
}),
|
|
||||||
PrimOp::new("seq", 2, |vm, args| {
|
|
||||||
let [mut first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.force(vm).unwrap();
|
|
||||||
second.ok()
|
|
||||||
}),
|
|
||||||
PrimOp::new("deepSeq", 2, |vm, args| {
|
|
||||||
let [mut first, second]: [Value; 2] = args.try_into().unwrap();
|
|
||||||
first.force_deep(vm).unwrap();
|
|
||||||
second.ok()
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<usize, Ir> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for primop in primops {
|
map.insert(ctx.new_sym("true"), ctx.new_const(Const::Bool(true)).ir());
|
||||||
let primop = Rc::new(primop);
|
map.insert(ctx.new_sym("false"), ctx.new_const(Const::Bool(false)).ir());
|
||||||
env.insert(
|
|
||||||
vm.new_sym(format!("__{}", primop.name)),
|
|
||||||
Value::PrimOp(primop.clone()),
|
|
||||||
);
|
|
||||||
map.insert(vm.new_sym(primop.name), Value::PrimOp(primop));
|
|
||||||
}
|
|
||||||
let attrs: Rc<_> = AttrSet::from_inner(map).into();
|
|
||||||
let mut builtins = Value::AttrSet(attrs);
|
|
||||||
builtins.push_attr(vm.new_sym("builtins"), Value::Builtins);
|
|
||||||
|
|
||||||
env.insert(vm.new_sym("builtins"), builtins);
|
map
|
||||||
env
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use ecow::EcoString;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use crate::ty::common::Const;
|
||||||
|
use crate::ty::internal::Param;
|
||||||
use crate::ty::internal::{Const, Param};
|
|
||||||
|
|
||||||
type Slice<T> = Box<[T]>;
|
type Slice<T> = Box<[T]>;
|
||||||
|
|
||||||
@@ -14,40 +14,45 @@ pub enum OpCode {
|
|||||||
Const { idx: usize },
|
Const { idx: usize },
|
||||||
/// load a dynamic var onto stack
|
/// load a dynamic var onto stack
|
||||||
LookUp { sym: usize },
|
LookUp { sym: usize },
|
||||||
|
/// load a var from let binding onto stack
|
||||||
|
LookUpLet { level: usize, idx: usize },
|
||||||
/// load a thunk lazily onto stack
|
/// load a thunk lazily onto stack
|
||||||
LoadThunk { idx: usize },
|
LoadThunk { idx: usize },
|
||||||
|
/// load a thunk value onto stack
|
||||||
|
LoadValue { idx: usize },
|
||||||
/// let TOS capture current environment
|
/// let TOS capture current environment
|
||||||
CaptureEnv,
|
CaptureEnv,
|
||||||
/// force TOS to value
|
/// force TOS to value
|
||||||
ForceValue,
|
ForceValue,
|
||||||
|
/// TODO:
|
||||||
|
InsertValue,
|
||||||
|
|
||||||
/// [ .. func args @ .. ] consume (`arity` + 1) elements, call `func` with args` of length `arity`
|
/// [ .. func arg ] consume 2 elements, call `func` with arg
|
||||||
/// Example: __add 1 2 => [ LookUp("__add") Const(1) Const(2) Call(2) ]
|
Call,
|
||||||
Call { arity: usize },
|
|
||||||
/// make a function
|
/// make a function
|
||||||
Func { idx: usize },
|
Func { idx: usize },
|
||||||
|
/// load a function argument
|
||||||
|
Arg { level: usize },
|
||||||
|
|
||||||
/// consume 1 element, assert TOS is true
|
/// consume 1 element, assert TOS is true
|
||||||
Assert,
|
Assert,
|
||||||
|
|
||||||
/// jump forward
|
/// jump forward
|
||||||
Jmp { step: usize },
|
Jmp { step: usize },
|
||||||
/// [ .. cond ] consume 1 element, if `cond`` is true, then jump forward
|
|
||||||
JmpIfTrue { step: usize },
|
|
||||||
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
|
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
|
||||||
JmpIfFalse { step: usize },
|
JmpIfFalse { step: usize },
|
||||||
|
|
||||||
/// push an empty attribute set onto stack
|
/// push an empty attribute set onto stack
|
||||||
AttrSet { cap: usize },
|
AttrSet { cap: usize },
|
||||||
/// finalize the recursive attribute set at TOS
|
/// finalize the recursive attribute set at TOS
|
||||||
FinalizeRec,
|
FinalizeLet,
|
||||||
/// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
|
/// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
|
||||||
PushStaticAttr { name: usize },
|
PushStaticAttr { name: usize },
|
||||||
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
|
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
|
||||||
PushDynamicAttr,
|
PushDynamicAttr,
|
||||||
|
|
||||||
/// push an empty list onto stack
|
/// push an empty list onto stack
|
||||||
List,
|
List { cap: usize },
|
||||||
/// [ .. list elem ] consume 1 element, push `elem` into `list`
|
/// [ .. list elem ] consume 1 element, push `elem` into `list`
|
||||||
PushElem,
|
PushElem,
|
||||||
|
|
||||||
@@ -72,10 +77,12 @@ pub enum OpCode {
|
|||||||
SelectDynamic,
|
SelectDynamic,
|
||||||
/// [ .. set sym default ] select `sym` from `set` or `default`
|
/// [ .. set sym default ] select `sym` from `set` or `default`
|
||||||
SelectDynamicOrDefault,
|
SelectDynamicOrDefault,
|
||||||
/// enter the environment of the attribute set at TOS
|
/// enter the with environment of the attribute set at TOS
|
||||||
EnterEnv,
|
EnterWithEnv,
|
||||||
/// exit current envrironment
|
/// exit current envrironment
|
||||||
LeaveEnv,
|
LeaveEnv,
|
||||||
|
/// TODO:
|
||||||
|
PopEnv,
|
||||||
|
|
||||||
/// illegal operation, used as termporary placeholder
|
/// illegal operation, used as termporary placeholder
|
||||||
Illegal,
|
Illegal,
|
||||||
|
|||||||
159
src/compile.rs
159
src/compile.rs
@@ -35,7 +35,7 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compile(mut self, ir: ir::Ir) -> OpCodes {
|
fn compile(mut self, ir: ir::Ir) -> OpCodes {
|
||||||
ir.compile(&mut self);
|
ir.compile_force(&mut self);
|
||||||
self.opcodes()
|
self.opcodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +60,12 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Compile {
|
pub trait Compile: Sized {
|
||||||
fn compile(self, comp: &mut Compiler);
|
fn compile(self, comp: &mut Compiler);
|
||||||
|
fn compile_force(self, comp: &mut Compiler) {
|
||||||
|
self.compile(comp);
|
||||||
|
comp.push(OpCode::ForceValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CompileWithLength {
|
pub trait CompileWithLength {
|
||||||
@@ -81,6 +85,9 @@ impl Compile for ir::Const {
|
|||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
comp.push(OpCode::Const { idx: self.idx });
|
comp.push(OpCode::Const { idx: self.idx });
|
||||||
}
|
}
|
||||||
|
fn compile_force(self, comp: &mut Compiler) {
|
||||||
|
self.compile(comp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compile for ir::Var {
|
impl Compile for ir::Var {
|
||||||
@@ -89,10 +96,28 @@ impl Compile for ir::Var {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Arg {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::Arg { level: self.level })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::LetVar {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::LookUpLet {
|
||||||
|
level: self.level,
|
||||||
|
idx: self.idx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Compile for ir::Thunk {
|
impl Compile for ir::Thunk {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
comp.push(OpCode::LoadThunk { idx: self.idx });
|
comp.push(OpCode::LoadThunk { idx: self.idx });
|
||||||
}
|
}
|
||||||
|
fn compile_force(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::LoadValue { idx: self.idx });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compile for ir::Attrs {
|
impl Compile for ir::Attrs {
|
||||||
@@ -101,29 +126,30 @@ impl Compile for ir::Attrs {
|
|||||||
cap: self.stcs.len() + self.dyns.len(),
|
cap: self.stcs.len() + self.dyns.len(),
|
||||||
});
|
});
|
||||||
for stc in self.stcs {
|
for stc in self.stcs {
|
||||||
|
let thunk = stc.1.is_thunk();
|
||||||
stc.1.compile(comp);
|
stc.1.compile(comp);
|
||||||
if !self.rec {
|
if thunk {
|
||||||
comp.push(OpCode::CaptureEnv);
|
comp.push(OpCode::CaptureEnv);
|
||||||
}
|
}
|
||||||
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
||||||
}
|
}
|
||||||
for dynamic in self.dyns {
|
for dynamic in self.dyns {
|
||||||
dynamic.0.compile(comp);
|
let thunk = dynamic.1.is_thunk();
|
||||||
|
dynamic.0.compile_force(comp);
|
||||||
dynamic.1.compile(comp);
|
dynamic.1.compile(comp);
|
||||||
if !self.rec {
|
if thunk {
|
||||||
comp.push(OpCode::CaptureEnv);
|
comp.push(OpCode::CaptureEnv);
|
||||||
}
|
}
|
||||||
comp.push(OpCode::PushDynamicAttr)
|
comp.push(OpCode::PushDynamicAttr)
|
||||||
}
|
}
|
||||||
if self.rec {
|
|
||||||
comp.push(OpCode::FinalizeRec);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compile for ir::List {
|
impl Compile for ir::List {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
comp.push(OpCode::List);
|
comp.push(OpCode::List {
|
||||||
|
cap: self.items.len(),
|
||||||
|
});
|
||||||
for item in self.items {
|
for item in self.items {
|
||||||
item.compile(comp);
|
item.compile(comp);
|
||||||
comp.push(OpCode::PushElem);
|
comp.push(OpCode::PushElem);
|
||||||
@@ -136,11 +162,11 @@ impl Compile for ir::UnOp {
|
|||||||
use ir::UnOpKind::*;
|
use ir::UnOpKind::*;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Neg => {
|
Neg => {
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Neg });
|
comp.push(OpCode::UnOp { op: UnOp::Neg });
|
||||||
}
|
}
|
||||||
Not => {
|
Not => {
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,95 +178,95 @@ impl Compile for ir::BinOp {
|
|||||||
use ir::BinOpKind::*;
|
use ir::BinOpKind::*;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Add => {
|
Add => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Add });
|
comp.push(OpCode::BinOp { op: BinOp::Add });
|
||||||
}
|
}
|
||||||
Mul => {
|
Mul => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Mul });
|
comp.push(OpCode::BinOp { op: BinOp::Mul });
|
||||||
}
|
}
|
||||||
Div => {
|
Div => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Div });
|
comp.push(OpCode::BinOp { op: BinOp::Div });
|
||||||
}
|
}
|
||||||
And => {
|
And => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::And });
|
comp.push(OpCode::BinOp { op: BinOp::And });
|
||||||
}
|
}
|
||||||
Or => {
|
Or => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||||
}
|
}
|
||||||
Eq => {
|
Eq => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||||
}
|
}
|
||||||
Lt => {
|
Lt => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||||
}
|
}
|
||||||
Con => {
|
Con => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Con });
|
comp.push(OpCode::BinOp { op: BinOp::Con });
|
||||||
}
|
}
|
||||||
Upd => {
|
Upd => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Upd });
|
comp.push(OpCode::BinOp { op: BinOp::Upd });
|
||||||
}
|
}
|
||||||
|
|
||||||
Sub => {
|
Sub => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Sub });
|
comp.push(OpCode::BinOp { op: BinOp::Sub });
|
||||||
}
|
}
|
||||||
Impl => {
|
Impl => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||||
}
|
}
|
||||||
Neq => {
|
Neq => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
}
|
}
|
||||||
Gt => {
|
Gt => {
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||||
}
|
}
|
||||||
Leq => {
|
Leq => {
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
}
|
}
|
||||||
Geq => {
|
Geq => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
}
|
}
|
||||||
|
|
||||||
PipeL => {
|
PipeL => {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
comp.push(OpCode::Call { arity: 1 });
|
comp.push(OpCode::Call);
|
||||||
}
|
}
|
||||||
PipeR => {
|
PipeR => {
|
||||||
self.rhs.compile(comp);
|
self.rhs.compile_force(comp);
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile_force(comp);
|
||||||
comp.push(OpCode::Call { arity: 1 });
|
comp.push(OpCode::Call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,13 +276,14 @@ impl Compile for ir::HasAttr {
|
|||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
self.lhs.compile(comp);
|
self.lhs.compile(comp);
|
||||||
for attr in self.rhs {
|
for attr in self.rhs {
|
||||||
|
comp.push(OpCode::ForceValue);
|
||||||
match attr {
|
match attr {
|
||||||
ir::Attr::Str(sym) => {
|
ir::Attr::Str(sym) => {
|
||||||
comp.push(OpCode::AttrSet { cap: 0 });
|
comp.push(OpCode::AttrSet { cap: 0 });
|
||||||
comp.push(OpCode::SelectOrDefault { sym })
|
comp.push(OpCode::SelectOrDefault { sym })
|
||||||
}
|
}
|
||||||
ir::Attr::Dynamic(dynamic) => {
|
ir::Attr::Dynamic(dynamic) => {
|
||||||
dynamic.compile(comp);
|
dynamic.compile_force(comp);
|
||||||
comp.push(OpCode::AttrSet { cap: 0 });
|
comp.push(OpCode::AttrSet { cap: 0 });
|
||||||
comp.push(OpCode::SelectDynamicOrDefault);
|
comp.push(OpCode::SelectDynamicOrDefault);
|
||||||
}
|
}
|
||||||
@@ -281,6 +308,7 @@ impl Compile for ir::Select {
|
|||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
self.expr.compile(comp);
|
self.expr.compile(comp);
|
||||||
for attr in self.attrpath {
|
for attr in self.attrpath {
|
||||||
|
comp.push(OpCode::ForceValue);
|
||||||
match attr {
|
match attr {
|
||||||
ir::Attr::Str(sym) => {
|
ir::Attr::Str(sym) => {
|
||||||
comp.push(OpCode::AttrSet { cap: 0 });
|
comp.push(OpCode::AttrSet { cap: 0 });
|
||||||
@@ -303,11 +331,7 @@ impl Compile for ir::Select {
|
|||||||
let last = comp.pop().unwrap();
|
let last = comp.pop().unwrap();
|
||||||
let _ = comp.pop();
|
let _ = comp.pop();
|
||||||
default.compile(comp);
|
default.compile(comp);
|
||||||
match last {
|
comp.push(last);
|
||||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }),
|
|
||||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let last = comp.pop().unwrap();
|
let last = comp.pop().unwrap();
|
||||||
@@ -320,14 +344,18 @@ impl Compile for ir::Select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn compile_force(self, comp: &mut Compiler) {
|
||||||
|
self.compile(comp);
|
||||||
|
comp.push(OpCode::ForceValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compile for ir::ConcatStrings {
|
impl Compile for ir::ConcatStrings {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
let mut iter = self.parts.into_iter();
|
let mut iter = self.parts.into_iter();
|
||||||
iter.next().unwrap().compile(comp);
|
iter.next().unwrap().compile_force(comp);
|
||||||
for item in iter {
|
for item in iter {
|
||||||
item.compile(comp);
|
item.compile_force(comp);
|
||||||
comp.push(OpCode::ConcatString);
|
comp.push(OpCode::ConcatString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,9 +389,19 @@ impl Compile for ir::If {
|
|||||||
|
|
||||||
impl Compile for ir::Let {
|
impl Compile for ir::Let {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
self.attrs.compile(comp);
|
comp.push(OpCode::List {
|
||||||
comp.push(OpCode::EnterEnv);
|
cap: self.bindings.len(),
|
||||||
|
});
|
||||||
|
for (_, val) in self.bindings {
|
||||||
|
val.compile(comp);
|
||||||
|
comp.push(OpCode::PushElem);
|
||||||
|
}
|
||||||
|
comp.push(OpCode::FinalizeLet);
|
||||||
|
let thunk = self.expr.is_thunk();
|
||||||
self.expr.compile(comp);
|
self.expr.compile(comp);
|
||||||
|
if thunk {
|
||||||
|
comp.push(OpCode::CaptureEnv);
|
||||||
|
}
|
||||||
comp.push(OpCode::LeaveEnv);
|
comp.push(OpCode::LeaveEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +409,7 @@ impl Compile for ir::Let {
|
|||||||
impl Compile for ir::With {
|
impl Compile for ir::With {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
self.namespace.compile(comp);
|
self.namespace.compile(comp);
|
||||||
comp.push(OpCode::EnterEnv);
|
comp.push(OpCode::EnterWithEnv);
|
||||||
self.expr.compile(comp);
|
self.expr.compile(comp);
|
||||||
comp.push(OpCode::LeaveEnv);
|
comp.push(OpCode::LeaveEnv);
|
||||||
}
|
}
|
||||||
@@ -393,12 +431,11 @@ impl Compile for ir::LoadFunc {
|
|||||||
|
|
||||||
impl Compile for ir::Call {
|
impl Compile for ir::Call {
|
||||||
fn compile(self, comp: &mut Compiler) {
|
fn compile(self, comp: &mut Compiler) {
|
||||||
let arity = self.args.len();
|
self.func.compile_force(comp);
|
||||||
self.func.compile(comp);
|
|
||||||
self.args.into_iter().for_each(|arg| {
|
self.args.into_iter().for_each(|arg| {
|
||||||
arg.compile(comp);
|
arg.compile(comp);
|
||||||
|
comp.push(OpCode::Call);
|
||||||
});
|
});
|
||||||
comp.push(OpCode::Call { arity });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
142
src/env.rs
Normal file
142
src/env.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use std::hash::Hash;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::{ir::Ir, 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> {
|
||||||
|
map: Vec<V>,
|
||||||
|
last: Option<Rc<LetEnv<V>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type VmEnv<'gc> = Env<usize, Value<'gc>>;
|
||||||
|
pub type IrEnv<'gc> = Env<usize, Ir>;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct With<K: Hash + Eq, V> {
|
||||||
|
map: Option<Rc<HashMap<K, V>>>,
|
||||||
|
last: Option<Rc<With<K, V>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LetNode<K: Hash + Eq, V> {
|
||||||
|
Let(Rc<HashMap<K, V>>),
|
||||||
|
MultiArg(Rc<HashMap<K, V>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Type {
|
||||||
|
Arg,
|
||||||
|
Let,
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
|
||||||
|
pub fn new(map: Vec<V>) -> Rc<Self> {
|
||||||
|
Rc::new(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 {
|
||||||
|
&self.args[self.args.len() - level - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_let(&self, level: usize, idx: usize) -> &V {
|
||||||
|
self.let_.lookup(level, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_with(&self, symbol: &K) -> Option<&V> {
|
||||||
|
self.with.lookup(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_with(&self) -> bool {
|
||||||
|
self.with.map.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn enter_arg(self: Rc<Self>, val: V) -> Rc<Self> {
|
||||||
|
let mut args = self.args.clone();
|
||||||
|
Rc::make_mut(&mut args).push(val);
|
||||||
|
Rc::new(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 {
|
||||||
|
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 {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Clone> LetEnv<V> {
|
||||||
|
pub fn new(map: Vec<V>) -> Rc<Self> {
|
||||||
|
Rc::new(Self { map, last: None })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&self, level: usize, idx: usize) -> &V {
|
||||||
|
let mut cur = self;
|
||||||
|
for _ in 0..level {
|
||||||
|
cur = cur.last.as_ref().unwrap();
|
||||||
|
}
|
||||||
|
&cur.map[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> {
|
||||||
|
Rc::new(Self {
|
||||||
|
map,
|
||||||
|
last: Some(self),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq + Clone, V: Clone> With<K, V> {
|
||||||
|
pub fn lookup(&self, symbol: &K) -> Option<&V> {
|
||||||
|
if let Some(val) = self.map.as_ref()?.get(symbol) {
|
||||||
|
return Some(val);
|
||||||
|
}
|
||||||
|
self.last.as_ref().and_then(|env| env.lookup(symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> {
|
||||||
|
Rc::new(Self {
|
||||||
|
map: Some(map),
|
||||||
|
last: Some(self),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ pub enum Error {
|
|||||||
DowngradeError(String),
|
DowngradeError(String),
|
||||||
#[error("error occurred during evaluation stage: {0}")]
|
#[error("error occurred during evaluation stage: {0}")]
|
||||||
EvalError(String),
|
EvalError(String),
|
||||||
|
#[error("error occurred during JIT compile stage: {0}")]
|
||||||
|
CompileError(#[from] inkwell::builder::BuilderError),
|
||||||
#[error("unknown error")]
|
#[error("unknown error")]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|||||||
688
src/ir.rs
688
src/ir.rs
@@ -1,15 +1,22 @@
|
|||||||
|
use derive_more::{IsVariant, TryUnwrap, Unwrap};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rnix::ast::HasEntry;
|
||||||
use rnix::ast::{self, Expr};
|
use rnix::ast::{self, Expr};
|
||||||
|
|
||||||
|
use crate::builtins::ir_env;
|
||||||
use crate::compile::*;
|
use crate::compile::*;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::ty::internal as i;
|
use crate::ty::common as c;
|
||||||
|
|
||||||
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
||||||
let mut ctx = DowngradeContext::new();
|
let mut ctx = DowngradeContext::new();
|
||||||
|
let builtins = ir_env(&mut ctx);
|
||||||
|
let env = Env::new(&builtins);
|
||||||
let ir = expr.downgrade(&mut ctx)?;
|
let ir = expr.downgrade(&mut ctx)?;
|
||||||
|
let ir = ir.resolve(&mut ctx, &env)?;
|
||||||
Ok(Downgraded {
|
Ok(Downgraded {
|
||||||
top_level: ir,
|
top_level: ir,
|
||||||
consts: ctx.consts.into(),
|
consts: ctx.consts.into(),
|
||||||
@@ -20,14 +27,6 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Downcast<T: Sized>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
fn downcast_ref(&self) -> Option<&T>;
|
|
||||||
fn downcast_mut(&mut self) -> Option<&mut T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ir {
|
macro_rules! ir {
|
||||||
(
|
(
|
||||||
$(
|
$(
|
||||||
@@ -38,22 +37,59 @@ macro_rules! ir {
|
|||||||
)
|
)
|
||||||
,*$(,)?
|
,*$(,)?
|
||||||
) => {
|
) => {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, IsVariant, Unwrap)]
|
||||||
pub enum Ir {
|
pub enum Ir {
|
||||||
$(
|
$(
|
||||||
$ty($ty),
|
$ty($ty),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, IsVariant, TryUnwrap)]
|
||||||
|
pub enum IrRef<'a> {
|
||||||
|
$(
|
||||||
|
$ty(&'a $ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, IsVariant, TryUnwrap)]
|
||||||
|
pub enum IrMut<'a> {
|
||||||
|
$(
|
||||||
|
$ty(&'a mut $ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
impl Ir {
|
impl Ir {
|
||||||
|
#[inline]
|
||||||
fn boxed(self) -> Box<Self> {
|
fn boxed(self) -> Box<Self> {
|
||||||
Box::new(self)
|
Box::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ok(self) -> Result<Self> {
|
fn ok(self) -> Result<Self> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> IrRef {
|
||||||
|
match self {
|
||||||
|
$(Ir::$ty(ir) => IrRef::$ty(ir),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> IrMut {
|
||||||
|
match self {
|
||||||
|
$(Ir::$ty(ir) => IrMut::$ty(ir),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
match self {
|
||||||
|
$(Ir::$ty(ir) => ir.resolve(ctx, env),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Compile for Ir {
|
impl Compile for Ir {
|
||||||
fn compile(self, ctx: &mut Compiler) {
|
fn compile(self, ctx: &mut Compiler) {
|
||||||
match self {
|
match self {
|
||||||
@@ -67,7 +103,7 @@ macro_rules! ir {
|
|||||||
#[$($x)*]
|
#[$($x)*]
|
||||||
)*
|
)*
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct $ty {
|
pub struct $ty {
|
||||||
$(
|
$(
|
||||||
pub $name : $elemtype,
|
pub $name : $elemtype,
|
||||||
)*
|
)*
|
||||||
@@ -78,27 +114,12 @@ macro_rules! ir {
|
|||||||
Ir::$ty(self)
|
Ir::$ty(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Downcast<$ty> for Ir {
|
|
||||||
fn downcast_ref(&self) -> Option<&$ty> {
|
|
||||||
match self {
|
|
||||||
Ir::$ty(value) => Some(value),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn downcast_mut(&mut self) -> Option<&mut $ty> {
|
|
||||||
match self {
|
|
||||||
Ir::$ty(value) => Some(value),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ir! {
|
ir! {
|
||||||
Attrs => { stcs: HashMap<usize, Ir>, dyns: Vec<DynamicAttrPair>, rec: bool },
|
Attrs => { stcs: HashMap<usize, Ir>, dyns: Vec<DynamicAttrPair> },
|
||||||
List => { items: Vec<Ir> },
|
List => { items: Vec<Ir> },
|
||||||
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
||||||
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
||||||
@@ -108,13 +129,19 @@ ir! {
|
|||||||
LoadFunc => { idx: usize },
|
LoadFunc => { idx: usize },
|
||||||
Call => { func: Box<Ir>, args: Vec<Ir> },
|
Call => { func: Box<Ir>, args: Vec<Ir> },
|
||||||
|
|
||||||
Let => { attrs: Attrs, expr: Box<Ir> },
|
Let => { bindings: Vec<(usize, Ir)>, expr: Box<Ir> },
|
||||||
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
||||||
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
||||||
ConcatStrings => { parts: Vec<Ir> },
|
ConcatStrings => { parts: Vec<Ir> },
|
||||||
|
#[derive(Copy)]
|
||||||
Const => { idx: usize },
|
Const => { idx: usize },
|
||||||
|
#[derive(Copy)]
|
||||||
Var => { sym: usize },
|
Var => { sym: usize },
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
|
Arg => { level: usize },
|
||||||
|
#[derive(Copy)]
|
||||||
|
LetVar => { level: usize, idx: usize },
|
||||||
|
#[derive(Copy)]
|
||||||
Thunk => { idx: usize },
|
Thunk => { idx: usize },
|
||||||
Path => { expr: Box<Ir> },
|
Path => { expr: Box<Ir> },
|
||||||
}
|
}
|
||||||
@@ -122,19 +149,148 @@ ir! {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DynamicAttrPair(pub Ir, pub Ir);
|
pub struct DynamicAttrPair(pub Ir, pub Ir);
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct DowngradeContext {
|
pub struct DowngradeContext {
|
||||||
thunks: Vec<Ir>,
|
thunks: Vec<Ir>,
|
||||||
funcs: Vec<Func>,
|
funcs: Vec<Func>,
|
||||||
consts: Vec<i::Const>,
|
consts: Vec<c::Const>,
|
||||||
constmap: HashMap<i::Const, usize>,
|
constmap: HashMap<c::Const, usize>,
|
||||||
symbols: Vec<EcoString>,
|
symbols: Vec<EcoString>,
|
||||||
symmap: HashMap<EcoString, usize>,
|
symmap: HashMap<EcoString, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Env<'a, 'env> {
|
||||||
|
env: EnvNode<'a>,
|
||||||
|
prev: Option<&'env Env<'a, 'env>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnvNode<'a> {
|
||||||
|
Builtins(&'a HashMap<usize, Ir>),
|
||||||
|
Let(&'a Vec<usize>),
|
||||||
|
SingleArg(usize),
|
||||||
|
MultiArg(HashMap<usize, Option<Ir>>, Option<usize>),
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LookupResult {
|
||||||
|
Builtin(Ir),
|
||||||
|
Let { level: usize, idx: usize },
|
||||||
|
SingleArg { level: usize },
|
||||||
|
MultiArg { level: usize, default: Option<Ir> },
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'env> Env<'a, 'env> {
|
||||||
|
fn new(base: &'a HashMap<usize, Ir>) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::Builtins(base),
|
||||||
|
prev: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_let(&'env self, map: &'a Vec<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::Let(map),
|
||||||
|
prev: Some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_single_arg(&'env self, ident: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::SingleArg(ident),
|
||||||
|
prev: Some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_multi_arg(&'env self, map: HashMap<usize, Option<Ir>>, alias: Option<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::MultiArg(map, alias),
|
||||||
|
prev: Some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_with(&'env self) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::With,
|
||||||
|
prev: Some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _lookup(
|
||||||
|
&self,
|
||||||
|
ident: usize,
|
||||||
|
mut arg_level: usize,
|
||||||
|
mut let_level: usize,
|
||||||
|
has_with: bool,
|
||||||
|
) -> core::result::Result<LookupResult, ()> {
|
||||||
|
use EnvNode::*;
|
||||||
|
let mut has_with = has_with;
|
||||||
|
match &self.env {
|
||||||
|
Builtins(map) => {
|
||||||
|
return if let Some(ir) = map.get(&ident) {
|
||||||
|
Ok(LookupResult::Builtin(ir.clone()))
|
||||||
|
} else if has_with {
|
||||||
|
Ok(LookupResult::With)
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Let(map) => {
|
||||||
|
if let Ok(idx) = map.binary_search(&ident) {
|
||||||
|
return Ok(LookupResult::Let {
|
||||||
|
level: let_level,
|
||||||
|
idx,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let_level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SingleArg(arg) => {
|
||||||
|
if *arg == ident {
|
||||||
|
return Ok(LookupResult::SingleArg { level: arg_level });
|
||||||
|
} else {
|
||||||
|
arg_level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MultiArg(set, alias) => {
|
||||||
|
if let Some(default) = set.get(&ident) {
|
||||||
|
return Ok(LookupResult::MultiArg {
|
||||||
|
level: arg_level,
|
||||||
|
default: default.clone(),
|
||||||
|
});
|
||||||
|
} else if *alias == Some(ident) {
|
||||||
|
return Ok(LookupResult::SingleArg { level: arg_level });
|
||||||
|
} else {
|
||||||
|
arg_level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
With => has_with = true,
|
||||||
|
}
|
||||||
|
self.prev
|
||||||
|
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
|
||||||
|
.map_or_else(|| unreachable!(), |x| x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, ident: usize) -> core::result::Result<LookupResult, ()> {
|
||||||
|
self._lookup(ident, 0, 0, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DowngradeContext {
|
||||||
|
fn new() -> Self {
|
||||||
|
DowngradeContext {
|
||||||
|
thunks: Vec::new(),
|
||||||
|
funcs: Vec::new(),
|
||||||
|
consts: Vec::new(),
|
||||||
|
constmap: HashMap::new(),
|
||||||
|
symbols: Vec::new(),
|
||||||
|
symmap: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Downgraded {
|
pub struct Downgraded {
|
||||||
pub top_level: Ir,
|
pub top_level: Ir,
|
||||||
pub consts: Box<[i::Const]>,
|
pub consts: Box<[c::Const]>,
|
||||||
pub symbols: Vec<EcoString>,
|
pub symbols: Vec<EcoString>,
|
||||||
pub symmap: HashMap<EcoString, usize>,
|
pub symmap: HashMap<EcoString, usize>,
|
||||||
pub thunks: Box<[Ir]>,
|
pub thunks: Box<[Ir]>,
|
||||||
@@ -142,10 +298,6 @@ pub struct Downgraded {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DowngradeContext {
|
impl DowngradeContext {
|
||||||
fn new() -> DowngradeContext {
|
|
||||||
DowngradeContext::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_thunk(&mut self, thunk: Ir) -> Thunk {
|
fn new_thunk(&mut self, thunk: Ir) -> Thunk {
|
||||||
let idx = self.thunks.len();
|
let idx = self.thunks.len();
|
||||||
self.thunks.push(thunk);
|
self.thunks.push(thunk);
|
||||||
@@ -158,7 +310,7 @@ impl DowngradeContext {
|
|||||||
LoadFunc { idx }
|
LoadFunc { idx }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_const(&mut self, cnst: i::Const) -> Const {
|
pub fn new_const(&mut self, cnst: c::Const) -> Const {
|
||||||
if let Some(&idx) = self.constmap.get(&cnst) {
|
if let Some(&idx) = self.constmap.get(&cnst) {
|
||||||
Const { idx }
|
Const { idx }
|
||||||
} else {
|
} else {
|
||||||
@@ -170,7 +322,7 @@ impl DowngradeContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize {
|
pub fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize {
|
||||||
let sym = sym.into();
|
let sym = sym.into();
|
||||||
if let Some(&idx) = self.symmap.get(&sym) {
|
if let Some(&idx) = self.symmap.get(&sym) {
|
||||||
idx
|
idx
|
||||||
@@ -180,10 +332,48 @@ impl DowngradeContext {
|
|||||||
self.symbols.len() - 1
|
self.symbols.len() - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_sym(&self, idx: usize) -> &EcoString {
|
||||||
|
&self.symbols[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> {
|
||||||
|
let self_ptr = self as *mut Self;
|
||||||
|
self.funcs.get_mut(idx).map_or_else(
|
||||||
|
|| unreachable!(),
|
||||||
|
|func| {
|
||||||
|
unsafe {
|
||||||
|
let old = std::ptr::read(func);
|
||||||
|
std::ptr::write(func, old.resolve(self_ptr.as_mut().unwrap(), env)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> {
|
||||||
|
let self_ptr = self as *mut Self;
|
||||||
|
self.thunks.get_mut(idx).map_or_else(
|
||||||
|
|| unreachable!(),
|
||||||
|
|thunk| {
|
||||||
|
unsafe {
|
||||||
|
let old = std::ptr::read(thunk);
|
||||||
|
std::ptr::write(thunk, old.resolve(self_ptr.as_mut().unwrap(), env)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attrs {
|
impl Attrs {
|
||||||
fn _insert(&mut self, mut path: std::vec::IntoIter<Attr>, name: Attr, value: Ir) -> Result<()> {
|
fn _insert(
|
||||||
|
&mut self,
|
||||||
|
mut path: std::vec::IntoIter<Attr>,
|
||||||
|
name: Attr,
|
||||||
|
value: Ir,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
) -> Result<()> {
|
||||||
if let Some(attr) = path.next() {
|
if let Some(attr) = path.next() {
|
||||||
match attr {
|
match attr {
|
||||||
Attr::Str(ident) => {
|
Attr::Str(ident) => {
|
||||||
@@ -191,41 +381,37 @@ impl Attrs {
|
|||||||
self.stcs
|
self.stcs
|
||||||
.get_mut(&ident)
|
.get_mut(&ident)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.downcast_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| {
|
.try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!(
|
||||||
Error::DowngradeError(format!(
|
r#"attribute '{}' already defined"#,
|
||||||
r#""{ident}" already exsists in this set"#
|
ctx.get_sym(ident)
|
||||||
))
|
)))
|
||||||
})
|
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
|
||||||
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
|
|
||||||
} else {
|
} else {
|
||||||
let mut attrs = Attrs {
|
let mut attrs = Attrs {
|
||||||
rec: false,
|
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
attrs._insert(path, name, value)?;
|
attrs._insert(path, name, value, ctx)?;
|
||||||
assert!(self.stcs.insert(ident, attrs.ir()).is_none());
|
assert!(self.stcs.insert(ident, attrs.ir()).is_none());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Attr::Strs(string) => {
|
Attr::Strs(string) => {
|
||||||
let mut attrs = Attrs {
|
let mut attrs = Attrs {
|
||||||
rec: false,
|
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
attrs._insert(path, name, value)?;
|
attrs._insert(path, name, value, ctx)?;
|
||||||
self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir()));
|
self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Attr::Dynamic(dynamic) => {
|
Attr::Dynamic(dynamic) => {
|
||||||
let mut attrs = Attrs {
|
let mut attrs = Attrs {
|
||||||
rec: false,
|
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
attrs._insert(path, name, value)?;
|
attrs._insert(path, name, value, ctx)?;
|
||||||
self.dyns.push(DynamicAttrPair(dynamic, attrs.ir()));
|
self.dyns.push(DynamicAttrPair(dynamic, attrs.ir()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -235,7 +421,8 @@ impl Attrs {
|
|||||||
Attr::Str(ident) => {
|
Attr::Str(ident) => {
|
||||||
if self.stcs.get(&ident).is_some() {
|
if self.stcs.get(&ident).is_some() {
|
||||||
return Err(Error::DowngradeError(format!(
|
return Err(Error::DowngradeError(format!(
|
||||||
r#""{ident}" already exsists in this set"#
|
r#"attribute '{}' already defined"#,
|
||||||
|
ctx.get_sym(ident)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
self.stcs.insert(ident, value);
|
self.stcs.insert(ident, value);
|
||||||
@@ -251,10 +438,10 @@ impl Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, path: Vec<Attr>, value: Ir) -> Result<()> {
|
pub fn insert(&mut self, path: Vec<Attr>, value: Ir, ctx: &mut DowngradeContext) -> Result<()> {
|
||||||
let mut path = path.into_iter();
|
let mut path = path.into_iter();
|
||||||
let name = path.next_back().unwrap();
|
let name = path.next_back().unwrap();
|
||||||
self._insert(path, name, value)
|
self._insert(path, name, value, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _has_attr(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Option<bool> {
|
fn _has_attr(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Option<bool> {
|
||||||
@@ -262,7 +449,7 @@ impl Attrs {
|
|||||||
Some(Attr::Str(ident)) => self
|
Some(Attr::Str(ident)) => self
|
||||||
.stcs
|
.stcs
|
||||||
.get(ident)
|
.get(ident)
|
||||||
.and_then(|attrs| attrs.downcast_ref())
|
.and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok())
|
||||||
.map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)),
|
.map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)),
|
||||||
None => match name {
|
None => match name {
|
||||||
Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()),
|
Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()),
|
||||||
@@ -279,13 +466,30 @@ impl Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, TryUnwrap)]
|
||||||
pub enum Attr {
|
pub enum Attr {
|
||||||
Dynamic(Ir),
|
Dynamic(Ir),
|
||||||
Strs(ConcatStrings),
|
Strs(ConcatStrings),
|
||||||
Str(usize),
|
Str(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Attr {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Attr> {
|
||||||
|
use Attr::*;
|
||||||
|
Ok(match self {
|
||||||
|
Dynamic(ir) => Dynamic(ir.resolve(ctx, env)?),
|
||||||
|
other => other,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thunk {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
ctx.resolve_thunk(self.idx, env)?;
|
||||||
|
self.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum BinOpKind {
|
pub enum BinOpKind {
|
||||||
Add,
|
Add,
|
||||||
@@ -359,12 +563,20 @@ pub struct Func {
|
|||||||
pub enum Param {
|
pub enum Param {
|
||||||
Ident(usize),
|
Ident(usize),
|
||||||
Formals {
|
Formals {
|
||||||
|
// formals: Vec<(usize, Option<Ir>)>,
|
||||||
formals: Vec<(usize, Option<Thunk>)>,
|
formals: Vec<(usize, Option<Thunk>)>,
|
||||||
ellipsis: bool,
|
ellipsis: bool,
|
||||||
alias: Option<usize>,
|
alias: Option<usize>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LoadFunc {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
ctx.resolve_func(self.idx, env)?;
|
||||||
|
self.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait Downgrade
|
trait Downgrade
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@@ -410,6 +622,17 @@ impl Downgrade for ast::Assert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Assert {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
assertion: self.assertion.resolve(ctx, env)?.boxed(),
|
||||||
|
expr: self.expr.resolve(ctx, env)?.boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::IfElse {
|
impl Downgrade for ast::IfElse {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
If {
|
If {
|
||||||
@@ -422,6 +645,18 @@ impl Downgrade for ast::IfElse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl If {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
If {
|
||||||
|
cond: self.cond.resolve(ctx, env)?.boxed(),
|
||||||
|
consq: self.consq.resolve(ctx, env)?.boxed(),
|
||||||
|
alter: self.alter.resolve(ctx, env)?.boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Path {
|
impl Downgrade for ast::Path {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let parts = self
|
let parts = self
|
||||||
@@ -447,6 +682,16 @@ impl Downgrade for ast::Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
expr: self.expr.resolve(ctx, env)?.boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Str {
|
impl Downgrade for ast::Str {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let parts = self
|
let parts = self
|
||||||
@@ -467,6 +712,20 @@ impl Downgrade for ast::Str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConcatStrings {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
parts: self
|
||||||
|
.parts
|
||||||
|
.into_iter()
|
||||||
|
.map(|ir| ir.resolve(ctx, env))
|
||||||
|
.collect::<Result<Vec<_>>>()?,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Literal {
|
impl Downgrade for ast::Literal {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
@@ -479,20 +738,97 @@ impl Downgrade for ast::Literal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Const {
|
||||||
|
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
self.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Ident {
|
impl Downgrade for ast::Ident {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
Var {
|
let sym = ctx.new_sym(self.ident_token().unwrap().text());
|
||||||
sym: ctx.new_sym(self.to_string()),
|
Var { sym }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Var {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
use LookupResult::*;
|
||||||
|
match env
|
||||||
|
.lookup(self.sym)
|
||||||
|
.map_err(|_| Error::DowngradeError(format!("{} not found", ctx.get_sym(self.sym))))?
|
||||||
|
{
|
||||||
|
Builtin(ir) => ir,
|
||||||
|
Let { level, idx } => LetVar { level, idx }.ir(),
|
||||||
|
SingleArg { level } => Arg { level }.ir(),
|
||||||
|
MultiArg { level, default } => Select {
|
||||||
|
expr: Arg { level }.ir().boxed(),
|
||||||
|
attrpath: vec![Attr::Str(self.sym)],
|
||||||
|
default: default.map(Box::new),
|
||||||
|
}
|
||||||
|
.ir(),
|
||||||
|
With => self.ir(),
|
||||||
}
|
}
|
||||||
.ir()
|
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Arg {
|
||||||
|
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LetVar {
|
||||||
|
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::AttrSet {
|
impl Downgrade for ast::AttrSet {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let rec = self.rec_token().is_some();
|
let rec = self.rec_token().is_some();
|
||||||
downgrade_has_entry(self, rec, ctx).map(|attrs| attrs.ir())
|
let attrs = downgrade_attrs(self, ctx)?;
|
||||||
|
if rec {
|
||||||
|
let bindings = attrs
|
||||||
|
.stcs
|
||||||
|
.into_iter()
|
||||||
|
.sorted_by_key(|(k, _)| *k)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let stcs = bindings
|
||||||
|
.iter()
|
||||||
|
.map(|&(sym, _)| (sym, Var { sym }.ir()))
|
||||||
|
.collect();
|
||||||
|
Let {
|
||||||
|
bindings,
|
||||||
|
expr: Attrs { stcs, ..attrs }.ir().boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
} else {
|
||||||
|
attrs.ir()
|
||||||
|
}
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attrs {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
stcs: self
|
||||||
|
.stcs
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| Ok((k, v.resolve(ctx, env)?)))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
dyns: self
|
||||||
|
.dyns
|
||||||
|
.into_iter()
|
||||||
|
.map(|DynamicAttrPair(k, v)| {
|
||||||
|
Ok(DynamicAttrPair(k.resolve(ctx, env)?, v.resolve(ctx, env)?))
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,6 +842,20 @@ impl Downgrade for ast::List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
items: self
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.resolve(ctx, env))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::BinOp {
|
impl Downgrade for ast::BinOp {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
BinOp {
|
BinOp {
|
||||||
@@ -518,6 +868,18 @@ impl Downgrade for ast::BinOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BinOp {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
lhs: self.lhs.resolve(ctx, env)?.boxed(),
|
||||||
|
rhs: self.rhs.resolve(ctx, env)?.boxed(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::HasAttr {
|
impl Downgrade for ast::HasAttr {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let attrs = self.expr().unwrap().downgrade(ctx)?;
|
let attrs = self.expr().unwrap().downgrade(ctx)?;
|
||||||
@@ -531,6 +893,21 @@ impl Downgrade for ast::HasAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasAttr {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
lhs: self.lhs.resolve(ctx, env)?.boxed(),
|
||||||
|
rhs: self
|
||||||
|
.rhs
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| attr.resolve(ctx, env))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::UnaryOp {
|
impl Downgrade for ast::UnaryOp {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
UnOp {
|
UnOp {
|
||||||
@@ -542,6 +919,17 @@ impl Downgrade for ast::UnaryOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UnOp {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
rhs: self.rhs.resolve(ctx, env)?.boxed(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Select {
|
impl Downgrade for ast::Select {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
Select {
|
Select {
|
||||||
@@ -557,12 +945,33 @@ impl Downgrade for ast::Select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Select {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
let default = if let Some(default) = self.default {
|
||||||
|
Some(default.resolve(ctx, env)?.boxed())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
expr: self.expr.resolve(ctx, env)?.boxed(),
|
||||||
|
attrpath: self
|
||||||
|
.attrpath
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| attr.resolve(ctx, env))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
default,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::LegacyLet {
|
impl Downgrade for ast::LegacyLet {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let attrs = downgrade_has_entry(self, true, ctx)?;
|
let attrs = downgrade_attrs(self, ctx)?;
|
||||||
Select {
|
Select {
|
||||||
expr: attrs.ir().boxed(),
|
expr: attrs.ir().boxed(),
|
||||||
attrpath: vec![Attr::Str(ctx.new_sym("body".to_string()))],
|
attrpath: vec![Attr::Str(ctx.new_sym("body"))],
|
||||||
default: None,
|
default: None,
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -572,24 +981,102 @@ impl Downgrade for ast::LegacyLet {
|
|||||||
|
|
||||||
impl Downgrade for ast::LetIn {
|
impl Downgrade for ast::LetIn {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let body = self.body().unwrap();
|
let expr = self.body().unwrap().downgrade(ctx)?.boxed();
|
||||||
let attrs = downgrade_has_entry(self, true, ctx)?;
|
let mut bindings = HashMap::new();
|
||||||
let expr = body.downgrade(ctx)?.boxed();
|
for entry in self.entries() {
|
||||||
Let { attrs, expr }.ir().ok()
|
match entry {
|
||||||
|
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut bindings, ctx)?,
|
||||||
|
ast::Entry::AttrpathValue(value) => {
|
||||||
|
let mut path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?.into_iter();
|
||||||
|
let value = value.value().unwrap().downgrade(ctx)?;
|
||||||
|
let unwrap_ident = |ident: Attr| {
|
||||||
|
ident.try_unwrap_str().map_err(|_| {
|
||||||
|
Error::DowngradeError("dynamic attributes not allowed in let".into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let ident = unwrap_ident(path.next().unwrap())?;
|
||||||
|
if path.len() > 1 {
|
||||||
|
let mut attrs = bindings
|
||||||
|
.entry(ident)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
Attrs {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
})
|
||||||
|
.as_mut()
|
||||||
|
.try_unwrap_attrs()
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::DowngradeError(format!(
|
||||||
|
r#"attribute '{}' already defined"#,
|
||||||
|
ctx.get_sym(ident)
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
while path.len() > 1 {
|
||||||
|
attrs = attrs
|
||||||
|
.stcs
|
||||||
|
.entry(unwrap_ident(path.next().unwrap())?)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
Attrs {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
})
|
||||||
|
.as_mut()
|
||||||
|
.try_unwrap_attrs()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
attrs
|
||||||
|
.stcs
|
||||||
|
.insert(unwrap_ident(path.next().unwrap())?, value);
|
||||||
|
} else {
|
||||||
|
bindings.insert(ident, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bindings = bindings.into_iter().sorted_by_key(|(k, _)| *k).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).sorted().collect();
|
||||||
|
let env = env.enter_let(&map);
|
||||||
|
let bindings = self
|
||||||
|
.bindings
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
Ok((
|
||||||
|
k,
|
||||||
|
match v.resolve(ctx, &env)? {
|
||||||
|
ir @ Ir::Const(_) => ir,
|
||||||
|
ir => ctx.new_thunk(ir).ir(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
let expr = self.expr.resolve(ctx, &env)?.boxed();
|
||||||
|
Self { bindings, expr }.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::With {
|
impl Downgrade for ast::With {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let namespace = self.namespace().unwrap().downgrade(ctx)?;
|
let namespace = self.namespace().unwrap().downgrade(ctx)?.boxed();
|
||||||
if let Ir::Attrs(attrs) = namespace {
|
let expr = self.body().unwrap().downgrade(ctx)?.boxed();
|
||||||
let expr = self.body().unwrap().downgrade(ctx)?.boxed();
|
With { namespace, expr }.ir().ok()
|
||||||
Let { attrs, expr }.ir().ok()
|
}
|
||||||
} else {
|
}
|
||||||
let namespace = namespace.boxed();
|
|
||||||
let expr = self.body().unwrap().downgrade(ctx)?.boxed();
|
impl With {
|
||||||
With { namespace, expr }.ir().ok()
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
}
|
let namespace = self.namespace.resolve(ctx, env)?.boxed();
|
||||||
|
let expr = self.expr.resolve(ctx, &env.enter_with())?.boxed();
|
||||||
|
Self { namespace, expr }.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,6 +1089,24 @@ impl Downgrade for ast::Lambda {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Func {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Func> {
|
||||||
|
let env = match &self.param {
|
||||||
|
Param::Ident(ident) => env.enter_single_arg(*ident),
|
||||||
|
Param::Formals { formals, alias, .. } => env.enter_multi_arg(
|
||||||
|
formals
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|(ident, default)| (ident, default.map(Ir::Thunk)))
|
||||||
|
.collect(),
|
||||||
|
*alias,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let body = self.body.resolve(ctx, &env)?.boxed();
|
||||||
|
Ok(Self { body, ..self })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::Apply {
|
impl Downgrade for ast::Apply {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
let mut args = vec![self.argument().unwrap().downgrade(ctx)?];
|
let mut args = vec![self.argument().unwrap().downgrade(ctx)?];
|
||||||
@@ -616,6 +1121,21 @@ impl Downgrade for ast::Apply {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Call {
|
||||||
|
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
|
Self {
|
||||||
|
func: self.func.resolve(ctx, env)?.boxed(),
|
||||||
|
args: self
|
||||||
|
.args
|
||||||
|
.into_iter()
|
||||||
|
.map(|arg| arg.resolve(ctx, env))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
|
fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
|
||||||
match param {
|
match param {
|
||||||
ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))),
|
ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))),
|
||||||
@@ -636,6 +1156,7 @@ fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Resul
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.downgrade(ctx)
|
.downgrade(ctx)
|
||||||
.map(|ok| (ident, Some(ctx.new_thunk(ok))))
|
.map(|ok| (ident, Some(ctx.new_thunk(ok))))
|
||||||
|
// .map(|ok| (ident, Some(ok)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
@@ -650,14 +1171,9 @@ fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Resul
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downgrade_has_entry(
|
fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result<Attrs> {
|
||||||
has_entry: impl ast::HasEntry,
|
|
||||||
rec: bool,
|
|
||||||
ctx: &mut DowngradeContext,
|
|
||||||
) -> Result<Attrs> {
|
|
||||||
let entires = has_entry.entries();
|
let entires = has_entry.entries();
|
||||||
let mut attrs = Attrs {
|
let mut attrs = Attrs {
|
||||||
rec,
|
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -715,7 +1231,7 @@ fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Attr> {
|
|||||||
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))),
|
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))),
|
||||||
Str(string) => {
|
Str(string) => {
|
||||||
let parts = string.normalized_parts();
|
let parts = string.normalized_parts();
|
||||||
if parts.len() == 0 {
|
if parts.is_empty() {
|
||||||
Ok(Attr::Str(ctx.new_sym("")))
|
Ok(Attr::Str(ctx.new_sym("")))
|
||||||
} else if parts.len() == 1 {
|
} else if parts.len() == 1 {
|
||||||
match parts.into_iter().next().unwrap() {
|
match parts.into_iter().next().unwrap() {
|
||||||
@@ -757,5 +1273,5 @@ fn downgrade_attrpathvalue(
|
|||||||
x @ Ir::Const(_) => x,
|
x @ Ir::Const(_) => x,
|
||||||
x => ctx.new_thunk(x).ir(),
|
x => ctx.new_thunk(x).ir(),
|
||||||
};
|
};
|
||||||
attrs.insert(path, value)
|
attrs.insert(path, value, ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
396
src/jit/helpers.rs
Normal file
396
src/jit/helpers.rs
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use inkwell::AddressSpace;
|
||||||
|
use inkwell::context::Context;
|
||||||
|
use inkwell::execution_engine::ExecutionEngine;
|
||||||
|
use inkwell::module::Module;
|
||||||
|
use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType};
|
||||||
|
use inkwell::values::{BasicValueEnum, FunctionValue};
|
||||||
|
|
||||||
|
use crate::bytecode::OpCodes;
|
||||||
|
use crate::env::VmEnv;
|
||||||
|
use crate::jit::JITValueData;
|
||||||
|
use crate::ty::internal::{Thunk, Value};
|
||||||
|
use crate::vm::VM;
|
||||||
|
|
||||||
|
use super::{JITContext, JITValue, ValueTag};
|
||||||
|
|
||||||
|
pub struct Helpers<'ctx> {
|
||||||
|
pub int_type: IntType<'ctx>,
|
||||||
|
pub float_type: FloatType<'ctx>,
|
||||||
|
pub bool_type: IntType<'ctx>,
|
||||||
|
pub ptr_int_type: IntType<'ctx>,
|
||||||
|
pub ptr_type: PointerType<'ctx>,
|
||||||
|
pub value_type: StructType<'ctx>,
|
||||||
|
pub func_type: FunctionType<'ctx>,
|
||||||
|
|
||||||
|
pub new_thunk: FunctionValue<'ctx>,
|
||||||
|
|
||||||
|
pub debug: FunctionValue<'ctx>,
|
||||||
|
pub capture_env: FunctionValue<'ctx>,
|
||||||
|
pub neg: FunctionValue<'ctx>,
|
||||||
|
pub not: FunctionValue<'ctx>,
|
||||||
|
pub add: FunctionValue<'ctx>,
|
||||||
|
pub sub: FunctionValue<'ctx>,
|
||||||
|
pub eq: FunctionValue<'ctx>,
|
||||||
|
pub or: FunctionValue<'ctx>,
|
||||||
|
pub call: FunctionValue<'ctx>,
|
||||||
|
pub arg: FunctionValue<'ctx>,
|
||||||
|
pub lookup_let: FunctionValue<'ctx>,
|
||||||
|
pub lookup: FunctionValue<'ctx>,
|
||||||
|
pub force: FunctionValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> Helpers<'ctx> {
|
||||||
|
pub fn new(
|
||||||
|
context: &'ctx Context,
|
||||||
|
module: &Module<'ctx>,
|
||||||
|
execution_engine: &ExecutionEngine<'ctx>,
|
||||||
|
) -> Self {
|
||||||
|
let int_type = context.i64_type();
|
||||||
|
let float_type = context.f64_type();
|
||||||
|
let bool_type = context.bool_type();
|
||||||
|
let ptr_int_type = context.ptr_sized_int_type(execution_engine.get_target_data(), None);
|
||||||
|
let ptr_type = context.ptr_type(AddressSpace::default());
|
||||||
|
let value_type = context.struct_type(&[int_type.into(), int_type.into()], false);
|
||||||
|
let func_type = value_type.fn_type(&[ptr_type.into()], false);
|
||||||
|
|
||||||
|
let new_thunk = module.add_function(
|
||||||
|
"new_thunk",
|
||||||
|
value_type.fn_type(&[ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let debug = module.add_function(
|
||||||
|
"debug",
|
||||||
|
context.void_type().fn_type(&[value_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let capture_env = module.add_function(
|
||||||
|
"capture_env",
|
||||||
|
context
|
||||||
|
.void_type()
|
||||||
|
.fn_type(&[value_type.into(), ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let neg = module.add_function(
|
||||||
|
"neg",
|
||||||
|
value_type.fn_type(&[value_type.into(), ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let not = module.add_function(
|
||||||
|
"not",
|
||||||
|
value_type.fn_type(&[value_type.into(), ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let add = module.add_function(
|
||||||
|
"add",
|
||||||
|
value_type.fn_type(&[value_type.into(), value_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let sub = module.add_function(
|
||||||
|
"sub",
|
||||||
|
value_type.fn_type(&[value_type.into(), value_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let eq = module.add_function(
|
||||||
|
"eq",
|
||||||
|
value_type.fn_type(&[value_type.into(), value_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let or = module.add_function(
|
||||||
|
"or",
|
||||||
|
value_type.fn_type(&[value_type.into(), value_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let call = module.add_function(
|
||||||
|
"call",
|
||||||
|
value_type.fn_type(
|
||||||
|
&[value_type.into(), value_type.into(), ptr_type.into()],
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let arg = module.add_function(
|
||||||
|
"arg",
|
||||||
|
value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let lookup_let = module.add_function(
|
||||||
|
"lookup_let",
|
||||||
|
value_type.fn_type(
|
||||||
|
&[ptr_int_type.into(), ptr_int_type.into(), ptr_type.into()],
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let lookup = module.add_function(
|
||||||
|
"lookup",
|
||||||
|
value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let force = module.add_function(
|
||||||
|
"force",
|
||||||
|
value_type.fn_type(
|
||||||
|
&[value_type.into(), ptr_type.into(), ptr_type.into()],
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
execution_engine.add_global_mapping(&new_thunk, helper_new_thunk as _);
|
||||||
|
execution_engine.add_global_mapping(&debug, helper_debug as _);
|
||||||
|
execution_engine.add_global_mapping(&capture_env, helper_capture_env as _);
|
||||||
|
execution_engine.add_global_mapping(&neg, helper_neg as _);
|
||||||
|
execution_engine.add_global_mapping(¬, helper_not as _);
|
||||||
|
execution_engine.add_global_mapping(&add, helper_add as _);
|
||||||
|
execution_engine.add_global_mapping(&sub, helper_sub as _);
|
||||||
|
execution_engine.add_global_mapping(&eq, helper_eq as _);
|
||||||
|
execution_engine.add_global_mapping(&or, helper_or as _);
|
||||||
|
execution_engine.add_global_mapping(&call, helper_call as _);
|
||||||
|
execution_engine.add_global_mapping(&arg, helper_arg as _);
|
||||||
|
execution_engine.add_global_mapping(&lookup_let, helper_lookup_let as _);
|
||||||
|
execution_engine.add_global_mapping(&lookup, helper_lookup as _);
|
||||||
|
execution_engine.add_global_mapping(&force, helper_force as _);
|
||||||
|
|
||||||
|
Helpers {
|
||||||
|
int_type,
|
||||||
|
float_type,
|
||||||
|
bool_type,
|
||||||
|
ptr_int_type,
|
||||||
|
ptr_type,
|
||||||
|
value_type,
|
||||||
|
func_type,
|
||||||
|
|
||||||
|
new_thunk,
|
||||||
|
|
||||||
|
debug,
|
||||||
|
capture_env,
|
||||||
|
neg,
|
||||||
|
not,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
eq,
|
||||||
|
or,
|
||||||
|
call,
|
||||||
|
arg,
|
||||||
|
lookup_let,
|
||||||
|
lookup,
|
||||||
|
force,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_int(&self, int: i64) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value_type
|
||||||
|
.const_named_struct(&[
|
||||||
|
self.int_type.const_int(ValueTag::Int as _, false).into(),
|
||||||
|
self.int_type.const_int(int as _, false).into(),
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_float(&self, float: f64) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value_type
|
||||||
|
.const_named_struct(&[
|
||||||
|
self.int_type.const_int(ValueTag::Float as _, false).into(),
|
||||||
|
self.float_type.const_float(float).into(),
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_bool(&self, bool: bool) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value_type
|
||||||
|
.const_named_struct(&[
|
||||||
|
self.int_type.const_int(ValueTag::Bool as _, false).into(),
|
||||||
|
self.bool_type.const_int(bool as _, false).into(),
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_null(&self) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value_type
|
||||||
|
.const_named_struct(&[
|
||||||
|
self.int_type.const_int(ValueTag::Null as _, false).into(),
|
||||||
|
self.int_type.const_zero().into(),
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_string(&self, string: *const u8) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value_type
|
||||||
|
.const_named_struct(&[
|
||||||
|
self.int_type.const_int(ValueTag::String as _, false).into(),
|
||||||
|
self.ptr_int_type.const_int(string as _, false).into(),
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match rhs.tag {
|
||||||
|
Int => JITValue {
|
||||||
|
tag: Int,
|
||||||
|
data: JITValueData {
|
||||||
|
int: -unsafe { rhs.data.int },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Float => JITValue {
|
||||||
|
tag: Float,
|
||||||
|
data: JITValueData {
|
||||||
|
float: -unsafe { rhs.data.float },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_not(rhs: JITValue, _env: *const VmEnv) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match rhs.tag {
|
||||||
|
Bool => JITValue {
|
||||||
|
tag: Bool,
|
||||||
|
data: JITValueData {
|
||||||
|
bool: !unsafe { rhs.data.bool },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_add(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match (lhs.tag, rhs.tag) {
|
||||||
|
(Int, Int) => JITValue {
|
||||||
|
tag: Int,
|
||||||
|
data: JITValueData {
|
||||||
|
int: unsafe { lhs.data.int + rhs.data.int },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(
|
||||||
|
"Addition not implemented for {:?} and {:?}",
|
||||||
|
lhs.tag,
|
||||||
|
rhs.tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_sub(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match (lhs.tag, rhs.tag) {
|
||||||
|
(Int, Int) => JITValue {
|
||||||
|
tag: Int,
|
||||||
|
data: JITValueData {
|
||||||
|
int: unsafe { lhs.data.int - rhs.data.int },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(
|
||||||
|
"Substruction not implemented for {:?} and {:?}",
|
||||||
|
lhs.tag,
|
||||||
|
rhs.tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_eq(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match (lhs.tag, rhs.tag) {
|
||||||
|
(Int, Int) => JITValue {
|
||||||
|
tag: Bool,
|
||||||
|
data: JITValueData {
|
||||||
|
bool: unsafe { lhs.data.int == rhs.data.int },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(
|
||||||
|
"Equation not implemented for {:?} and {:?}",
|
||||||
|
lhs.tag,
|
||||||
|
rhs.tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||||
|
use ValueTag::*;
|
||||||
|
match (lhs.tag, rhs.tag) {
|
||||||
|
(Bool, Bool) => JITValue {
|
||||||
|
tag: Bool,
|
||||||
|
data: JITValueData {
|
||||||
|
bool: unsafe { lhs.data.bool || rhs.data.bool },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => todo!(
|
||||||
|
"Substraction not implemented for {:?} and {:?}",
|
||||||
|
lhs.tag,
|
||||||
|
rhs.tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, vm: *const VM<'gc>) -> JITValue {
|
||||||
|
let vm = unsafe { vm.as_ref() }.unwrap();
|
||||||
|
let arg = Value::from(arg);
|
||||||
|
match func.tag {
|
||||||
|
ValueTag::Function => {
|
||||||
|
let func = Value::from(func).unwrap_func();
|
||||||
|
func.call_compile(arg, vm).unwrap().clone().into()
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue {
|
||||||
|
let env = unsafe { env.as_ref() }.unwrap();
|
||||||
|
let val: JITValue = env.lookup_arg(idx).clone().into();
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue {
|
||||||
|
let env = unsafe { env.as_ref() }.unwrap();
|
||||||
|
let val: JITValue = env.lookup_let(level, idx).clone().into();
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_force<'gc>(
|
||||||
|
thunk: JITValue,
|
||||||
|
vm: NonNull<VM<'gc>>,
|
||||||
|
jit: *const JITContext<'gc>,
|
||||||
|
) -> JITValue {
|
||||||
|
if !matches!(thunk.tag, ValueTag::Thunk) {
|
||||||
|
return thunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
let vm = unsafe { vm.as_ref() };
|
||||||
|
let thunk = Value::from(thunk).unwrap_thunk();
|
||||||
|
if let Some(val) = thunk.get_value() {
|
||||||
|
return val.clone().into();
|
||||||
|
}
|
||||||
|
let (opcodes, env) = thunk.suspend().unwrap();
|
||||||
|
let func = unsafe { jit.as_ref() }
|
||||||
|
.unwrap()
|
||||||
|
.compile_seq(opcodes.iter().copied().rev(), vm)
|
||||||
|
.unwrap();
|
||||||
|
let val = unsafe { func(env.as_ref() as *const _) };
|
||||||
|
thunk.insert_value(val.into());
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn helper_new_thunk(opcodes: *const OpCodes) -> JITValue {
|
||||||
|
Value::Thunk(Thunk::new(unsafe { opcodes.as_ref() }.unwrap())).into()
|
||||||
|
}
|
||||||
468
src/jit/mod.rs
Normal file
468
src/jit/mod.rs
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use inkwell::OptimizationLevel;
|
||||||
|
use inkwell::builder::Builder;
|
||||||
|
use inkwell::context::Context;
|
||||||
|
use inkwell::execution_engine::ExecutionEngine;
|
||||||
|
use inkwell::module::Module;
|
||||||
|
use inkwell::values::{BasicValueEnum, FunctionValue, PointerValue};
|
||||||
|
|
||||||
|
use crate::bytecode::{OpCode, UnOp};
|
||||||
|
use crate::env::VmEnv;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::stack::Stack;
|
||||||
|
use crate::ty::common::Const;
|
||||||
|
use crate::ty::internal::{Thunk, Value};
|
||||||
|
use crate::vm::VM;
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use helpers::Helpers;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
|
||||||
|
|
||||||
|
#[repr(u64)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ValueTag {
|
||||||
|
Null,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
String,
|
||||||
|
Bool,
|
||||||
|
AttrSet,
|
||||||
|
List,
|
||||||
|
Function,
|
||||||
|
Thunk,
|
||||||
|
Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct JITValue {
|
||||||
|
tag: ValueTag,
|
||||||
|
data: JITValueData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub union JITValueData {
|
||||||
|
int: i64,
|
||||||
|
float: f64,
|
||||||
|
bool: bool,
|
||||||
|
ptr: *const (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> From<JITValue> for Value<'gc> {
|
||||||
|
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 _) }),
|
||||||
|
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 {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::Int(int) => JITValue {
|
||||||
|
tag: ValueTag::Int,
|
||||||
|
data: JITValueData { int },
|
||||||
|
},
|
||||||
|
Value::Func(func) => JITValue {
|
||||||
|
tag: ValueTag::Function,
|
||||||
|
data: JITValueData {
|
||||||
|
ptr: Rc::into_raw(func) as *const _,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Value::Thunk(thunk) => JITValue {
|
||||||
|
tag: ValueTag::Thunk,
|
||||||
|
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;
|
||||||
|
|
||||||
|
impl<'gc> From<F<'gc>> for JITFunc<'gc> {
|
||||||
|
fn from(value: F<'gc>) -> Self {
|
||||||
|
Self(value, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Deref for JITFunc<'gc> {
|
||||||
|
type Target = F<'gc>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JITContext<'gc> {
|
||||||
|
context: &'gc Context,
|
||||||
|
module: Module<'gc>,
|
||||||
|
builder: Builder<'gc>,
|
||||||
|
execution_engine: ExecutionEngine<'gc>,
|
||||||
|
|
||||||
|
helpers: Helpers<'gc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> JITContext<'gc> {
|
||||||
|
pub fn new(context: &'gc Context) -> Self {
|
||||||
|
// force linker to link JIT engine
|
||||||
|
unsafe {
|
||||||
|
inkwell::llvm_sys::execution_engine::LLVMLinkInMCJIT();
|
||||||
|
}
|
||||||
|
let module = context.create_module("nixjit");
|
||||||
|
let execution_engine = module
|
||||||
|
.create_jit_execution_engine(OptimizationLevel::Aggressive)
|
||||||
|
.unwrap();
|
||||||
|
let helpers = Helpers::new(context, &module, &execution_engine);
|
||||||
|
|
||||||
|
JITContext {
|
||||||
|
execution_engine,
|
||||||
|
builder: context.create_builder(),
|
||||||
|
context,
|
||||||
|
module,
|
||||||
|
|
||||||
|
helpers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_ptr<T>(&self, ptr: *const T) -> PointerValue<'gc> {
|
||||||
|
self.builder
|
||||||
|
.build_int_to_ptr(
|
||||||
|
self.helpers.int_type.const_int(ptr as _, false),
|
||||||
|
self.helpers.ptr_type,
|
||||||
|
"ptrconv",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_seq(
|
||||||
|
&self,
|
||||||
|
mut opcodes: impl ExactSizeIterator<Item = OpCode>,
|
||||||
|
vm: &VM<'gc>,
|
||||||
|
) -> Result<JITFunc<'gc>> {
|
||||||
|
let mut stack = Stack::<_, STACK_SIZE>::new();
|
||||||
|
let func_ = self
|
||||||
|
.module
|
||||||
|
.add_function("nixjit_function", self.helpers.func_type, None);
|
||||||
|
let env = func_.get_nth_param(0).unwrap().into_pointer_value();
|
||||||
|
let entry = self.context.append_basic_block(func_, "entry");
|
||||||
|
self.builder.position_at_end(entry);
|
||||||
|
let len = opcodes.len();
|
||||||
|
self.build_expr(&mut opcodes, vm, env, &mut stack, func_, len)?;
|
||||||
|
|
||||||
|
assert_eq!(stack.len(), 1);
|
||||||
|
let value = stack.pop();
|
||||||
|
let exit = self.context.append_basic_block(func_, "exit");
|
||||||
|
self.builder.build_unconditional_branch(exit)?;
|
||||||
|
self.builder.position_at_end(exit);
|
||||||
|
self.builder.build_return(Some(&value))?;
|
||||||
|
if func_.verify(true) {
|
||||||
|
unsafe {
|
||||||
|
let name = func_.get_name().to_str().unwrap();
|
||||||
|
let func = self.execution_engine.get_function(name).unwrap();
|
||||||
|
Ok(JITFunc(func.as_raw(), PhantomData))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_expr<const CAP: usize>(
|
||||||
|
&self,
|
||||||
|
iter: &mut impl Iterator<Item = OpCode>,
|
||||||
|
vm: &VM<'gc>,
|
||||||
|
env: PointerValue<'gc>,
|
||||||
|
stack: &mut Stack<BasicValueEnum<'gc>, CAP>,
|
||||||
|
func: FunctionValue<'gc>,
|
||||||
|
mut length: usize,
|
||||||
|
) -> Result<usize> {
|
||||||
|
while length > 1 {
|
||||||
|
let opcode = iter.next().unwrap();
|
||||||
|
let br = self.single_op(opcode, vm, env, stack)?;
|
||||||
|
length -= 1;
|
||||||
|
if br > 0 {
|
||||||
|
let consq = self.context.append_basic_block(func, "consq");
|
||||||
|
let alter = self.context.append_basic_block(func, "alter");
|
||||||
|
let cont = self.context.append_basic_block(func, "cont");
|
||||||
|
let cond = self
|
||||||
|
.builder
|
||||||
|
.build_alloca(self.helpers.value_type, "cond_alloca")?;
|
||||||
|
let result = self
|
||||||
|
.builder
|
||||||
|
.build_alloca(self.helpers.value_type, "result_alloca")?;
|
||||||
|
self.builder.build_store(cond, stack.pop())?;
|
||||||
|
self.builder.build_conditional_branch(
|
||||||
|
self.builder
|
||||||
|
.build_load(
|
||||||
|
self.context.bool_type(),
|
||||||
|
self.builder.build_struct_gep(
|
||||||
|
self.helpers.value_type,
|
||||||
|
cond,
|
||||||
|
1,
|
||||||
|
"gep_cond",
|
||||||
|
)?,
|
||||||
|
"load_cond",
|
||||||
|
)?
|
||||||
|
.into_int_value(),
|
||||||
|
consq,
|
||||||
|
alter,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
length -= br;
|
||||||
|
self.builder.position_at_end(consq);
|
||||||
|
let br = self.build_expr(iter, vm, env, stack, func, br)?;
|
||||||
|
self.builder.build_store(result, stack.pop())?;
|
||||||
|
self.builder.build_unconditional_branch(cont)?;
|
||||||
|
|
||||||
|
length -= br;
|
||||||
|
self.builder.position_at_end(alter);
|
||||||
|
self.build_expr(iter, vm, env, stack, func, br)?;
|
||||||
|
self.builder.build_store(result, stack.pop())?;
|
||||||
|
self.builder.build_unconditional_branch(cont)?;
|
||||||
|
|
||||||
|
self.builder.position_at_end(cont);
|
||||||
|
stack.push(self.builder.build_load(
|
||||||
|
self.helpers.value_type,
|
||||||
|
result,
|
||||||
|
"load_result",
|
||||||
|
)?)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length > 0 {
|
||||||
|
self.single_op(iter.next().unwrap(), vm, env, stack)
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn single_op<const CAP: usize>(
|
||||||
|
&self,
|
||||||
|
opcode: OpCode,
|
||||||
|
vm: &VM<'_>,
|
||||||
|
env: PointerValue<'gc>,
|
||||||
|
stack: &mut Stack<BasicValueEnum<'gc>, CAP>,
|
||||||
|
) -> Result<usize> {
|
||||||
|
match opcode {
|
||||||
|
OpCode::Const { idx } => {
|
||||||
|
use Const::*;
|
||||||
|
match vm.get_const(idx) {
|
||||||
|
Int(int) => stack.push(self.helpers.new_int(int))?,
|
||||||
|
Float(float) => stack.push(self.helpers.new_float(float))?,
|
||||||
|
Bool(bool) => stack.push(self.helpers.new_bool(bool))?,
|
||||||
|
String(string) => stack.push(self.helpers.const_string(string.as_ptr()))?,
|
||||||
|
Null => stack.push(self.helpers.new_null())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpCode::LoadThunk { idx } => stack.push(
|
||||||
|
self.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.new_thunk,
|
||||||
|
&[self.new_ptr(vm.get_thunk(idx) as *const _).into()],
|
||||||
|
"call_capture_env",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.unwrap_left(),
|
||||||
|
)?,
|
||||||
|
OpCode::ForceValue => {
|
||||||
|
let thunk = stack.pop();
|
||||||
|
let _ = stack.push(
|
||||||
|
self.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.force,
|
||||||
|
&[
|
||||||
|
thunk.into(),
|
||||||
|
self.new_ptr(vm as *const _).into(),
|
||||||
|
self.new_ptr(self as *const _).into(),
|
||||||
|
],
|
||||||
|
"call_force",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
OpCode::CaptureEnv => {
|
||||||
|
let thunk = *stack.tos();
|
||||||
|
self.builder.build_direct_call(
|
||||||
|
self.helpers.capture_env,
|
||||||
|
&[thunk.into(), env.into()],
|
||||||
|
"call_capture_env",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
OpCode::UnOp { op } => {
|
||||||
|
use UnOp::*;
|
||||||
|
let rhs = stack.pop();
|
||||||
|
stack.push(match op {
|
||||||
|
Neg => self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(self.helpers.neg, &[rhs.into(), env.into()], "call_neg")?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
Not => self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(self.helpers.not, &[rhs.into(), env.into()], "call_neg")?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
OpCode::BinOp { op } => {
|
||||||
|
use crate::bytecode::BinOp;
|
||||||
|
let rhs = stack.pop();
|
||||||
|
let lhs = stack.pop();
|
||||||
|
match op {
|
||||||
|
BinOp::Add => {
|
||||||
|
let result = self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.add,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
"call_add",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap();
|
||||||
|
stack.push(result)?;
|
||||||
|
}
|
||||||
|
BinOp::Sub => {
|
||||||
|
let result = self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.sub,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
"call_add",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap();
|
||||||
|
stack.push(result)?;
|
||||||
|
}
|
||||||
|
BinOp::Eq => {
|
||||||
|
let result = self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.eq,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
"call_eq",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap();
|
||||||
|
stack.push(result)?;
|
||||||
|
}
|
||||||
|
BinOp::Or => {
|
||||||
|
let result = self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.or,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
"call_or",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap();
|
||||||
|
stack.push(result)?;
|
||||||
|
}
|
||||||
|
_ => todo!("BinOp::{:?} not implemented in JIT", op),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpCode::Arg { level } => stack.push(
|
||||||
|
self.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.arg,
|
||||||
|
&[
|
||||||
|
self.helpers
|
||||||
|
.ptr_int_type
|
||||||
|
.const_int(level as u64, false)
|
||||||
|
.into(),
|
||||||
|
env.into(),
|
||||||
|
],
|
||||||
|
"call_arg",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
)?,
|
||||||
|
OpCode::LookUpLet { level, idx } => stack.push(
|
||||||
|
self.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.lookup_let,
|
||||||
|
&[
|
||||||
|
self.helpers
|
||||||
|
.ptr_int_type
|
||||||
|
.const_int(level as u64, false)
|
||||||
|
.into(),
|
||||||
|
self.helpers
|
||||||
|
.ptr_int_type
|
||||||
|
.const_int(idx as u64, false)
|
||||||
|
.into(),
|
||||||
|
env.into(),
|
||||||
|
],
|
||||||
|
"call_lookup_let",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
)?,
|
||||||
|
OpCode::LookUp { sym } => stack.push(
|
||||||
|
self.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.lookup,
|
||||||
|
&[
|
||||||
|
self.helpers
|
||||||
|
.ptr_int_type
|
||||||
|
.const_int(sym as u64, false)
|
||||||
|
.into(),
|
||||||
|
env.into(),
|
||||||
|
],
|
||||||
|
"call_lookup",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap(),
|
||||||
|
)?,
|
||||||
|
OpCode::Call => {
|
||||||
|
let arg = stack.pop();
|
||||||
|
let func = stack.pop();
|
||||||
|
let ret = self
|
||||||
|
.builder
|
||||||
|
.build_direct_call(
|
||||||
|
self.helpers.call,
|
||||||
|
&[func.into(), arg.into(), self.new_ptr(vm).into()],
|
||||||
|
"call",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap();
|
||||||
|
stack.push(ret)?;
|
||||||
|
}
|
||||||
|
OpCode::JmpIfFalse { step } => return Ok(step),
|
||||||
|
OpCode::Jmp { step } => return Ok(step),
|
||||||
|
_ => todo!("{opcode:?} not implemented"),
|
||||||
|
}
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/jit/test.rs
Normal file
96
src/jit/test.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use inkwell::context::Context;
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
|
use crate::compile::compile;
|
||||||
|
use crate::ir::downgrade;
|
||||||
|
use crate::jit::JITContext;
|
||||||
|
use crate::ty::common::Const;
|
||||||
|
use crate::ty::public::*;
|
||||||
|
use crate::vm::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_jit_const() {
|
||||||
|
// test_expr("let f = _: 1; in (f 1) + (f 1)", int!(2));
|
||||||
|
test_expr("let f = _: 1; in (f 1) == (f 1)", boolean!(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arith() {
|
||||||
|
test_expr("let f = _: -(-1); in (f 1) + (f 1)", int!(2));
|
||||||
|
}
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
#![cfg_attr(test, feature(test))]
|
#![cfg_attr(test, feature(test))]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#![feature(iter_collect_into)]
|
||||||
|
#![feature(arbitrary_self_types)]
|
||||||
|
|
||||||
mod builtins;
|
mod builtins;
|
||||||
mod bytecode;
|
mod bytecode;
|
||||||
|
mod env;
|
||||||
mod stack;
|
mod stack;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
|
||||||
pub mod compile;
|
pub mod compile;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
|
pub mod jit;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub use ty::public::Value;
|
pub use ty::public::Value;
|
||||||
|
|||||||
40
src/stack.rs
40
src/stack.rs
@@ -3,11 +3,6 @@ use std::ops::Deref;
|
|||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
|
||||||
pub struct Stack<T, const CAP: usize> {
|
|
||||||
items: [MaybeUninit<T>; CAP],
|
|
||||||
top: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! into {
|
macro_rules! into {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
// SAFETY: This macro is used to transmute `MaybeUninit<Value<'vm>>` to `Value<'vm>`
|
// SAFETY: This macro is used to transmute `MaybeUninit<Value<'vm>>` to `Value<'vm>`
|
||||||
@@ -18,6 +13,18 @@ macro_rules! into {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Stack<T, const CAP: usize> {
|
||||||
|
items: [MaybeUninit<T>; CAP],
|
||||||
|
top: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T, const CAP: usize> Default for Stack<T, CAP> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, const CAP: usize> Stack<T, CAP> {
|
impl<T, const CAP: usize> Stack<T, CAP> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Stack {
|
Stack {
|
||||||
@@ -29,18 +36,15 @@ impl<T, const CAP: usize> Stack<T, CAP> {
|
|||||||
pub fn push(&mut self, item: T) -> Result<()> {
|
pub fn push(&mut self, item: T) -> Result<()> {
|
||||||
self.items
|
self.items
|
||||||
.get_mut(self.top)
|
.get_mut(self.top)
|
||||||
.map_or_else(
|
.map_or_else(|| Err(Error::EvalError("stack overflow".to_string())), Ok)?
|
||||||
|| Err(Error::EvalError("stack overflow".to_string())),
|
|
||||||
|ok| Ok(ok),
|
|
||||||
)?
|
|
||||||
.write(item);
|
.write(item);
|
||||||
self.top += 1;
|
self.top += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) -> T {
|
pub fn pop(&mut self) -> T {
|
||||||
|
let item = self.items.get_mut(self.top - 1).unwrap();
|
||||||
self.top -= 1;
|
self.top -= 1;
|
||||||
let item = self.items.get_mut(self.top).unwrap();
|
|
||||||
|
|
||||||
// SAFETY: `item` at `self.top` was previously written and is initialized.
|
// SAFETY: `item` at `self.top` was previously written and is initialized.
|
||||||
// We replace it with `MaybeUninit::uninit()` and then `assume_init`
|
// We replace it with `MaybeUninit::uninit()` and then `assume_init`
|
||||||
@@ -48,20 +52,12 @@ impl<T, const CAP: usize> Stack<T, CAP> {
|
|||||||
unsafe { replace(item, MaybeUninit::uninit()).assume_init() }
|
unsafe { replace(item, MaybeUninit::uninit()).assume_init() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tos(&self) -> Result<&T> {
|
pub fn tos(&self) -> &T {
|
||||||
if self.top == 0 {
|
into!(&self.items[self.top - 1])
|
||||||
panic!("stack empty")
|
|
||||||
} else {
|
|
||||||
Ok(into!(&self.items[self.top - 1]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tos_mut(&mut self) -> Result<&mut T> {
|
pub fn tos_mut(&mut self) -> &mut T {
|
||||||
if self.top == 0 {
|
into!(&mut self.items[self.top - 1])
|
||||||
panic!("stack empty")
|
|
||||||
} else {
|
|
||||||
Ok(into!(&mut self.items[self.top - 1]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
104
src/ty/common.rs
104
src/ty/common.rs
@@ -1,14 +1,116 @@
|
|||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Constructor, Hash)]
|
#[derive(Clone, Debug, PartialEq, Constructor, Hash)]
|
||||||
pub struct Catchable {
|
pub struct Catchable {
|
||||||
msg: String,
|
msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for Catchable {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Catchable { msg: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Catchable {
|
impl Display for Catchable {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
write!(f, "<error: {}>", self.msg)
|
write!(f, "<error: {}>", self.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
||||||
|
pub enum Const {
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(EcoString),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Const {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
use Const::*;
|
||||||
|
std::mem::discriminant(self).hash(state);
|
||||||
|
match self {
|
||||||
|
Int(x) => x.hash(state),
|
||||||
|
Float(x) => x.to_bits().hash(state),
|
||||||
|
Bool(x) => x.hash(state),
|
||||||
|
String(x) => x.hash(state),
|
||||||
|
Null => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Const {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
use Const::*;
|
||||||
|
match self {
|
||||||
|
Int(x) => write!(f, "{x}"),
|
||||||
|
Float(x) => write!(f, "{x}"),
|
||||||
|
Bool(x) => write!(f, "{x}"),
|
||||||
|
String(x) => write!(f, "{x:?}"),
|
||||||
|
Null => write!(f, "null"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Const {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Const::Bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Const {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Const::Int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for Const {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
Const::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EcoString> for Const {
|
||||||
|
fn from(value: EcoString) -> Self {
|
||||||
|
Const::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Const {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Const::String(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Const {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Const::String(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Const {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(Bool(a), Bool(b)) => a == b,
|
||||||
|
(Int(a), Int(b)) => a == b,
|
||||||
|
(Float(a), Float(b)) => a == b,
|
||||||
|
(Int(a), Float(b)) => *a as f64 == *b,
|
||||||
|
(Float(a), Int(b)) => *b as f64 == *a,
|
||||||
|
(String(a), String(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Const {}
|
||||||
|
|
||||||
|
pub enum MaybeThunk {
|
||||||
|
Thunk(usize),
|
||||||
|
Const(Const),
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,45 +1,53 @@
|
|||||||
use hashbrown::{HashMap, HashSet};
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::vm::VM;
|
||||||
use crate::vm::{Env, VM};
|
|
||||||
|
|
||||||
use super::super::public as p;
|
use super::super::public as p;
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Constructor, Clone, PartialEq)]
|
#[derive(Constructor, Clone, PartialEq)]
|
||||||
pub struct AttrSet<'vm> {
|
pub struct AttrSet<'gc> {
|
||||||
data: HashMap<usize, Value<'vm>>,
|
data: HashMap<usize, Value<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> AttrSet<'vm> {
|
impl<'gc> From<HashMap<usize, Value<'gc>>> for AttrSet<'gc> {
|
||||||
pub fn empty() -> Self {
|
fn from(data: HashMap<usize, Value<'gc>>) -> Self {
|
||||||
AttrSet {
|
Self { data }
|
||||||
data: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> Deref for AttrSet<'gc> {
|
||||||
|
type Target = HashMap<usize, Value<'gc>>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> AttrSet<'gc> {
|
||||||
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: usize, val: Value<'vm>) {
|
pub fn push_attr_force(&mut self, sym: usize, val: Value<'gc>) {
|
||||||
self.data.insert(sym, val);
|
self.data.insert(sym, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_attr(&mut self, sym: usize, val: Value<'vm>) {
|
pub fn push_attr(&mut self, sym: usize, val: Value<'gc>) {
|
||||||
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: usize) -> Option<Value<'vm>> {
|
pub fn select(&self, sym: usize) -> Option<Value<'gc>> {
|
||||||
self.data.get(&sym).cloned()
|
self.data.get(&sym).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,52 +55,34 @@ impl<'vm> AttrSet<'vm> {
|
|||||||
self.data.get(&sym).is_some()
|
self.data.get(&sym).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capture(&mut self, env: &Env<'vm>) {
|
pub fn update(&mut self, other: &AttrSet<'gc>) {
|
||||||
self.data.iter().for_each(|(_, v)| match v.clone() {
|
|
||||||
Value::Thunk(ref thunk) => {
|
|
||||||
thunk.capture(env.clone());
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, other: &AttrSet<'vm>) {
|
|
||||||
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, v.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_inner(&self) -> &HashMap<usize, Value<'vm>> {
|
pub fn as_inner(&self) -> &HashMap<usize, Value<'gc>> {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_inner(data: HashMap<usize, Value<'vm>>) -> Self {
|
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<usize, Value<'gc>>> {
|
||||||
|
unsafe { std::mem::transmute(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_inner(data: HashMap<usize, Value<'gc>>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<()> {
|
pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool {
|
||||||
let mut map: Vec<_> = self
|
|
||||||
.data
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
|
||||||
.collect();
|
|
||||||
for (_, v) in map.iter_mut() {
|
|
||||||
v.force_deep(vm)?;
|
|
||||||
}
|
|
||||||
self.data = map.into_iter().collect();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eq_impl(&self, other: &AttrSet<'vm>, vm: &'vm VM<'_>) -> 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_key(|(k, _)| **k),
|
self.data.iter().sorted_by_key(|(k, _)| **k),
|
||||||
self.data.iter().sorted_by_key(|(k, _)| **k),
|
self.data.iter().sorted_by_key(|(k, _)| **k),
|
||||||
)
|
)
|
||||||
.all(|((_, v1), (_, v2))| v1.eq_impl(v2, vm))
|
.all(|((_, v1), (_, v2))| v1.eq_impl(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value {
|
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||||
p::Value::AttrSet(p::AttrSet::new(
|
p::Value::AttrSet(p::AttrSet::new(
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use derive_more::{IsVariant, Unwrap};
|
|
||||||
use ecow::EcoString;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
|
||||||
pub enum Const {
|
|
||||||
Bool(bool),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
String(EcoString),
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for Const {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
use Const::*;
|
|
||||||
match self {
|
|
||||||
Int(x) => x.hash(state),
|
|
||||||
Float(x) => x.to_bits().hash(state),
|
|
||||||
Bool(x) => x.hash(state),
|
|
||||||
String(x) => x.hash(state),
|
|
||||||
x @ Null => x.hash(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for Const {
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
Const::Bool(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i64> for Const {
|
|
||||||
fn from(value: i64) -> Self {
|
|
||||||
Const::Int(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> for Const {
|
|
||||||
fn from(value: f64) -> Self {
|
|
||||||
Const::Float(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EcoString> for Const {
|
|
||||||
fn from(value: EcoString) -> Self {
|
|
||||||
Const::String(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Const {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Const::String(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Const {
|
|
||||||
fn from(value: &str) -> Self {
|
|
||||||
Const::String(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Const {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(Bool(a), Bool(b)) => a == b,
|
|
||||||
(Int(a), Int(b)) => a == b,
|
|
||||||
(Float(a), Float(b)) => a == b,
|
|
||||||
(Int(a), Float(b)) => *a as f64 == *b,
|
|
||||||
(Float(a), Int(b)) => *b as f64 == *a,
|
|
||||||
(String(a), String(b)) => a == b,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Const {}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
use derive_more::Constructor;
|
use std::cell::{Cell, OnceCell};
|
||||||
use itertools::Itertools;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::bytecode::Func as BFunc;
|
use crate::bytecode::Func as BFunc;
|
||||||
|
use crate::env::VmEnv;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::ty::internal::{Thunk, Value};
|
use crate::jit::JITFunc;
|
||||||
use crate::vm::{Env, VM};
|
use crate::ty::internal::Value;
|
||||||
|
use crate::vm::VM;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Param {
|
pub enum Param {
|
||||||
@@ -37,59 +39,28 @@ impl From<ir::Param> for Param {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type JITFunc<'vm> =
|
pub struct Func<'gc> {
|
||||||
unsafe extern "C" fn(vm: *mut VM<'_>, *mut Env<'vm>, *mut Value<'vm>) -> Value<'vm>;
|
pub func: &'gc BFunc,
|
||||||
|
pub env: Rc<VmEnv<'gc>>,
|
||||||
#[derive(Debug, Clone, Constructor)]
|
pub compiled: OnceCell<JITFunc<'gc>>,
|
||||||
pub struct Func<'vm> {
|
pub count: Cell<usize>,
|
||||||
pub func: &'vm BFunc,
|
|
||||||
pub env: Env<'vm>,
|
|
||||||
pub compiled: Option<JITFunc<'vm>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> Func<'vm> {
|
impl<'gc> Func<'gc> {
|
||||||
pub fn call(&self, vm: &'vm VM<'_>, arg: Value<'vm>) -> Result<Value<'vm>> {
|
pub fn new(func: &'gc BFunc, env: Rc<VmEnv<'gc>>) -> Self {
|
||||||
use Param::*;
|
Self {
|
||||||
|
func,
|
||||||
let mut env = self.env.clone();
|
env,
|
||||||
|
compiled: OnceCell::new(),
|
||||||
match self.func.param.clone() {
|
count: Cell::new(0),
|
||||||
Ident(ident) => env = env.enter([(ident.into(), arg)].into_iter()),
|
|
||||||
Formals {
|
|
||||||
formals,
|
|
||||||
ellipsis,
|
|
||||||
alias,
|
|
||||||
} => {
|
|
||||||
let arg = arg.unwrap_attr_set();
|
|
||||||
let mut new = Vec::with_capacity(formals.len() + alias.iter().len());
|
|
||||||
if !ellipsis
|
|
||||||
&& arg
|
|
||||||
.as_inner()
|
|
||||||
.iter()
|
|
||||||
.map(|(k, _)| k)
|
|
||||||
.sorted()
|
|
||||||
.ne(formals.iter().map(|(k, _)| k).sorted())
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
for (formal, default) in formals {
|
|
||||||
let formal = formal.clone().into();
|
|
||||||
let arg = arg
|
|
||||||
.select(formal)
|
|
||||||
.or_else(|| {
|
|
||||||
default.map(|idx| Value::Thunk(Thunk::new(vm.get_thunk(idx)).into()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
new.push((formal, arg));
|
|
||||||
}
|
|
||||||
if let Some(alias) = alias {
|
|
||||||
new.push((alias.clone().into(), Value::AttrSet(arg)));
|
|
||||||
}
|
|
||||||
env = env.enter(new.into_iter());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vm.eval(self.func.opcodes.iter().copied(), env)
|
pub fn call_compile(&self, arg: Value<'gc>, vm: &'gc VM<'gc>) -> Result<Value<'gc>> {
|
||||||
|
let env = self.env.clone().enter_arg(arg);
|
||||||
|
let compiled = self.compiled.get_or_init(|| vm.compile_func(self.func));
|
||||||
|
let ret = unsafe { compiled(env.as_ref() as *const VmEnv) };
|
||||||
|
Ok(ret.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,58 @@
|
|||||||
|
use std::rc::Weak;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use crate::env::VmEnv;
|
||||||
use rpds::Vector;
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use crate::ty::public as p;
|
use crate::ty::public as p;
|
||||||
use crate::vm::VM;
|
use crate::vm::VM;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
#[derive(Debug, Constructor, Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct List<'vm> {
|
pub struct List<'gc> {
|
||||||
data: Vector<Value<'vm>>,
|
data: Vec<Value<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> List<'vm> {
|
impl<'gc> Default for List<'gc> {
|
||||||
pub fn empty() -> Self {
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> List<'gc> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
List { data: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
List {
|
List {
|
||||||
data: Vector::new(),
|
data: Vec::with_capacity(cap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, elem: Value<'vm>) {
|
pub fn push(&mut self, elem: Value<'gc>) {
|
||||||
self.data.push_back_mut(elem);
|
self.data.push(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn concat(&mut self, other: &List<'vm>) {
|
pub fn concat(&mut self, other: &List<'gc>) {
|
||||||
for elem in other.data.iter() {
|
for elem in other.data.iter() {
|
||||||
self.data.push_back_mut(elem.clone());
|
self.data.push(elem.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<()> {
|
pub fn capture(&mut self, env: &Weak<VmEnv<'gc>>) {
|
||||||
let mut vec: Vec<_> = self.data.iter().cloned().collect();
|
self.data.iter().for_each(|v| {
|
||||||
for v in vec.iter_mut() {
|
if let Value::Thunk(ref thunk) = v.clone() {
|
||||||
v.force_deep(vm)?;
|
thunk.capture_env_weak(env.clone());
|
||||||
}
|
}
|
||||||
self.data = vec.into_iter().collect();
|
})
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value {
|
pub fn into_inner(self) -> Vec<Value<'gc>> {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||||
p::Value::List(p::List::new(
|
p::Value::List(p::List::new(
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -1,43 +1,44 @@
|
|||||||
use hashbrown::HashSet;
|
|
||||||
use std::cell::OnceCell;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use derive_more::{IsVariant, Unwrap};
|
use derive_more::{IsVariant, Unwrap};
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use replace_with::replace_with_or_abort;
|
||||||
|
|
||||||
use super::common as c;
|
use super::common::*;
|
||||||
use super::public as p;
|
use super::public as p;
|
||||||
|
|
||||||
use crate::bytecode::OpCodes;
|
use crate::bytecode::OpCodes;
|
||||||
|
use crate::env::VmEnv;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::vm::{Env, VM};
|
use crate::vm::VM;
|
||||||
|
|
||||||
mod attrset;
|
mod attrset;
|
||||||
mod cnst;
|
|
||||||
mod func;
|
mod func;
|
||||||
mod list;
|
mod list;
|
||||||
mod primop;
|
mod primop;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use attrset::*;
|
pub use attrset::*;
|
||||||
pub use cnst::Const;
|
|
||||||
pub use func::*;
|
pub use func::*;
|
||||||
pub use list::List;
|
pub use list::List;
|
||||||
pub use primop::*;
|
pub use primop::*;
|
||||||
|
|
||||||
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)]
|
#[derive(IsVariant, Unwrap, Clone)]
|
||||||
pub enum Value<'vm> {
|
pub enum Value<'gc> {
|
||||||
Const(Const),
|
Int(i64),
|
||||||
Thunk(Rc<Thunk<'vm>>),
|
Float(f64),
|
||||||
ThunkRef(&'vm Thunk<'vm>),
|
Bool(bool),
|
||||||
AttrSet(Rc<AttrSet<'vm>>),
|
String(Rc<String>),
|
||||||
List(Rc<List<'vm>>),
|
Null,
|
||||||
Catchable(c::Catchable),
|
Thunk(Thunk<'gc>),
|
||||||
PrimOp(Rc<PrimOp<'vm>>),
|
AttrSet(Rc<AttrSet<'gc>>),
|
||||||
PartialPrimOp(Rc<PartialPrimOp<'vm>>),
|
List(Rc<List<'gc>>),
|
||||||
Func(Rc<Func<'vm>>),
|
Catchable(Rc<String>),
|
||||||
Builtins,
|
PrimOp(Rc<PrimOp>),
|
||||||
|
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
|
||||||
|
Func(Rc<Func<'gc>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Value<'_> {
|
impl Hash for Value<'_> {
|
||||||
@@ -45,29 +46,37 @@ impl Hash for Value<'_> {
|
|||||||
use Value::*;
|
use Value::*;
|
||||||
std::mem::discriminant(self).hash(state);
|
std::mem::discriminant(self).hash(state);
|
||||||
match self {
|
match self {
|
||||||
Const(x) => x.hash(state),
|
AttrSet(x) => Rc::as_ptr(x).hash(state),
|
||||||
Thunk(x) => (x.as_ref() as *const self::Thunk).hash(state),
|
List(x) => Rc::as_ptr(x).hash(state),
|
||||||
ThunkRef(x) => (*x as *const self::Thunk).hash(state),
|
_ => 0.hash(state),
|
||||||
AttrSet(x) => (x.as_ref() as *const self::AttrSet).hash(state),
|
|
||||||
List(x) => (x.as_ref() as *const self::List).hash(state),
|
|
||||||
Catchable(x) => x.hash(state),
|
|
||||||
PrimOp(x) => (x.as_ref() as *const self::PrimOp).hash(state),
|
|
||||||
PartialPrimOp(x) => (x.as_ref() as *const self::PartialPrimOp).hash(state),
|
|
||||||
Func(x) => (x.as_ref() as *const self::Func).hash(state),
|
|
||||||
Builtins => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> Value<'vm> {
|
impl<'gc> Value<'gc> {
|
||||||
fn eq_impl(&self, other: &Self, vm: &'vm VM<'_>) -> bool {
|
fn eq_impl(&self, other: &Self) -> bool {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Const(a), Const(b)) => a.eq(b),
|
(Bool(a), Bool(b)) => a == b,
|
||||||
(AttrSet(a), AttrSet(b)) => a.eq_impl(b, vm),
|
(Int(a), Int(b)) => a == b,
|
||||||
|
(Float(a), Float(b)) => a == b,
|
||||||
|
(Int(a), Float(b)) => *a as f64 == *b,
|
||||||
|
(Float(a), Int(b)) => *b as f64 == *a,
|
||||||
|
(String(a), String(b)) => a.as_str().eq(b.as_str()),
|
||||||
|
(Null, Null) => true,
|
||||||
|
(AttrSet(a), AttrSet(b)) => a.eq_impl(b),
|
||||||
(List(a), List(b)) => a.eq(b),
|
(List(a), List(b)) => a.eq(b),
|
||||||
(Builtins, AttrSet(attrs)) => attrs.has_attr(vm.new_sym("builtins")),
|
_ => false,
|
||||||
(AttrSet(attrs), Builtins) => attrs.has_attr(vm.new_sym("builtins")),
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> PartialEq for Value<'gc> {
|
||||||
|
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)),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,68 +84,43 @@ impl<'vm> Value<'vm> {
|
|||||||
|
|
||||||
impl Eq for Value<'_> {}
|
impl Eq for Value<'_> {}
|
||||||
|
|
||||||
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)]
|
#[derive(IsVariant, Unwrap, Clone)]
|
||||||
pub enum ValueAsRef<'v, 'vm: 'v> {
|
pub enum ValueAsRef<'v, 'gc> {
|
||||||
Const(&'v Const),
|
Int(i64),
|
||||||
Thunk(&'v Thunk<'vm>),
|
Float(f64),
|
||||||
AttrSet(&'v AttrSet<'vm>),
|
Bool(bool),
|
||||||
List(&'v List<'vm>),
|
String(&'v str),
|
||||||
Catchable(&'v c::Catchable),
|
Null,
|
||||||
PrimOp(&'v PrimOp<'vm>),
|
Thunk(&'v Thunk<'gc>),
|
||||||
PartialPrimOp(&'v PartialPrimOp<'vm>),
|
AttrSet(&'v AttrSet<'gc>),
|
||||||
Func(&'v Func<'vm>),
|
List(&'v List<'gc>),
|
||||||
|
Catchable(&'v str),
|
||||||
|
PrimOp(&'v PrimOp),
|
||||||
|
PartialPrimOp(&'v PartialPrimOp<'gc>),
|
||||||
|
Func(&'v Func<'gc>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, IsVariant, Unwrap, PartialEq)]
|
impl<'gc, 'v> Value<'gc> {
|
||||||
pub enum ValueAsMut<'v, 'vm: 'v> {
|
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> {
|
||||||
Const(&'v mut Const),
|
|
||||||
Thunk(&'v Thunk<'vm>),
|
|
||||||
AttrSet(&'v mut AttrSet<'vm>),
|
|
||||||
List(&'v mut List<'vm>),
|
|
||||||
Catchable(&'v mut c::Catchable),
|
|
||||||
PrimOp(&'v mut PrimOp<'vm>),
|
|
||||||
PartialPrimOp(&'v mut PartialPrimOp<'vm>),
|
|
||||||
Func(&'v Func<'vm>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'v, 'vm: 'v> Value<'vm> {
|
|
||||||
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'vm> {
|
|
||||||
use Value::*;
|
use Value::*;
|
||||||
use ValueAsRef as R;
|
use ValueAsRef as R;
|
||||||
match self {
|
match self {
|
||||||
Const(x) => R::Const(x),
|
Int(x) => R::Int(*x),
|
||||||
|
Float(x) => R::Float(*x),
|
||||||
|
Bool(x) => R::Bool(*x),
|
||||||
|
String(x) => R::String(x),
|
||||||
|
Null => R::Null,
|
||||||
Thunk(x) => R::Thunk(x),
|
Thunk(x) => R::Thunk(x),
|
||||||
ThunkRef(x) => R::Thunk(x),
|
|
||||||
AttrSet(x) => R::AttrSet(x),
|
AttrSet(x) => R::AttrSet(x),
|
||||||
List(x) => R::List(x),
|
List(x) => R::List(x),
|
||||||
Catchable(x) => R::Catchable(x),
|
Catchable(x) => R::Catchable(x),
|
||||||
PrimOp(x) => R::PrimOp(x),
|
PrimOp(x) => R::PrimOp(x),
|
||||||
PartialPrimOp(x) => R::PartialPrimOp(x),
|
PartialPrimOp(x) => R::PartialPrimOp(x),
|
||||||
Func(x) => R::Func(x),
|
Func(x) => R::Func(x),
|
||||||
Builtins => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_mut(&'v mut self) -> ValueAsMut<'v, 'vm> {
|
|
||||||
use Value::*;
|
|
||||||
use ValueAsMut as M;
|
|
||||||
match self {
|
|
||||||
Const(x) => M::Const(x),
|
|
||||||
Thunk(x) => M::Thunk(x),
|
|
||||||
ThunkRef(x) => M::Thunk(x),
|
|
||||||
AttrSet(x) => M::AttrSet(Rc::make_mut(x)),
|
|
||||||
List(x) => M::List(Rc::make_mut(x)),
|
|
||||||
Catchable(x) => M::Catchable(x),
|
|
||||||
PrimOp(x) => M::PrimOp(Rc::make_mut(x)),
|
|
||||||
PartialPrimOp(x) => M::PartialPrimOp(Rc::make_mut(x)),
|
|
||||||
Func(x) => M::Func(x),
|
|
||||||
Builtins => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'gc> Value<'gc> {
|
||||||
use Value::Const as VmConst;
|
|
||||||
impl<'vm> Value<'vm> {
|
|
||||||
pub fn ok(self) -> Result<Self> {
|
pub fn ok(self) -> Result<Self> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@@ -144,16 +128,18 @@ impl<'vm> Value<'vm> {
|
|||||||
pub fn typename(&self) -> &'static str {
|
pub fn typename(&self) -> &'static str {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
Const(_) => unreachable!(),
|
Int(_) => "int",
|
||||||
|
Float(_) => "float",
|
||||||
|
Bool(_) => "bool",
|
||||||
|
String(_) => "string",
|
||||||
|
Null => "null",
|
||||||
Thunk(_) => "thunk",
|
Thunk(_) => "thunk",
|
||||||
ThunkRef(_) => "thunk",
|
|
||||||
AttrSet(_) => "set",
|
AttrSet(_) => "set",
|
||||||
List(_) => "list",
|
List(_) => "list",
|
||||||
Catchable(_) => unreachable!(),
|
Catchable(_) => unreachable!(),
|
||||||
PrimOp(_) => "lambda",
|
PrimOp(_) => "lambda",
|
||||||
PartialPrimOp(_) => "lambda",
|
PartialPrimOp(_) => "lambda",
|
||||||
Func(_) => "lambda",
|
Func(_) => "lambda",
|
||||||
Builtins => "set",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,139 +151,139 @@ impl<'vm> Value<'vm> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self, vm: &'vm VM<'_>, args: Vec<Self>) -> Result<Self> {
|
pub fn call(&mut self, arg: Self, vm: &VM) -> Result<()> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
if matches!(arg, Value::Catchable(_)) {
|
||||||
PrimOp(func) => func.call(vm, args),
|
*self = arg;
|
||||||
PartialPrimOp(func) => func.call(vm, args),
|
return Ok(());
|
||||||
func @ Value::Func(_) => {
|
}
|
||||||
let mut iter = args.into_iter();
|
*self = match self {
|
||||||
let mut func = func.clone();
|
PrimOp(func) => func.call(arg, vm),
|
||||||
while let Some(arg) = iter.next() {
|
PartialPrimOp(func) => func.call(arg, vm),
|
||||||
func = match func {
|
Catchable(_) => return Ok(()),
|
||||||
PrimOp(func) => {
|
_ => todo!(),
|
||||||
return func.call(vm, [arg].into_iter().chain(iter).collect());
|
}?;
|
||||||
}
|
Ok(())
|
||||||
PartialPrimOp(func) => {
|
}
|
||||||
return func.call(vm, [arg].into_iter().chain(iter).collect());
|
|
||||||
}
|
pub fn not(&mut self) {
|
||||||
Func(func) => func.call(vm, arg)?,
|
use Value::*;
|
||||||
_ => todo!(),
|
*self = match &*self {
|
||||||
}
|
Bool(bool) => Bool(!bool),
|
||||||
}
|
Value::Catchable(_) => return,
|
||||||
func.ok()
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and(&mut self, other: Self) {
|
||||||
|
use Value::*;
|
||||||
|
*self = match (&*self, other) {
|
||||||
|
(Bool(a), Bool(b)) => Bool(*a && b),
|
||||||
|
(Value::Catchable(_), _) => return,
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(&mut self, other: Self) {
|
||||||
|
use Value::*;
|
||||||
|
*self = match (&*self, other) {
|
||||||
|
(Bool(a), Bool(b)) => Bool(*a || b),
|
||||||
|
(Value::Catchable(_), _) => return,
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eq(&mut self, other: Self) {
|
||||||
|
use Value::Bool;
|
||||||
|
*self = match (&*self, other) {
|
||||||
|
(Value::Catchable(_), _) => return,
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
(s, other) => Bool(s.eq_impl(&other)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lt(&mut self, other: Self) {
|
||||||
|
use Value::*;
|
||||||
|
*self = Bool(match (&*self, other) {
|
||||||
|
(Int(a), Int(b)) => *a < b,
|
||||||
|
(Int(a), Float(b)) => (*a as f64) < b,
|
||||||
|
(Float(a), Int(b)) => *a < b as f64,
|
||||||
|
(Float(a), Float(b)) => *a < b,
|
||||||
|
(String(a), String(b)) => a.as_str() < b.as_str(),
|
||||||
|
(Value::Catchable(_), _) => return,
|
||||||
|
(_, x @ Value::Catchable(_)) => {
|
||||||
|
*self = x;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
x @ Catchable(_) => x.clone().ok(),
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match self {
|
|
||||||
VmConst(Bool(bool)) => VmConst(Bool(!bool)),
|
|
||||||
x @ Value::Catchable(_) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn and(self, other: Self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a && b)),
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn or(self, other: Self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a || b)),
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eq(self, other: Self, vm: &'vm VM<'_>) -> Self {
|
|
||||||
use Const::Bool;
|
|
||||||
match (self, other) {
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
(s, other) => VmConst(Bool(s.eq_impl(&other, vm))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lt(self, other: Self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
VmConst(Bool(match (self, other) {
|
|
||||||
(VmConst(Int(a)), VmConst(Int(b))) => a < b,
|
|
||||||
(VmConst(Int(a)), VmConst(Float(b))) => (a as f64) < b,
|
|
||||||
(VmConst(Float(a)), VmConst(Int(b))) => a < b as f64,
|
|
||||||
(VmConst(Float(a)), VmConst(Float(b))) => a < b,
|
|
||||||
(VmConst(String(a)), VmConst(String(b))) => a < b,
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => return x,
|
|
||||||
_ => todo!(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn neg(self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match self {
|
|
||||||
VmConst(Int(int)) => VmConst(Int(-int)),
|
|
||||||
VmConst(Float(float)) => VmConst(Float(-float)),
|
|
||||||
x @ Value::Catchable(_) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(self, other: Self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a + b)),
|
|
||||||
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 + b)),
|
|
||||||
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a + b as f64)),
|
|
||||||
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a + b)),
|
|
||||||
(VmConst(String(a)), VmConst(String(b))) => {
|
|
||||||
let mut string = a.clone();
|
|
||||||
string.push_str(b.as_str());
|
|
||||||
VmConst(String(string))
|
|
||||||
}
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mul(self, other: Self) -> Self {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a * b)),
|
|
||||||
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 * b)),
|
|
||||||
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a * b as f64)),
|
|
||||||
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a * b)),
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn div(self, other: Self) -> Result<Self> {
|
|
||||||
use Const::*;
|
|
||||||
Ok(match (self, other) {
|
|
||||||
(_, VmConst(Int(0))) => return Err(Error::EvalError("division by zero".to_string())),
|
|
||||||
(_, VmConst(Float(0.))) => {
|
|
||||||
return Err(Error::EvalError("division by zero".to_string()));
|
|
||||||
}
|
|
||||||
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a / b)),
|
|
||||||
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 / b)),
|
|
||||||
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a / b as f64)),
|
|
||||||
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a / b)),
|
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn neg(&mut self) {
|
||||||
|
use Value::*;
|
||||||
|
*self = match &*self {
|
||||||
|
Int(int) => Int(-int),
|
||||||
|
Float(float) => Float(-float),
|
||||||
|
Value::Catchable(_) => return,
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, other: Self) {
|
||||||
|
use Value::*;
|
||||||
|
replace_with_or_abort(self, |a| match (a, other) {
|
||||||
|
(Int(a), Int(b)) => Int(a + b),
|
||||||
|
(Int(a), Float(b)) => Float(a as f64 + b),
|
||||||
|
(Float(a), Int(b)) => Float(a + b as f64),
|
||||||
|
(Float(a), Float(b)) => Float(a + b),
|
||||||
|
(String(mut a), String(b)) => {
|
||||||
|
Rc::make_mut(&mut a).push_str(&b);
|
||||||
|
String(a)
|
||||||
|
}
|
||||||
|
(a @ Value::Catchable(_), _) => a,
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
_ => todo!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul(&mut self, other: Self) {
|
||||||
|
use Value::*;
|
||||||
|
*self = match (&*self, other) {
|
||||||
|
(Int(a), Int(b)) => Int(a * b),
|
||||||
|
(Int(a), Float(b)) => Float(*a as f64 * b),
|
||||||
|
(Float(a), Int(b)) => Float(a * b as f64),
|
||||||
|
(Float(a), Float(b)) => Float(a * b),
|
||||||
|
(Value::Catchable(_), _) => return,
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div(&mut self, other: Self) -> Result<()> {
|
||||||
|
use Value::*;
|
||||||
|
*self = match (&*self, other) {
|
||||||
|
(_, Int(0)) => return Err(Error::EvalError("division by zero".to_string())),
|
||||||
|
(_, Float(0.)) => {
|
||||||
|
return Err(Error::EvalError("division by zero".to_string()));
|
||||||
|
}
|
||||||
|
(Int(a), Int(b)) => Int(a / b),
|
||||||
|
(Int(a), Float(b)) => Float(*a as f64 / b),
|
||||||
|
(Float(a), Int(b)) => Float(a / b as f64),
|
||||||
|
(Float(a), Float(b)) => Float(a / b),
|
||||||
|
(Value::Catchable(_), _) => return Ok(()),
|
||||||
|
(_, x @ Value::Catchable(_)) => x,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
|
pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
|
||||||
match (self.coerce_to_string(), other.coerce_to_string()) {
|
match (self.coerce_to_string(), other.coerce_to_string()) {
|
||||||
(VmConst(Const::String(a)), VmConst(Const::String(b))) => a.push_str(b.as_str()),
|
(Value::String(a), Value::String(b)) => {
|
||||||
|
Rc::make_mut(a).push_str(b.as_str());
|
||||||
|
}
|
||||||
(_, Value::Catchable(_)) => *self = other,
|
(_, Value::Catchable(_)) => *self = other,
|
||||||
(Value::Catchable(_), _) => (),
|
(Value::Catchable(_), _) => (),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
@@ -317,20 +303,23 @@ impl<'vm> Value<'vm> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn concat(self, other: Self) -> Self {
|
pub fn concat(&mut self, other: Self) {
|
||||||
|
if let x @ Value::Catchable(_) = other {
|
||||||
|
*self = x;
|
||||||
|
return;
|
||||||
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::List(mut a), Value::List(b)) => {
|
(Value::List(a), Value::List(b)) => {
|
||||||
Rc::make_mut(&mut a).concat(b.as_ref());
|
Rc::make_mut(a).concat(&b);
|
||||||
Value::List(a)
|
|
||||||
}
|
}
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
(Value::Catchable(_), _) => (),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self {
|
pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self {
|
||||||
if let Value::AttrSet(attrs) = self {
|
if let Value::AttrSet(attrs) = self {
|
||||||
Rc::make_mut(attrs).push_attr(sym, val)
|
Rc::make_mut(attrs).push_attr(sym, val);
|
||||||
} else if let Value::Catchable(_) = self {
|
} else if let Value::Catchable(_) = self {
|
||||||
} else if let Value::Catchable(_) = val {
|
} else if let Value::Catchable(_) = val {
|
||||||
*self = val
|
*self = val
|
||||||
@@ -340,18 +329,21 @@ impl<'vm> Value<'vm> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self, other: Self) -> Self {
|
pub fn update(&mut self, other: Self) {
|
||||||
|
if let x @ Value::Catchable(_) = other {
|
||||||
|
*self = x;
|
||||||
|
return;
|
||||||
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::AttrSet(mut a), Value::AttrSet(b)) => {
|
(Value::AttrSet(a), Value::AttrSet(b)) => {
|
||||||
Rc::make_mut(&mut a).update(b.as_ref());
|
Rc::make_mut(a).update(&b);
|
||||||
Value::AttrSet(a)
|
|
||||||
}
|
}
|
||||||
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
|
(Value::Catchable(_), _) => (),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(&mut self, sym: usize, vm: &'vm VM<'_>) -> Result<&mut Self> {
|
pub fn select(&mut self, sym: usize, vm: &VM<'gc>) -> Result<&mut Self> {
|
||||||
let val = match self {
|
let val = match self {
|
||||||
Value::AttrSet(attrs) => attrs
|
Value::AttrSet(attrs) => attrs
|
||||||
.select(sym)
|
.select(sym)
|
||||||
@@ -362,10 +354,7 @@ impl<'vm> Value<'vm> {
|
|||||||
self.typename()
|
self.typename()
|
||||||
))),
|
))),
|
||||||
}?;
|
}?;
|
||||||
if let Value::Builtins = val {
|
*self = val;
|
||||||
} else {
|
|
||||||
*self = val;
|
|
||||||
}
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,26 +369,23 @@ impl<'vm> Value<'vm> {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Value::Builtins = val {
|
*self = val;
|
||||||
} else {
|
|
||||||
*self = val;
|
|
||||||
}
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_attr(&mut self, sym: usize) -> &mut Self {
|
pub fn has_attr(&mut self, sym: usize) -> &mut Self {
|
||||||
if let Value::AttrSet(attrs) = self {
|
if let Value::AttrSet(attrs) = self {
|
||||||
let val = VmConst(Const::Bool(attrs.has_attr(sym)));
|
let val = Value::Bool(attrs.has_attr(sym));
|
||||||
*self = val;
|
*self = val;
|
||||||
} else if let Value::Catchable(_) = self {
|
} else if let Value::Catchable(_) = self {
|
||||||
} else {
|
} else {
|
||||||
*self = VmConst(Const::Bool(false));
|
*self = Value::Bool(false);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coerce_to_string(&mut self) -> &mut Self {
|
pub fn coerce_to_string(&mut self) -> &mut Self {
|
||||||
if let VmConst(Const::String(_)) = self {
|
if let Value::String(_) = self {
|
||||||
} else if let Value::Catchable(_) = self {
|
} else if let Value::Catchable(_) = self {
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -407,111 +393,110 @@ impl<'vm> Value<'vm> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> {
|
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||||
if let Value::Thunk(thunk) = self {
|
|
||||||
let value = thunk.force(vm)?;
|
|
||||||
*self = value
|
|
||||||
} else if let Value::ThunkRef(thunk) = self {
|
|
||||||
let value = thunk.force(vm)?;
|
|
||||||
*self = value
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> {
|
|
||||||
match self {
|
|
||||||
Value::Thunk(thunk) => {
|
|
||||||
let mut value = thunk.force(vm)?;
|
|
||||||
let _ = value.force_deep(vm)?;
|
|
||||||
*self = value;
|
|
||||||
}
|
|
||||||
Value::ThunkRef(thunk) => {
|
|
||||||
let mut value = thunk.force(vm)?;
|
|
||||||
let _ = value.force_deep(vm)?;
|
|
||||||
*self = value;
|
|
||||||
}
|
|
||||||
Value::List(list) => Rc::make_mut(list).force_deep(vm)?,
|
|
||||||
Value::AttrSet(attrs) => Rc::make_mut(attrs).force_deep(vm)?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value {
|
|
||||||
use self::Value::*;
|
use self::Value::*;
|
||||||
use p::Value;
|
use p::Value;
|
||||||
if seen.contains(self) {
|
if seen.contains(self) {
|
||||||
return Value::Repeated;
|
return Value::Repeated;
|
||||||
}
|
}
|
||||||
seen.insert(self.clone());
|
|
||||||
match self {
|
match self {
|
||||||
AttrSet(attrs) => attrs.to_public(vm, seen),
|
AttrSet(attrs) => {
|
||||||
List(list) => list.to_public(vm, seen),
|
seen.insert(self.clone());
|
||||||
Catchable(catchable) => Value::Catchable(catchable.clone()),
|
attrs.to_public(vm, seen)
|
||||||
Const(cnst) => Value::Const(cnst.clone().into()),
|
}
|
||||||
|
List(list) => {
|
||||||
|
seen.insert(self.clone());
|
||||||
|
list.to_public(vm, seen)
|
||||||
|
}
|
||||||
|
Catchable(catchable) => Value::Catchable(catchable.as_ref().clone().into()),
|
||||||
|
Int(x) => Value::Const(Const::Int(*x)),
|
||||||
|
Float(x) => Value::Const(Const::Float(*x)),
|
||||||
|
Bool(x) => Value::Const(Const::Bool(*x)),
|
||||||
|
String(x) => Value::Const(Const::String(x.as_str().into())),
|
||||||
|
Null => Value::Const(Const::Null),
|
||||||
Thunk(_) => Value::Thunk,
|
Thunk(_) => Value::Thunk,
|
||||||
ThunkRef(_) => Value::Thunk,
|
|
||||||
PrimOp(primop) => Value::PrimOp(primop.name),
|
PrimOp(primop) => Value::PrimOp(primop.name),
|
||||||
PartialPrimOp(primop) => Value::PartialPrimOp(primop.name),
|
PartialPrimOp(primop) => Value::PartialPrimOp(primop.name),
|
||||||
Func(_) => Value::Func,
|
Func(_) => Value::Func,
|
||||||
Builtins => Value::Repeated,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[repr(transparent)]
|
||||||
pub struct Thunk<'vm> {
|
#[derive(Clone)]
|
||||||
pub thunk: RefCell<_Thunk<'vm>>,
|
pub struct Thunk<'gc> {
|
||||||
|
pub thunk: Rc<RefCell<_Thunk<'gc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, IsVariant, Unwrap, Clone)]
|
#[derive(IsVariant, Unwrap)]
|
||||||
pub enum _Thunk<'vm> {
|
pub enum _Thunk<'gc> {
|
||||||
Code(&'vm OpCodes, OnceCell<Env<'vm>>),
|
Code(&'gc OpCodes, Option<Env<'gc>>),
|
||||||
SuspendedFrom(*const Thunk<'vm>),
|
Suspended,
|
||||||
Value(Value<'vm>),
|
Value(Value<'gc>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> Thunk<'vm> {
|
#[derive(Unwrap)]
|
||||||
pub fn new(opcodes: &'vm OpCodes) -> Self {
|
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 {
|
||||||
thunk: RefCell::new(_Thunk::Code(opcodes, OnceCell::new())),
|
thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capture(&self, env: Env<'vm>) {
|
pub fn capture_env(&self, env: Rc<VmEnv<'gc>>) {
|
||||||
if let _Thunk::Code(_, envcell) = &*self.thunk.borrow() {
|
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
|
||||||
envcell.get_or_init(|| env);
|
let _ = envcell.insert(Env::Strong(env));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force(&self, vm: &'vm VM<'_>) -> Result<Value<'vm>> {
|
pub fn capture_env_weak(&self, env: Weak<VmEnv<'gc>>) {
|
||||||
match &*self.thunk.borrow() {
|
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
|
||||||
_Thunk::Value(value) => return Ok(value.clone()),
|
let _ = envcell.insert(Env::Weak(env));
|
||||||
_Thunk::SuspendedFrom(from) => {
|
}
|
||||||
return Err(Error::EvalError(format!(
|
}
|
||||||
"thunk {:p} already suspended from {from:p} (infinite recursion encountered)",
|
|
||||||
self as *const Thunk
|
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
|
||||||
}
|
}
|
||||||
_Thunk::Code(..) => (),
|
});
|
||||||
}
|
|
||||||
let (opcodes, env) = std::mem::replace(
|
|
||||||
&mut *self.thunk.borrow_mut(),
|
|
||||||
_Thunk::SuspendedFrom(self as *const Thunk),
|
|
||||||
)
|
|
||||||
.unwrap_code();
|
|
||||||
let value = vm.eval(opcodes.iter().copied(), env.get().unwrap().clone())?;
|
|
||||||
let _ = std::mem::replace(
|
|
||||||
&mut *self.thunk.borrow_mut(),
|
|
||||||
_Thunk::Value(value.clone().into()),
|
|
||||||
);
|
|
||||||
Ok(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&'vm self) -> Option<Value<'vm>> {
|
pub fn suspend(&self) -> Result<(&'gc OpCodes, Rc<VmEnv<'gc>>)> {
|
||||||
match &*self.thunk.borrow() {
|
use _Thunk::*;
|
||||||
_Thunk::Value(value) => Some(value.clone()),
|
match std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Suspended) {
|
||||||
_ => None,
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,45 +8,42 @@ use crate::vm::VM;
|
|||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Constructor)]
|
#[derive(Debug, Clone, Constructor)]
|
||||||
pub struct PrimOp<'vm> {
|
pub struct PrimOp {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
func: fn(&'vm VM<'_>, Vec<Value<'vm>>) -> Result<Value<'vm>>,
|
func: for<'gc> fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for PrimOp<'_> {
|
impl PartialEq for PrimOp {
|
||||||
fn eq(&self, _: &Self) -> bool {
|
fn eq(&self, _: &Self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> PrimOp<'vm> {
|
impl PrimOp {
|
||||||
pub fn call(&self, vm: &'vm VM<'_>, args: Vec<Value<'vm>>) -> Result<Value<'vm>> {
|
pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
|
||||||
if (args.len()) < self.arity {
|
let mut args = Vec::with_capacity(self.arity);
|
||||||
Value::PartialPrimOp(
|
args.push(arg);
|
||||||
PartialPrimOp {
|
if self.arity > 1 {
|
||||||
name: self.name,
|
Value::PartialPrimOp(Rc::new(PartialPrimOp {
|
||||||
arity: self.arity - args.len(),
|
name: self.name,
|
||||||
args,
|
arity: self.arity - 1,
|
||||||
func: self.func,
|
args,
|
||||||
}
|
func: self.func,
|
||||||
.into(),
|
}))
|
||||||
)
|
|
||||||
.ok()
|
.ok()
|
||||||
} else if args.len() == self.arity {
|
|
||||||
(self.func)(vm, args)
|
|
||||||
} else {
|
} else {
|
||||||
unimplemented!()
|
(self.func)(args, vm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PartialPrimOp<'vm> {
|
pub struct PartialPrimOp<'gc> {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
args: Vec<Value<'vm>>,
|
args: Vec<Value<'gc>>,
|
||||||
func: fn(&'vm VM<'_>, Vec<Value<'vm>>) -> Result<Value<'vm>>,
|
func: fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for PartialPrimOp<'_> {
|
impl PartialEq for PartialPrimOp<'_> {
|
||||||
@@ -55,20 +52,21 @@ impl PartialEq for PartialPrimOp<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> PartialPrimOp<'vm> {
|
impl<'gc> PartialPrimOp<'gc> {
|
||||||
pub fn call(self: &Rc<Self>, vm: &'vm VM<'_>, args: Vec<Value<'vm>>) -> Result<Value<'vm>> {
|
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
|
||||||
let len = args.len();
|
let func = self.func;
|
||||||
let mut self_clone = self.clone();
|
let Some(ret) = ({
|
||||||
let self_mut = Rc::make_mut(&mut self_clone);
|
let self_mut = Rc::make_mut(self);
|
||||||
self_mut.args.extend(args);
|
self_mut.args.push(arg);
|
||||||
self_mut.arity -= len;
|
self_mut.arity -= 1;
|
||||||
if self_mut.arity > 0 {
|
if self_mut.arity == 0 {
|
||||||
Value::PartialPrimOp(self_clone).ok()
|
Some(func(std::mem::take(&mut self_mut.args), vm))
|
||||||
} else if self_mut.arity == 0 {
|
} else {
|
||||||
let args = std::mem::replace(&mut self_mut.args, Vec::new());
|
None
|
||||||
(self.func)(vm, args)
|
}
|
||||||
} else {
|
}) else {
|
||||||
unimplemented!()
|
return Value::PartialPrimOp(self.clone()).ok();
|
||||||
}
|
};
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
// TODO: Contextful String
|
// TODO: Contextful String
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use rpds::List;
|
|
||||||
|
|
||||||
pub struct StringContext {
|
pub struct StringContext {
|
||||||
context: List<()>,
|
context: Vec<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringContext {
|
impl StringContext {
|
||||||
pub fn new() -> StringContext {
|
pub fn new() -> StringContext {
|
||||||
StringContext {
|
StringContext {
|
||||||
context: List::new(),
|
context: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,9 @@ use std::sync::LazyLock;
|
|||||||
use derive_more::{Constructor, IsVariant, Unwrap};
|
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rpds::VectorSync;
|
|
||||||
|
|
||||||
use super::common::*;
|
use super::common::*;
|
||||||
|
|
||||||
mod cnst;
|
|
||||||
|
|
||||||
pub use cnst::Const;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
||||||
pub struct Symbol(EcoString);
|
pub struct Symbol(EcoString);
|
||||||
|
|
||||||
@@ -80,9 +75,16 @@ impl Debug for AttrSet {
|
|||||||
|
|
||||||
impl Display for AttrSet {
|
impl Display for AttrSet {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
use Value::*;
|
||||||
write!(f, "{{ ")?;
|
write!(f, "{{ ")?;
|
||||||
for (k, v) in self.data.iter() {
|
for (k, v) in self.data.iter() {
|
||||||
write!(f, "{k} = {v}; ")?;
|
write!(f, "{k} = ")?;
|
||||||
|
match v {
|
||||||
|
AttrSet(_) => write!(f, "{{ ... }}"),
|
||||||
|
List(_) => write!(f, "[ ... ]"),
|
||||||
|
v => write!(f, "{v}"),
|
||||||
|
}?;
|
||||||
|
write!(f, "; ")?;
|
||||||
}
|
}
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,7 @@ impl Display for AttrSet {
|
|||||||
|
|
||||||
#[derive(Constructor, Clone, Debug, PartialEq)]
|
#[derive(Constructor, Clone, Debug, PartialEq)]
|
||||||
pub struct List {
|
pub struct List {
|
||||||
data: VectorSync<Value>,
|
data: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for List {
|
impl Display for List {
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
|
||||||
|
|
||||||
use derive_more::{IsVariant, Unwrap};
|
|
||||||
use ecow::EcoString;
|
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
|
|
||||||
use super::super::internal as i;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
|
||||||
pub enum Const {
|
|
||||||
Bool(bool),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
String(EcoString),
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Const {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
||||||
use Const::*;
|
|
||||||
match self {
|
|
||||||
Bool(b) => write!(f, "{b}"),
|
|
||||||
Int(i) => write!(f, "{i}"),
|
|
||||||
Float(float) => write!(f, "{float}"),
|
|
||||||
String(s) => write!(f, "{s}"),
|
|
||||||
Null => write!(f, "null"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i::Const> for Const {
|
|
||||||
fn from(value: i::Const) -> Self {
|
|
||||||
use i::Const::*;
|
|
||||||
match value {
|
|
||||||
Bool(bool) => Const::Bool(bool),
|
|
||||||
Int(int) => Const::Int(int),
|
|
||||||
Float(float) => Const::Float(float),
|
|
||||||
String(string) => Const::String(string),
|
|
||||||
Null => Const::Null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for Const {
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
Const::Bool(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i64> for Const {
|
|
||||||
fn from(value: i64) -> Self {
|
|
||||||
Const::Int(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> for Const {
|
|
||||||
fn from(value: f64) -> Self {
|
|
||||||
Const::Float(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EcoString> for Const {
|
|
||||||
fn from(value: EcoString) -> Self {
|
|
||||||
Const::String(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Const {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Const::String(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Const {
|
|
||||||
fn from(value: &str) -> Self {
|
|
||||||
Const::String(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Const> for &'a bool {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Const::Bool(b) => Ok(b),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> TryFrom<&'a Const> for &'a i64 {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Const::Int(int) => Ok(int),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Const> for &'a f64 {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Const::Float(float) => Ok(float),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Const> for &'a str {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Const::String(string) => Ok(string),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Const {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
use Const::*;
|
|
||||||
match (self, other) {
|
|
||||||
(Bool(a), Bool(b)) => a == b,
|
|
||||||
(Int(a), Int(b)) => a == b,
|
|
||||||
(Float(a), Float(b)) => a == b,
|
|
||||||
(String(a), String(b)) => a == b,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Const {}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
use hashbrown::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crate::ty::internal::{AttrSet, Value};
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct Env<'vm> {
|
|
||||||
last: Option<Rc<Env<'vm>>>,
|
|
||||||
map: Rc<HashMap<usize, Value<'vm>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'vm> Env<'vm> {
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
Env::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup(&self, symbol: usize) -> Option<Value<'vm>> {
|
|
||||||
if let Some(val) = self.map.get(&symbol).cloned() {
|
|
||||||
return Some(val);
|
|
||||||
}
|
|
||||||
self.last.as_ref().map(|env| env.lookup(symbol)).flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, symbol: usize, value: Value<'vm>) {
|
|
||||||
Rc::make_mut(&mut self.map).insert(symbol, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter(self, new: impl Iterator<Item = (usize, Value<'vm>)>) -> Self {
|
|
||||||
let map = Rc::new(new.collect());
|
|
||||||
let last = Some(
|
|
||||||
Env {
|
|
||||||
last: self.last,
|
|
||||||
map: self.map,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
Env { last, map }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_with(self, new: Rc<AttrSet<'vm>>) -> Self {
|
|
||||||
let map = Rc::new(
|
|
||||||
new.as_inner()
|
|
||||||
.iter()
|
|
||||||
.map(|(&k, v)| {
|
|
||||||
(
|
|
||||||
k,
|
|
||||||
if let Value::Builtins = v {
|
|
||||||
Value::AttrSet(new.clone())
|
|
||||||
} else {
|
|
||||||
v.clone()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
let last = Some(
|
|
||||||
Env {
|
|
||||||
last: self.last.clone(),
|
|
||||||
map: self.map.clone(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
Env { last, map }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn leave(self) -> Self {
|
|
||||||
self.last.unwrap().as_ref().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use inkwell::builder::Builder;
|
|
||||||
use inkwell::context::Context;
|
|
||||||
use inkwell::execution_engine::ExecutionEngine;
|
|
||||||
use inkwell::module::Module;
|
|
||||||
use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, StructType};
|
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue};
|
|
||||||
use inkwell::{AddressSpace, OptimizationLevel};
|
|
||||||
|
|
||||||
use crate::stack::Stack;
|
|
||||||
|
|
||||||
use super::STACK_SIZE;
|
|
||||||
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum ValueTag {
|
|
||||||
Int,
|
|
||||||
String,
|
|
||||||
Bool,
|
|
||||||
AttrSet,
|
|
||||||
List,
|
|
||||||
Function,
|
|
||||||
Thunk,
|
|
||||||
Path,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct JITValue {
|
|
||||||
tag: ValueTag,
|
|
||||||
data: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct JITContext<'ctx> {
|
|
||||||
context: &'ctx Context,
|
|
||||||
module: Module<'ctx>,
|
|
||||||
builder: Builder<'ctx>,
|
|
||||||
execution_engine: ExecutionEngine<'ctx>,
|
|
||||||
stack: Stack<BasicValueEnum<'ctx>, STACK_SIZE>,
|
|
||||||
cur_func: Option<FunctionValue<'ctx>>,
|
|
||||||
|
|
||||||
value_type: StructType<'ctx>,
|
|
||||||
func_type: FunctionType<'ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> JITContext<'ctx> {
|
|
||||||
pub fn new(context: &'ctx Context) -> Pin<Box<Self>> {
|
|
||||||
let module = context.create_module("nixjit");
|
|
||||||
let stack = Stack::new();
|
|
||||||
|
|
||||||
let int_type = context.i64_type();
|
|
||||||
let pointer_type = context.ptr_type(AddressSpace::default());
|
|
||||||
let value_type = context.struct_type(&[int_type.into(), int_type.into()], false);
|
|
||||||
let func_type = value_type.fn_type(
|
|
||||||
&[pointer_type.into(), pointer_type.into(), value_type.into()],
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
Pin::new(Box::new(JITContext {
|
|
||||||
execution_engine: module
|
|
||||||
.create_jit_execution_engine(OptimizationLevel::Default)
|
|
||||||
.unwrap(),
|
|
||||||
builder: context.create_builder(),
|
|
||||||
context,
|
|
||||||
module,
|
|
||||||
stack,
|
|
||||||
cur_func: None,
|
|
||||||
|
|
||||||
value_type,
|
|
||||||
func_type,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_int(&self, int: i64) -> IntValue {
|
|
||||||
self.context.i64_type().const_int(int as u64, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_trace(&mut self) {}
|
|
||||||
}
|
|
||||||
499
src/vm/mod.rs
499
src/vm/mod.rs
@@ -1,69 +1,309 @@
|
|||||||
use hashbrown::{HashMap, HashSet};
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::pin::Pin;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use inkwell::context::Context;
|
||||||
|
|
||||||
use crate::builtins::env;
|
|
||||||
use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp};
|
use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp};
|
||||||
|
use crate::env::VmEnv;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
use crate::jit::{JITContext, JITFunc};
|
||||||
|
use crate::stack::Stack;
|
||||||
|
use crate::ty::common::Const;
|
||||||
use crate::ty::internal::*;
|
use crate::ty::internal::*;
|
||||||
use crate::ty::public::{self as p, Symbol};
|
use crate::ty::public::{self as p, Symbol};
|
||||||
|
|
||||||
use crate::stack::Stack;
|
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
pub use env::Env;
|
|
||||||
pub use jit::JITContext;
|
|
||||||
|
|
||||||
mod env;
|
|
||||||
mod jit;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
pub const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
|
const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
|
||||||
|
const COLLECTOR_GRANULARITY: f64 = 1024.0;
|
||||||
|
|
||||||
pub fn run(prog: Program, jit: Pin<Box<JITContext<'_>>>) -> Result<p::Value> {
|
struct ContextWrapper(Context);
|
||||||
let vm = VM::new(
|
|
||||||
prog.thunks,
|
pub fn run(mut prog: Program) -> Result<p::Value> {
|
||||||
prog.funcs,
|
let jit = Context::create();
|
||||||
RefCell::new(prog.symbols),
|
prog.thunks.iter_mut().for_each(|code| code.reverse());
|
||||||
RefCell::new(prog.symmap),
|
prog.funcs
|
||||||
prog.consts,
|
.iter_mut()
|
||||||
jit,
|
.for_each(|F { opcodes, .. }| opcodes.reverse());
|
||||||
);
|
let vm = VM {
|
||||||
let env = env(&vm);
|
thunks: prog.thunks,
|
||||||
let mut seen = HashSet::new();
|
funcs: prog.funcs,
|
||||||
let value = vm
|
consts: prog.consts,
|
||||||
.eval(prog.top_level.into_iter(), env)?
|
symbols: RefCell::new(prog.symbols),
|
||||||
.to_public(&vm, &mut seen);
|
symmap: RefCell::new(prog.symmap),
|
||||||
Ok(value)
|
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)]
|
#[derive(Constructor)]
|
||||||
pub struct VM<'jit> {
|
pub struct VM<'gc> {
|
||||||
thunks: Box<[OpCodes]>,
|
thunks: Box<[OpCodes]>,
|
||||||
funcs: Box<[F]>,
|
funcs: Box<[F]>,
|
||||||
symbols: RefCell<Vec<EcoString>>,
|
symbols: RefCell<Vec<EcoString>>,
|
||||||
symmap: RefCell<HashMap<EcoString, usize>>,
|
symmap: RefCell<HashMap<EcoString, usize>>,
|
||||||
consts: Box<[Const]>,
|
consts: Box<[Const]>,
|
||||||
jit: Pin<Box<JITContext<'jit>>>,
|
jit: JITContext<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm, 'jit: 'vm> VM<'jit> {
|
impl<'gc> VM<'gc> {
|
||||||
pub fn get_thunk(&self, idx: usize) -> &OpCodes {
|
pub fn get_thunk(&self, idx: usize) -> &'gc OpCodes {
|
||||||
&self.thunks[idx]
|
unsafe { &*(&self.thunks[idx] as *const _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_func(&self, idx: usize) -> &F {
|
pub fn get_func(&self, idx: usize) -> &'gc F {
|
||||||
&self.funcs[idx]
|
unsafe { &*(&self.funcs[idx] as *const _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sym(&self, idx: usize) -> Symbol {
|
pub fn get_sym(&self, idx: usize) -> Symbol {
|
||||||
self.symbols.borrow()[idx].clone().into()
|
self.symbols.borrow()[idx].clone().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_sym(&self, sym: impl Into<EcoString>) -> usize {
|
pub fn new_sym<T: Into<EcoString>>(&self, sym: T) -> usize {
|
||||||
let sym = sym.into();
|
let sym = sym.into();
|
||||||
if let Some(&idx) = self.symmap.borrow().get(&sym) {
|
if let Some(&idx) = self.symmap.borrow().get(&sym) {
|
||||||
idx
|
idx
|
||||||
@@ -76,192 +316,13 @@ impl<'vm, 'jit: 'vm> VM<'jit> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(
|
pub fn get_const(&self, idx: usize) -> Const {
|
||||||
&'vm self,
|
self.consts[idx].clone()
|
||||||
opcodes: impl Iterator<Item = OpCode>,
|
|
||||||
mut env: Env<'vm>,
|
|
||||||
) -> Result<Value<'vm>> {
|
|
||||||
let mut stack = Stack::<_, STACK_SIZE>::new();
|
|
||||||
let mut iter = opcodes.into_iter();
|
|
||||||
while let Some(opcode) = iter.next() {
|
|
||||||
let jmp = self.single_op(opcode, &mut stack, &mut env)?;
|
|
||||||
for _ in 0..jmp {
|
|
||||||
iter.next().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(stack.len(), 1);
|
|
||||||
let mut ret = stack.pop();
|
|
||||||
ret.force(self)?;
|
|
||||||
Ok(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
pub fn compile_func(&self, func: &'gc F) -> JITFunc<'gc> {
|
||||||
fn single_op<'s, const CAP: usize>(
|
self.jit
|
||||||
&'vm self,
|
.compile_seq(func.opcodes.iter().copied().rev(), self)
|
||||||
opcode: OpCode,
|
.unwrap()
|
||||||
stack: &'s mut Stack<Value<'vm>, CAP>,
|
|
||||||
env: &mut Env<'vm>,
|
|
||||||
) -> Result<usize> {
|
|
||||||
match opcode {
|
|
||||||
OpCode::Illegal => panic!("illegal opcode"),
|
|
||||||
OpCode::Const { idx } => stack.push(Value::Const(self.consts[idx].clone()))?,
|
|
||||||
OpCode::LoadThunk { idx } => {
|
|
||||||
stack.push(Value::Thunk(Thunk::new(self.get_thunk(idx)).into()))?
|
|
||||||
}
|
|
||||||
OpCode::CaptureEnv => match stack.tos()? {
|
|
||||||
Value::Thunk(thunk) => thunk.capture(env.clone()),
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
OpCode::ForceValue => {
|
|
||||||
stack.tos_mut()?.force(self)?;
|
|
||||||
}
|
|
||||||
OpCode::Jmp { step } => return Ok(step),
|
|
||||||
OpCode::JmpIfTrue { step } => {
|
|
||||||
if let Value::Const(Const::Bool(true)) = stack.pop() {
|
|
||||||
return Ok(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OpCode::JmpIfFalse { step } => {
|
|
||||||
if let Value::Const(Const::Bool(false)) = stack.pop() {
|
|
||||||
return Ok(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OpCode::Call { arity } => {
|
|
||||||
let mut args = Vec::with_capacity(arity);
|
|
||||||
for _ in 0..arity {
|
|
||||||
args.insert(0, stack.pop());
|
|
||||||
}
|
|
||||||
let mut func = stack.pop();
|
|
||||||
func.force(self)?;
|
|
||||||
stack.push(func.call(self, args)?)?;
|
|
||||||
}
|
|
||||||
OpCode::Func { idx } => {
|
|
||||||
let func = self.get_func(idx);
|
|
||||||
stack.push(Value::Func(Func::new(func, env.clone(), None).into()))?;
|
|
||||||
}
|
|
||||||
OpCode::UnOp { op } => {
|
|
||||||
use UnOp::*;
|
|
||||||
let mut value = stack.pop();
|
|
||||||
value.force(self)?;
|
|
||||||
stack.push(match op {
|
|
||||||
Neg => value.neg(),
|
|
||||||
Not => value.not(),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
OpCode::BinOp { op } => {
|
|
||||||
use BinOp::*;
|
|
||||||
let mut rhs = stack.pop();
|
|
||||||
let mut lhs = stack.pop();
|
|
||||||
lhs.force(self)?;
|
|
||||||
rhs.force(self)?;
|
|
||||||
stack.push(match op {
|
|
||||||
Add => lhs.add(rhs),
|
|
||||||
Sub => lhs.add(rhs.neg()),
|
|
||||||
Mul => lhs.mul(rhs),
|
|
||||||
Div => lhs.div(rhs)?,
|
|
||||||
And => lhs.and(rhs),
|
|
||||||
Or => lhs.or(rhs),
|
|
||||||
Eq => lhs.eq(rhs, self),
|
|
||||||
Lt => lhs.lt(rhs),
|
|
||||||
Con => lhs.concat(rhs),
|
|
||||||
Upd => lhs.update(rhs),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
OpCode::ConcatString => {
|
|
||||||
let mut rhs = stack.pop();
|
|
||||||
rhs.force(self)?;
|
|
||||||
stack.tos_mut()?.concat_string(rhs);
|
|
||||||
}
|
|
||||||
OpCode::Path => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
OpCode::List => {
|
|
||||||
stack.push(Value::List(List::empty().into()))?;
|
|
||||||
}
|
|
||||||
OpCode::PushElem => {
|
|
||||||
let elem = stack.pop();
|
|
||||||
stack.tos_mut()?.push(elem);
|
|
||||||
}
|
|
||||||
OpCode::AttrSet { cap } => {
|
|
||||||
stack.push(Value::AttrSet(AttrSet::with_capacity(cap).into()))?;
|
|
||||||
}
|
|
||||||
OpCode::FinalizeRec => {
|
|
||||||
let env = env.clone().enter(
|
|
||||||
stack
|
|
||||||
.tos()?
|
|
||||||
.clone()
|
|
||||||
.unwrap_attr_set()
|
|
||||||
.as_inner()
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.clone(), v.clone())),
|
|
||||||
);
|
|
||||||
stack.tos_mut()?.as_mut().unwrap_attr_set().capture(&env);
|
|
||||||
}
|
|
||||||
OpCode::PushStaticAttr { name } => {
|
|
||||||
let val = stack.pop();
|
|
||||||
stack.tos_mut()?.push_attr(name, val);
|
|
||||||
}
|
|
||||||
OpCode::PushDynamicAttr => {
|
|
||||||
let val = stack.pop();
|
|
||||||
let mut sym = stack.pop();
|
|
||||||
sym.force(self)?.coerce_to_string();
|
|
||||||
let sym = self.new_sym(sym.unwrap_const().unwrap_string());
|
|
||||||
stack.tos_mut()?.push_attr(sym, val);
|
|
||||||
}
|
|
||||||
OpCode::Select { sym } => {
|
|
||||||
stack.tos_mut()?.force(self)?.select(sym, self)?;
|
|
||||||
}
|
|
||||||
OpCode::SelectOrDefault { sym } => {
|
|
||||||
let default = stack.pop();
|
|
||||||
stack
|
|
||||||
.tos_mut()?
|
|
||||||
.force(self)?
|
|
||||||
.select_with_default(sym, default)?;
|
|
||||||
}
|
|
||||||
OpCode::SelectDynamic => {
|
|
||||||
let mut val = stack.pop();
|
|
||||||
val.force(self)?;
|
|
||||||
val.coerce_to_string();
|
|
||||||
let sym = self.new_sym(val.unwrap_const().unwrap_string());
|
|
||||||
stack.tos_mut()?.force(self)?.select(sym, self)?;
|
|
||||||
}
|
|
||||||
OpCode::SelectDynamicOrDefault => {
|
|
||||||
let default = stack.pop();
|
|
||||||
let mut val = stack.pop();
|
|
||||||
val.force(self)?;
|
|
||||||
val.coerce_to_string();
|
|
||||||
let sym = self.new_sym(val.unwrap_const().unwrap_string());
|
|
||||||
stack
|
|
||||||
.tos_mut()?
|
|
||||||
.force(self)?
|
|
||||||
.select_with_default(sym, default)?;
|
|
||||||
}
|
|
||||||
OpCode::HasAttr { sym } => {
|
|
||||||
stack.tos_mut()?.force(self)?.has_attr(sym);
|
|
||||||
}
|
|
||||||
OpCode::HasDynamicAttr => {
|
|
||||||
let mut val = stack.pop();
|
|
||||||
val.coerce_to_string();
|
|
||||||
let sym = self.new_sym(val.unwrap_const().unwrap_string());
|
|
||||||
stack.tos_mut()?.force(self)?.has_attr(sym);
|
|
||||||
}
|
|
||||||
OpCode::LookUp { sym } => {
|
|
||||||
stack.push(env.lookup(sym).ok_or_else(|| {
|
|
||||||
Error::EvalError(format!("{} not found", self.get_sym(sym)))
|
|
||||||
})?)?;
|
|
||||||
}
|
|
||||||
OpCode::EnterEnv => match stack.pop() {
|
|
||||||
Value::AttrSet(attrs) => *env = env.clone().enter_with(attrs),
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
OpCode::LeaveEnv => *env = env.clone().leave(),
|
|
||||||
OpCode::Assert => {
|
|
||||||
if !stack.pop().unwrap_const().unwrap_bool() {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use inkwell::context::Context;
|
|
||||||
use test::{Bencher, black_box};
|
use test::{Bencher, black_box};
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use rpds::vector_sync;
|
|
||||||
|
|
||||||
use crate::compile::compile;
|
use crate::compile::compile;
|
||||||
use crate::ir::downgrade;
|
use crate::ir::downgrade;
|
||||||
use crate::ty::public::Symbol;
|
use crate::ty::common::Const;
|
||||||
use crate::ty::public::*;
|
use crate::ty::public::*;
|
||||||
use crate::vm::JITContext;
|
|
||||||
|
|
||||||
use super::run;
|
use super::run;
|
||||||
|
|
||||||
@@ -21,9 +20,7 @@ fn test_expr(expr: &str, expected: Value) {
|
|||||||
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
||||||
let prog = compile(downgraded);
|
let prog = compile(downgraded);
|
||||||
dbg!(&prog);
|
dbg!(&prog);
|
||||||
let ctx = Context::create();
|
assert_eq!(run(prog).unwrap(), expected);
|
||||||
let jit = JITContext::new(&ctx);
|
|
||||||
assert_eq!(run(prog, jit).unwrap(), expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! map {
|
macro_rules! map {
|
||||||
@@ -77,7 +74,7 @@ macro_rules! symbol {
|
|||||||
|
|
||||||
macro_rules! list {
|
macro_rules! list {
|
||||||
($($x:tt)*) => (
|
($($x:tt)*) => (
|
||||||
Value::List(List::new(vector_sync![$($x)*]))
|
Value::List(List::new(vec![$($x)*]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,9 +222,10 @@ fn test_fib() {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_fib(b: &mut Bencher) {
|
fn bench_fib(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
black_box(test_expr(
|
test_expr(
|
||||||
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 20",
|
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
|
||||||
int!(6765),
|
int!(832040),
|
||||||
))
|
);
|
||||||
|
black_box(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user