chore: cleanup
This commit is contained in:
59
Cargo.lock
generated
59
Cargo.lock
generated
@@ -80,6 +80,26 @@ version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"hashbrown 0.14.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
@@ -251,6 +271,16 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
@@ -297,6 +327,7 @@ dependencies = [
|
||||
name = "nixjit"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"derive_more",
|
||||
"ecow",
|
||||
"hashbrown 0.15.3",
|
||||
@@ -315,6 +346,19 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
@@ -343,6 +387,15 @@ dependencies = [
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -447,6 +500,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
|
||||
@@ -27,6 +27,7 @@ derive_more = { version = "2.0", features = ["full"] }
|
||||
ecow = "0.2"
|
||||
regex = "1.11"
|
||||
hashbrown = "0.15"
|
||||
dashmap = "6.1"
|
||||
replace_with = "0.1"
|
||||
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
@@ -2,11 +2,10 @@ 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;
|
||||
use nixjit::eval::eval;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut args = std::env::args();
|
||||
@@ -23,9 +22,8 @@ fn main() -> Result<()> {
|
||||
));
|
||||
}
|
||||
let expr = root.tree().expr().unwrap();
|
||||
let downgraded = downgrade(expr)?;
|
||||
let prog = compile(downgraded);
|
||||
println!("{}", run(prog)?);
|
||||
let expr = downgrade(expr)?;
|
||||
println!("{}", eval(expr)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use ecow::EcoString;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::ty::common::Const;
|
||||
use crate::ir::{DowngradeContext, Ir};
|
||||
use crate::ir::{DowngradeContext, Ir, Const};
|
||||
|
||||
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<usize, Ir> {
|
||||
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<EcoString, Ir> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(ctx.new_sym("true"), ctx.new_const(Const::Bool(true)).ir());
|
||||
map.insert(ctx.new_sym("false"), ctx.new_const(Const::Bool(false)).ir());
|
||||
|
||||
map.insert("true".into(), Const::from(true).ir());
|
||||
map.insert("false".into(), Const::from(false).ir());
|
||||
map
|
||||
}
|
||||
|
||||
125
src/bytecode.rs
125
src/bytecode.rs
@@ -1,125 +0,0 @@
|
||||
use ecow::EcoString;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::ty::common::Const;
|
||||
use crate::ty::internal::Param;
|
||||
|
||||
type Slice<T> = Box<[T]>;
|
||||
|
||||
pub type OpCodes = Slice<OpCode>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum OpCode {
|
||||
/// load a constant onto stack
|
||||
Const { idx: usize },
|
||||
/// load a dynamic var onto stack
|
||||
LookUp { sym: usize },
|
||||
/// load a var from let binding onto stack
|
||||
LookUpLet { level: usize, idx: usize },
|
||||
/// load a thunk lazily onto stack
|
||||
LoadThunk { idx: usize },
|
||||
/// load a thunk value onto stack
|
||||
LoadValue { idx: usize },
|
||||
/// let TOS capture current environment
|
||||
CaptureEnv,
|
||||
/// force TOS to value
|
||||
ForceValue,
|
||||
/// TODO:
|
||||
InsertValue,
|
||||
|
||||
/// [ .. func arg ] consume 2 elements, call `func` with arg
|
||||
Call,
|
||||
/// make a function
|
||||
Func { idx: usize },
|
||||
/// load a function argument
|
||||
Arg { level: usize },
|
||||
|
||||
/// consume 1 element, assert TOS is true
|
||||
Assert,
|
||||
|
||||
/// jump forward
|
||||
Jmp { step: usize },
|
||||
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
|
||||
JmpIfFalse { step: usize },
|
||||
|
||||
/// push an empty attribute set onto stack
|
||||
AttrSet { cap: usize },
|
||||
/// finalize the recursive attribute set at TOS
|
||||
FinalizeLet,
|
||||
/// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
|
||||
PushStaticAttr { name: usize },
|
||||
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
|
||||
PushDynamicAttr,
|
||||
|
||||
/// push an empty list onto stack
|
||||
List { cap: usize },
|
||||
/// [ .. list elem ] consume 1 element, push `elem` into `list`
|
||||
PushElem,
|
||||
|
||||
/// convert the string as TOS to a path
|
||||
Path,
|
||||
|
||||
/// [ .. a b ] consume 2 elements, perform a string concatenation `a` + `b`
|
||||
ConcatString,
|
||||
/// [ .. a b ] consume 2 elements, perform a binary operation `a` `op` `b`
|
||||
BinOp { op: BinOp },
|
||||
/// [ .. a ] consume 1 element, perform a unary operation `op` `a`
|
||||
UnOp { op: UnOp },
|
||||
/// set TOS to the bool value of whether TOS contains `sym`
|
||||
HasAttr { sym: usize },
|
||||
/// [ .. set sym ] consume 2 elements, set TOS to the bool value of whether `set` contains `sym`
|
||||
HasDynamicAttr,
|
||||
/// [ .. set ] select `sym` from `set`
|
||||
Select { sym: usize },
|
||||
/// [ .. set default ] select `sym` from `set` or `default`
|
||||
SelectOrDefault { sym: usize },
|
||||
/// [ .. set sym ] select `sym` from `set`
|
||||
SelectDynamic,
|
||||
/// [ .. set sym default ] select `sym` from `set` or `default`
|
||||
SelectDynamicOrDefault,
|
||||
/// enter the with environment of the attribute set at TOS
|
||||
EnterWithEnv,
|
||||
/// exit current envrironment
|
||||
LeaveEnv,
|
||||
/// TODO:
|
||||
PopEnv,
|
||||
|
||||
/// illegal operation, used as termporary placeholder
|
||||
Illegal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BinOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
And,
|
||||
Or,
|
||||
Eq,
|
||||
Lt,
|
||||
Con,
|
||||
Upd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum UnOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Func {
|
||||
pub param: Param,
|
||||
pub opcodes: OpCodes,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Program {
|
||||
pub top_level: OpCodes,
|
||||
pub thunks: Slice<OpCodes>,
|
||||
pub funcs: Slice<Func>,
|
||||
pub symbols: Vec<EcoString>,
|
||||
pub symmap: HashMap<EcoString, usize>,
|
||||
pub consts: Box<[Const]>,
|
||||
}
|
||||
447
src/compile.rs
447
src/compile.rs
@@ -1,447 +0,0 @@
|
||||
use crate::bytecode::*;
|
||||
use crate::ir;
|
||||
|
||||
pub struct Compiler {
|
||||
opcodes: Vec<OpCode>,
|
||||
}
|
||||
|
||||
pub fn compile(downgraded: ir::Downgraded) -> Program {
|
||||
Program {
|
||||
top_level: Compiler::new().compile(downgraded.top_level),
|
||||
thunks: downgraded
|
||||
.thunks
|
||||
.into_iter()
|
||||
.map(|thunk| Compiler::new().compile(thunk))
|
||||
.collect(),
|
||||
funcs: downgraded
|
||||
.funcs
|
||||
.into_iter()
|
||||
.map(|func| Func {
|
||||
param: func.param.into(),
|
||||
opcodes: Compiler::new().compile(*func.body),
|
||||
})
|
||||
.collect(),
|
||||
symbols: downgraded.symbols,
|
||||
symmap: downgraded.symmap,
|
||||
consts: downgraded.consts,
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
opcodes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compile(mut self, ir: ir::Ir) -> OpCodes {
|
||||
ir.compile_force(&mut self);
|
||||
self.opcodes()
|
||||
}
|
||||
|
||||
fn push(&mut self, code: OpCode) {
|
||||
self.opcodes.push(code);
|
||||
}
|
||||
|
||||
fn idx(&self) -> usize {
|
||||
self.opcodes.len()
|
||||
}
|
||||
|
||||
fn modify(&mut self, idx: usize, code: OpCode) {
|
||||
self.opcodes[idx] = code;
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<OpCode> {
|
||||
self.opcodes.pop()
|
||||
}
|
||||
|
||||
fn opcodes(self) -> OpCodes {
|
||||
self.opcodes.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Compile: Sized {
|
||||
fn compile(self, comp: &mut Compiler);
|
||||
fn compile_force(self, comp: &mut Compiler) {
|
||||
self.compile(comp);
|
||||
comp.push(OpCode::ForceValue);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompileWithLength {
|
||||
fn compile_with_length(self, comp: &mut Compiler) -> usize;
|
||||
}
|
||||
|
||||
impl<T: Compile> CompileWithLength for T {
|
||||
fn compile_with_length(self, comp: &mut Compiler) -> usize {
|
||||
let start = comp.idx();
|
||||
self.compile(comp);
|
||||
let end = comp.idx();
|
||||
end - start
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Const {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::Const { idx: self.idx });
|
||||
}
|
||||
fn compile_force(self, comp: &mut Compiler) {
|
||||
self.compile(comp);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Var {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::LookUp { sym: self.sym });
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
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 {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::AttrSet {
|
||||
cap: self.stcs.len() + self.dyns.len(),
|
||||
});
|
||||
for stc in self.stcs {
|
||||
let thunk = stc.1.is_thunk();
|
||||
stc.1.compile(comp);
|
||||
if thunk {
|
||||
comp.push(OpCode::CaptureEnv);
|
||||
}
|
||||
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
||||
}
|
||||
for dynamic in self.dyns {
|
||||
let thunk = dynamic.1.is_thunk();
|
||||
dynamic.0.compile_force(comp);
|
||||
dynamic.1.compile(comp);
|
||||
if thunk {
|
||||
comp.push(OpCode::CaptureEnv);
|
||||
}
|
||||
comp.push(OpCode::PushDynamicAttr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::List {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::List {
|
||||
cap: self.items.len(),
|
||||
});
|
||||
for item in self.items {
|
||||
item.compile(comp);
|
||||
comp.push(OpCode::PushElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::UnOp {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
use ir::UnOpKind::*;
|
||||
match self.kind {
|
||||
Neg => {
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::UnOp { op: UnOp::Neg });
|
||||
}
|
||||
Not => {
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::BinOp {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
use ir::BinOpKind::*;
|
||||
match self.kind {
|
||||
Add => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Add });
|
||||
}
|
||||
Mul => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Mul });
|
||||
}
|
||||
Div => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Div });
|
||||
}
|
||||
And => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::And });
|
||||
}
|
||||
Or => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||
}
|
||||
Eq => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||
}
|
||||
Lt => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||
}
|
||||
Con => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Con });
|
||||
}
|
||||
Upd => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Upd });
|
||||
}
|
||||
|
||||
Sub => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Sub });
|
||||
}
|
||||
Impl => {
|
||||
self.lhs.compile_force(comp);
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||
}
|
||||
Neq => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
Gt => {
|
||||
self.rhs.compile_force(comp);
|
||||
self.lhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||
}
|
||||
Leq => {
|
||||
self.rhs.compile_force(comp);
|
||||
self.lhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
Geq => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Lt });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
|
||||
PipeL => {
|
||||
self.lhs.compile_force(comp);
|
||||
self.rhs.compile_force(comp);
|
||||
comp.push(OpCode::Call);
|
||||
}
|
||||
PipeR => {
|
||||
self.rhs.compile_force(comp);
|
||||
self.lhs.compile_force(comp);
|
||||
comp.push(OpCode::Call);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::HasAttr {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.lhs.compile(comp);
|
||||
for attr in self.rhs {
|
||||
comp.push(OpCode::ForceValue);
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
}
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile_force(comp);
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::HasAttr { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Select {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.expr.compile(comp);
|
||||
for attr in self.attrpath {
|
||||
comp.push(OpCode::ForceValue);
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
}
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile(comp);
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::AttrSet { cap: 0 });
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.default {
|
||||
Some(default) => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
default.compile(comp);
|
||||
comp.push(last);
|
||||
}
|
||||
None => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::Select { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamic),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn compile_force(self, comp: &mut Compiler) {
|
||||
self.compile(comp);
|
||||
comp.push(OpCode::ForceValue);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::ConcatStrings {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
let mut iter = self.parts.into_iter();
|
||||
iter.next().unwrap().compile_force(comp);
|
||||
for item in iter {
|
||||
item.compile_force(comp);
|
||||
comp.push(OpCode::ConcatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::If {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.cond.compile(comp);
|
||||
|
||||
let idx_jmp_if_false = comp.idx();
|
||||
// place holder
|
||||
comp.push(OpCode::Illegal);
|
||||
|
||||
let consq_length = self.consq.compile_with_length(comp);
|
||||
|
||||
let idx_jmp = comp.idx();
|
||||
// place holder
|
||||
comp.push(OpCode::Illegal);
|
||||
|
||||
let alter_length = self.alter.compile_with_length(comp);
|
||||
|
||||
comp.modify(
|
||||
idx_jmp_if_false,
|
||||
OpCode::JmpIfFalse {
|
||||
step: consq_length + 1,
|
||||
},
|
||||
);
|
||||
comp.modify(idx_jmp, OpCode::Jmp { step: alter_length });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Let {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::List {
|
||||
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);
|
||||
if thunk {
|
||||
comp.push(OpCode::CaptureEnv);
|
||||
}
|
||||
comp.push(OpCode::LeaveEnv);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::With {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.namespace.compile(comp);
|
||||
comp.push(OpCode::EnterWithEnv);
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::LeaveEnv);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Assert {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.assertion.compile(comp);
|
||||
comp.push(OpCode::Assert);
|
||||
self.expr.compile(comp);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::LoadFunc {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::Func { idx: self.idx });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Call {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.func.compile_force(comp);
|
||||
self.args.into_iter().for_each(|arg| {
|
||||
arg.compile(comp);
|
||||
comp.push(OpCode::Call);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Path {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::Path);
|
||||
}
|
||||
}
|
||||
29
src/engine/mod.rs
Normal file
29
src/engine/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use crate::eval::Evaluate;
|
||||
use crate::ir::{Downgraded, Ir};
|
||||
use crate::ty::public::Value;
|
||||
use crate::error::Result;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub struct Engine {
|
||||
|
||||
}
|
||||
|
||||
pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
||||
let mut engine = Engine::new();
|
||||
engine.eval(downgraded.top_level)
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn eval(&mut self, expr: Ir) -> Result<Value> {
|
||||
expr.eval(self).map(|val| val.to_public(self, &mut HashSet::new()))
|
||||
}
|
||||
}
|
||||
|
||||
229
src/engine/test.rs
Normal file
229
src/engine/test.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
#![allow(unused_macros)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::ir::downgrade;
|
||||
use crate::ty::common::Const;
|
||||
use crate::ty::public::*;
|
||||
|
||||
use super::eval;
|
||||
|
||||
#[inline]
|
||||
fn test_expr(expr: &str, expected: Value) {
|
||||
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
||||
assert_eq!(eval(downgraded).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::String(EcoString::from($e))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! symbol {
|
||||
($e:expr) => {
|
||||
Symbol::from($e.to_string())
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! list {
|
||||
($($x:tt)*) => (
|
||||
Value::List(List::new(vec![$($x)*]))
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! attrs {
|
||||
($($x:tt)*) => (
|
||||
Value::AttrSet(AttrSet::new(map!{$($x)*}))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arith() {
|
||||
test_expr("1", int!(1));
|
||||
test_expr("1.", float!(1));
|
||||
test_expr("-1", int!(-1));
|
||||
test_expr("-1.", float!(-1));
|
||||
test_expr("1 + 1", int!(2));
|
||||
test_expr("1 + 1.", float!(2));
|
||||
test_expr("1. + 1", float!(2));
|
||||
test_expr("1. + 1.", float!(2));
|
||||
test_expr("1 - 1", int!(0));
|
||||
test_expr("1 - 1.", float!(0));
|
||||
test_expr("1. - 1", float!(0));
|
||||
test_expr("1. - 1.", float!(0));
|
||||
test_expr("1 * 1", int!(1));
|
||||
test_expr("1 * 1.", float!(1));
|
||||
test_expr("1. * 1", float!(1));
|
||||
test_expr("1. * 1.", float!(1));
|
||||
test_expr("1 / 1", int!(1));
|
||||
test_expr("1 / 1.", float!(1));
|
||||
test_expr("1. / 1", float!(1));
|
||||
test_expr("1. / 1.", float!(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmp() {
|
||||
test_expr("1 < 2", boolean!(true));
|
||||
test_expr("1 < 1", boolean!(false));
|
||||
test_expr("1 > 0", boolean!(true));
|
||||
test_expr("1 > 1", boolean!(false));
|
||||
test_expr("1 <= 1", boolean!(true));
|
||||
test_expr("1 <= 0", boolean!(false));
|
||||
test_expr("1 >= 1", boolean!(true));
|
||||
test_expr("1 >= 2", boolean!(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
test_expr(r#""test""#, string!("test"));
|
||||
test_expr(r#""hello" + " world""#, string!("hello world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool() {
|
||||
test_expr("true", boolean!(true));
|
||||
test_expr("false", boolean!(false));
|
||||
test_expr("!false", boolean!(true));
|
||||
test_expr("true && false", boolean!(false));
|
||||
test_expr("true || false", boolean!(true));
|
||||
test_expr("true -> false", boolean!(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
test_expr(
|
||||
"[ 1 2 3 true ]",
|
||||
list![int!(1), int!(2), int!(3), boolean!(true)],
|
||||
);
|
||||
test_expr(
|
||||
"[ 1 2 ] ++ [ 3 4 ]",
|
||||
list![int!(1), int!(2), int!(3), int!(4)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attrs() {
|
||||
test_expr(
|
||||
"{ a = 1; }",
|
||||
attrs! {
|
||||
symbol!("a") => int!(1)
|
||||
},
|
||||
);
|
||||
test_expr("{ a = 1; }.a", int!(1));
|
||||
test_expr("{ a = 1; }.b or 1", int!(1));
|
||||
test_expr(
|
||||
"{ a = { a = 1; }; }.a",
|
||||
attrs! {
|
||||
symbol!("a") => int!(1)
|
||||
},
|
||||
);
|
||||
test_expr("{ a.b = 1; }.a.b", int!(1));
|
||||
test_expr(
|
||||
"{ a.b = 1; a.c = 2; }",
|
||||
attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } },
|
||||
);
|
||||
test_expr("{ a.b = 1; } ? a.b", boolean!(true));
|
||||
test_expr(
|
||||
"{ a.b = 1; } // { a.c = 2; }",
|
||||
attrs! { symbol!("a") => attrs!{ symbol!("c") => int!(2) } },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if() {
|
||||
test_expr("if true || false then 1 else 2", int!(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with() {
|
||||
test_expr(r#"with { a = 1; }; a"#, int!(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_let() {
|
||||
test_expr(r#"let a = 1; in a"#, int!(1));
|
||||
test_expr(r#"let a = 1; b = a; in b"#, int!(1));
|
||||
test_expr(r#"let a = { a = 1; }; b = "a"; in a.${b}"#, int!(1));
|
||||
test_expr(
|
||||
r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#,
|
||||
attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func() {
|
||||
test_expr("(x: x) 1", int!(1));
|
||||
test_expr("(x: x) (x: x) 1", int!(1));
|
||||
test_expr("(x: y: x + y) 1 1", int!(2));
|
||||
test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3));
|
||||
test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3));
|
||||
test_expr(
|
||||
"(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }",
|
||||
int!(3),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_fib() {
|
||||
test_expr(
|
||||
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
|
||||
int!(832040),
|
||||
)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fib(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
test_expr(
|
||||
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
|
||||
int!(832040),
|
||||
);
|
||||
black_box(())
|
||||
})
|
||||
}
|
||||
|
||||
129
src/eval/jit/compile.rs
Normal file
129
src/eval/jit/compile.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use crate::ir::*;
|
||||
|
||||
use super::JITContext;
|
||||
|
||||
pub trait JITCompile {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>);
|
||||
}
|
||||
|
||||
impl JITCompile for Attrs {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for List {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for HasAttr {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for BinOp {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for UnOp {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Select {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for If {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for LoadFunc {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Call {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Let {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for With {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Assert {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for ConcatStrings {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Const {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for String {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Var {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Arg {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for LetVar {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Thunk {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Path {
|
||||
fn compile<'gc>(self, ctx: &JITContext<'gc>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,11 @@ 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::eval::Engine;
|
||||
use crate::ty::internal::{Thunk, Value};
|
||||
use crate::vm::VM;
|
||||
|
||||
use super::{JITContext, JITValue, ValueTag};
|
||||
use super::{JITContext, JITValue, ValueTag, JITValueData};
|
||||
|
||||
pub struct Helpers<'ctx> {
|
||||
pub int_type: IntType<'ctx>,
|
||||
@@ -337,16 +335,8 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||
}
|
||||
}
|
||||
|
||||
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_call<'gc>(func: JITValue, arg: JITValue, engine: *mut Engine) -> JITValue {
|
||||
todo!()
|
||||
}
|
||||
|
||||
extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue {
|
||||
@@ -369,28 +359,15 @@ extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {
|
||||
|
||||
extern "C" fn helper_force<'gc>(
|
||||
thunk: JITValue,
|
||||
vm: NonNull<VM<'gc>>,
|
||||
vm: NonNull<Engine>,
|
||||
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
|
||||
todo!()
|
||||
}
|
||||
|
||||
extern "C" fn helper_new_thunk(opcodes: *const OpCodes) -> JITValue {
|
||||
Value::Thunk(Thunk::new(unsafe { opcodes.as_ref() }.unwrap())).into()
|
||||
extern "C" fn helper_new_thunk(opcodes: *const ()) -> JITValue {
|
||||
todo!()
|
||||
}
|
||||
140
src/eval/jit/mod.rs
Normal file
140
src/eval/jit/mod.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use inkwell::module::Module;
|
||||
|
||||
use crate::env::VmEnv;
|
||||
use crate::ty::internal::{Value, Thunk};
|
||||
|
||||
mod helpers;
|
||||
mod compile;
|
||||
|
||||
pub use compile::JITCompile;
|
||||
use helpers::Helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
#[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<'ctx> {
|
||||
context: &'ctx Context,
|
||||
module: Module<'ctx>,
|
||||
builder: Builder<'ctx>,
|
||||
execution_engine: ExecutionEngine<'ctx>,
|
||||
|
||||
helpers: Helpers<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> JITContext<'ctx> {
|
||||
pub fn new(context: &'ctx 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,14 @@ use inkwell::context::Context;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::compile::compile;
|
||||
use crate::ir::downgrade;
|
||||
use crate::jit::JITContext;
|
||||
use super::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);
|
||||
todo!()
|
||||
}
|
||||
|
||||
macro_rules! map {
|
||||
141
src/eval/mod.rs
Normal file
141
src/eval/mod.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use crate::ty::common::Const;
|
||||
use crate::ty::internal::Value;
|
||||
use crate::ir::{self, Downgraded};
|
||||
use crate::ty::public as p;
|
||||
use crate::error::Result;
|
||||
use crate::engine::Engine;
|
||||
|
||||
pub mod jit;
|
||||
|
||||
pub trait Evaluate {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>>;
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Attrs {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::List {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::HasAttr {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::BinOp {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::UnOp {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Select {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::If {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::LoadFunc {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Call {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Let {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::With {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Assert {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::ConcatStrings {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::String {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Const {
|
||||
fn eval<'a>(self, _: &'a Engine) -> Result<Value<'a>> {
|
||||
match self.val {
|
||||
Const::Null => Value::Null,
|
||||
Const::Int(x) => Value::Int(x),
|
||||
Const::Float(x) => Value::Float(x),
|
||||
Const::Bool(x) => Value::Bool(x),
|
||||
}.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Var {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Arg {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::LetVar {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Thunk {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Path {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(expr: Downgraded) -> Result<p::Value> {
|
||||
todo!()
|
||||
}
|
||||
@@ -7,9 +7,16 @@ use rnix::ast::HasEntry;
|
||||
use rnix::ast::{self, Expr};
|
||||
|
||||
use crate::builtins::ir_env;
|
||||
use crate::compile::*;
|
||||
use crate::error::*;
|
||||
use crate::ty::public::Symbol;
|
||||
use crate::ty::common as c;
|
||||
use crate::ty::internal::Value;
|
||||
use crate::eval::jit::{JITContext, JITCompile};
|
||||
use crate::eval::Evaluate;
|
||||
use crate::engine::Engine;
|
||||
|
||||
mod utils;
|
||||
use utils::*;
|
||||
|
||||
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
||||
let mut ctx = DowngradeContext::new();
|
||||
@@ -19,9 +26,6 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
||||
let ir = ir.resolve(&mut ctx, &env)?;
|
||||
Ok(Downgraded {
|
||||
top_level: ir,
|
||||
consts: ctx.consts.into(),
|
||||
symbols: ctx.symbols,
|
||||
symmap: ctx.symmap,
|
||||
thunks: ctx.thunks.into(),
|
||||
funcs: ctx.funcs.into(),
|
||||
})
|
||||
@@ -90,20 +94,29 @@ macro_rules! ir {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Compile for Ir {
|
||||
fn compile(self, ctx: &mut Compiler) {
|
||||
|
||||
impl JITCompile for Ir {
|
||||
fn compile(self, ctx: &JITContext) {
|
||||
match self {
|
||||
$(Ir::$ty(ir) => ir.compile(ctx),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for Ir {
|
||||
fn eval<'a>(self, engine: &'a Engine) -> Result<Value<'a>> {
|
||||
match self {
|
||||
$(Ir::$ty(ir) => ir.eval(engine),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
$(
|
||||
#[$($x)*]
|
||||
)*
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct $ty {
|
||||
pub struct $ty {
|
||||
$(
|
||||
pub $name : $elemtype,
|
||||
)*
|
||||
@@ -119,7 +132,7 @@ macro_rules! ir {
|
||||
}
|
||||
|
||||
ir! {
|
||||
Attrs => { stcs: HashMap<usize, Ir>, dyns: Vec<DynamicAttrPair> },
|
||||
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
|
||||
List => { items: Vec<Ir> },
|
||||
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
||||
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
||||
@@ -129,14 +142,13 @@ ir! {
|
||||
LoadFunc => { idx: usize },
|
||||
Call => { func: Box<Ir>, args: Vec<Ir> },
|
||||
|
||||
Let => { bindings: Vec<(usize, Ir)>, expr: Box<Ir> },
|
||||
Let => { bindings: Vec<(EcoString, Ir)>, expr: Box<Ir> },
|
||||
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
||||
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
||||
ConcatStrings => { parts: Vec<Ir> },
|
||||
#[derive(Copy)]
|
||||
Const => { idx: usize },
|
||||
#[derive(Copy)]
|
||||
Var => { sym: usize },
|
||||
Const => { val: c::Const },
|
||||
String => { val: EcoString },
|
||||
Var => { sym: EcoString },
|
||||
#[derive(Copy)]
|
||||
Arg => { level: usize },
|
||||
#[derive(Copy)]
|
||||
@@ -146,16 +158,18 @@ ir! {
|
||||
Path => { expr: Box<Ir> },
|
||||
}
|
||||
|
||||
impl<T: Into<c::Const>> From<T> for Const {
|
||||
fn from(value: T) -> Self {
|
||||
Const { val: value.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DynamicAttrPair(pub Ir, pub Ir);
|
||||
|
||||
pub struct DowngradeContext {
|
||||
thunks: Vec<Ir>,
|
||||
funcs: Vec<Func>,
|
||||
consts: Vec<c::Const>,
|
||||
constmap: HashMap<c::Const, usize>,
|
||||
symbols: Vec<EcoString>,
|
||||
symmap: HashMap<EcoString, usize>,
|
||||
}
|
||||
|
||||
struct Env<'a, 'env> {
|
||||
@@ -164,10 +178,10 @@ struct Env<'a, 'env> {
|
||||
}
|
||||
|
||||
enum EnvNode<'a> {
|
||||
Builtins(&'a HashMap<usize, Ir>),
|
||||
Let(&'a Vec<usize>),
|
||||
SingleArg(usize),
|
||||
MultiArg(HashMap<usize, Option<Ir>>, Option<usize>),
|
||||
Builtins(&'a HashMap<EcoString, Ir>),
|
||||
Let(&'a Vec<EcoString>),
|
||||
SingleArg(EcoString),
|
||||
MultiArg(HashMap<EcoString, Option<Ir>>, Option<EcoString>),
|
||||
With,
|
||||
}
|
||||
|
||||
@@ -180,28 +194,28 @@ enum LookupResult {
|
||||
}
|
||||
|
||||
impl<'a, 'env> Env<'a, 'env> {
|
||||
fn new(base: &'a HashMap<usize, Ir>) -> Self {
|
||||
fn new(base: &'a HashMap<EcoString, Ir>) -> Self {
|
||||
Self {
|
||||
env: EnvNode::Builtins(base),
|
||||
prev: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_let(&'env self, map: &'a Vec<usize>) -> Self {
|
||||
fn enter_let(&'env self, map: &'a Vec<EcoString>) -> Self {
|
||||
Self {
|
||||
env: EnvNode::Let(map),
|
||||
prev: Some(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_single_arg(&'env self, ident: usize) -> Self {
|
||||
fn enter_single_arg(&'env self, ident: EcoString) -> Self {
|
||||
Self {
|
||||
env: EnvNode::SingleArg(ident),
|
||||
prev: Some(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_multi_arg(&'env self, map: HashMap<usize, Option<Ir>>, alias: Option<usize>) -> Self {
|
||||
fn enter_multi_arg(&'env self, map: HashMap<EcoString, Option<Ir>>, alias: Option<EcoString>) -> Self {
|
||||
Self {
|
||||
env: EnvNode::MultiArg(map, alias),
|
||||
prev: Some(self),
|
||||
@@ -217,7 +231,7 @@ impl<'a, 'env> Env<'a, 'env> {
|
||||
|
||||
fn _lookup(
|
||||
&self,
|
||||
ident: usize,
|
||||
ident: &EcoString,
|
||||
mut arg_level: usize,
|
||||
mut let_level: usize,
|
||||
has_with: bool,
|
||||
@@ -226,7 +240,7 @@ impl<'a, 'env> Env<'a, 'env> {
|
||||
let mut has_with = has_with;
|
||||
match &self.env {
|
||||
Builtins(map) => {
|
||||
return if let Some(ir) = map.get(&ident) {
|
||||
return if let Some(ir) = map.get(ident) {
|
||||
Ok(LookupResult::Builtin(ir.clone()))
|
||||
} else if has_with {
|
||||
Ok(LookupResult::With)
|
||||
@@ -235,7 +249,7 @@ impl<'a, 'env> Env<'a, 'env> {
|
||||
};
|
||||
}
|
||||
Let(map) => {
|
||||
if let Ok(idx) = map.binary_search(&ident) {
|
||||
if let Ok(idx) = map.binary_search(ident) {
|
||||
return Ok(LookupResult::Let {
|
||||
level: let_level,
|
||||
idx,
|
||||
@@ -245,19 +259,19 @@ impl<'a, 'env> Env<'a, 'env> {
|
||||
}
|
||||
}
|
||||
SingleArg(arg) => {
|
||||
if *arg == ident {
|
||||
if arg == ident {
|
||||
return Ok(LookupResult::SingleArg { level: arg_level });
|
||||
} else {
|
||||
arg_level += 1;
|
||||
}
|
||||
}
|
||||
MultiArg(set, alias) => {
|
||||
if let Some(default) = set.get(&ident) {
|
||||
if let Some(default) = set.get(ident) {
|
||||
return Ok(LookupResult::MultiArg {
|
||||
level: arg_level,
|
||||
default: default.clone(),
|
||||
});
|
||||
} else if *alias == Some(ident) {
|
||||
} else if alias.as_ref() == Some(ident) {
|
||||
return Ok(LookupResult::SingleArg { level: arg_level });
|
||||
} else {
|
||||
arg_level += 1;
|
||||
@@ -270,7 +284,7 @@ impl<'a, 'env> Env<'a, 'env> {
|
||||
.map_or_else(|| unreachable!(), |x| x)
|
||||
}
|
||||
|
||||
fn lookup(&self, ident: usize) -> core::result::Result<LookupResult, ()> {
|
||||
fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
|
||||
self._lookup(ident, 0, 0, false)
|
||||
}
|
||||
}
|
||||
@@ -280,19 +294,12 @@ impl DowngradeContext {
|
||||
DowngradeContext {
|
||||
thunks: Vec::new(),
|
||||
funcs: Vec::new(),
|
||||
consts: Vec::new(),
|
||||
constmap: HashMap::new(),
|
||||
symbols: Vec::new(),
|
||||
symmap: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Downgraded {
|
||||
pub top_level: Ir,
|
||||
pub consts: Box<[c::Const]>,
|
||||
pub symbols: Vec<EcoString>,
|
||||
pub symmap: HashMap<EcoString, usize>,
|
||||
pub thunks: Box<[Ir]>,
|
||||
pub funcs: Box<[Func]>,
|
||||
}
|
||||
@@ -310,33 +317,6 @@ impl DowngradeContext {
|
||||
LoadFunc { idx }
|
||||
}
|
||||
|
||||
pub fn new_const(&mut self, cnst: c::Const) -> Const {
|
||||
if let Some(&idx) = self.constmap.get(&cnst) {
|
||||
Const { idx }
|
||||
} else {
|
||||
self.constmap.insert(cnst.clone(), self.consts.len());
|
||||
self.consts.push(cnst);
|
||||
Const {
|
||||
idx: self.consts.len() - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize {
|
||||
let sym = sym.into();
|
||||
if let Some(&idx) = self.symmap.get(&sym) {
|
||||
idx
|
||||
} else {
|
||||
self.symmap.insert(sym.clone(), self.symbols.len());
|
||||
self.symbols.push(sym);
|
||||
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(
|
||||
@@ -384,7 +364,7 @@ impl Attrs {
|
||||
.as_mut()
|
||||
.try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!(
|
||||
r#"attribute '{}' already defined"#,
|
||||
ctx.get_sym(ident)
|
||||
Symbol::from(ident)
|
||||
)))
|
||||
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
|
||||
} else {
|
||||
@@ -422,7 +402,7 @@ impl Attrs {
|
||||
if self.stcs.get(&ident).is_some() {
|
||||
return Err(Error::DowngradeError(format!(
|
||||
r#"attribute '{}' already defined"#,
|
||||
ctx.get_sym(ident)
|
||||
Symbol::from(ident)
|
||||
)));
|
||||
}
|
||||
self.stcs.insert(ident, value);
|
||||
@@ -470,7 +450,7 @@ impl Attrs {
|
||||
pub enum Attr {
|
||||
Dynamic(Ir),
|
||||
Strs(ConcatStrings),
|
||||
Str(usize),
|
||||
Str(EcoString),
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
@@ -561,12 +541,11 @@ pub struct Func {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Param {
|
||||
Ident(usize),
|
||||
Ident(EcoString),
|
||||
Formals {
|
||||
// formals: Vec<(usize, Option<Ir>)>,
|
||||
formals: Vec<(usize, Option<Thunk>)>,
|
||||
formals: Vec<(EcoString, Option<Thunk>)>,
|
||||
ellipsis: bool,
|
||||
alias: Option<usize>,
|
||||
alias: Option<EcoString>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -662,7 +641,7 @@ impl Downgrade for ast::Path {
|
||||
let parts = self
|
||||
.parts()
|
||||
.map(|part| match part {
|
||||
ast::InterpolPart::Literal(lit) => ctx.new_const(lit.to_string().into()).ir().ok(),
|
||||
ast::InterpolPart::Literal(lit) => String { val: lit.to_string().into() }.ir().ok(),
|
||||
ast::InterpolPart::Interpolation(interpol) => {
|
||||
interpol.expr().unwrap().downgrade(ctx)
|
||||
}
|
||||
@@ -698,7 +677,7 @@ impl Downgrade for ast::Str {
|
||||
.normalized_parts()
|
||||
.into_iter()
|
||||
.map(|part| match part {
|
||||
ast::InterpolPart::Literal(lit) => ctx.new_const(lit.into()).ir().ok(),
|
||||
ast::InterpolPart::Literal(lit) => String { val: lit.into() }.ir().ok(),
|
||||
ast::InterpolPart::Interpolation(interpol) => {
|
||||
interpol.expr().unwrap().downgrade(ctx)
|
||||
}
|
||||
@@ -729,11 +708,10 @@ impl ConcatStrings {
|
||||
impl Downgrade for ast::Literal {
|
||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||
match self.kind() {
|
||||
ast::LiteralKind::Integer(int) => ctx.new_const(int.value().unwrap().into()),
|
||||
ast::LiteralKind::Float(float) => ctx.new_const(float.value().unwrap().into()),
|
||||
ast::LiteralKind::Uri(uri) => ctx.new_const(uri.to_string().into()),
|
||||
ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(),
|
||||
ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(),
|
||||
ast::LiteralKind::Uri(uri) => String { val: uri.to_string().into() }.ir()
|
||||
}
|
||||
.ir()
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
@@ -744,9 +722,15 @@ impl Const {
|
||||
}
|
||||
}
|
||||
|
||||
impl String {
|
||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
||||
self.ir().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Downgrade for ast::Ident {
|
||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||
let sym = ctx.new_sym(self.ident_token().unwrap().text());
|
||||
let sym = self.ident_token().unwrap().to_string().into();
|
||||
Var { sym }.ir().ok()
|
||||
}
|
||||
}
|
||||
@@ -754,10 +738,10 @@ impl Downgrade for ast::Ident {
|
||||
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))))?
|
||||
{
|
||||
let Ok(res) = env.lookup(&self.sym) else {
|
||||
return Err(Error::DowngradeError(format!("{} not found", Symbol::from(self.sym))))
|
||||
};
|
||||
match res {
|
||||
Builtin(ir) => ir,
|
||||
Let { level, idx } => LetVar { level, idx }.ir(),
|
||||
SingleArg { level } => Arg { level }.ir(),
|
||||
@@ -793,11 +777,11 @@ impl Downgrade for ast::AttrSet {
|
||||
let bindings = attrs
|
||||
.stcs
|
||||
.into_iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
||||
.collect::<Vec<_>>();
|
||||
let stcs = bindings
|
||||
.iter()
|
||||
.map(|&(sym, _)| (sym, Var { sym }.ir()))
|
||||
.map(|(sym, _)| (sym.clone(), Var { sym: sym.clone() }.ir()))
|
||||
.collect();
|
||||
Let {
|
||||
bindings,
|
||||
@@ -971,7 +955,7 @@ impl Downgrade for ast::LegacyLet {
|
||||
let attrs = downgrade_attrs(self, ctx)?;
|
||||
Select {
|
||||
expr: attrs.ir().boxed(),
|
||||
attrpath: vec![Attr::Str(ctx.new_sym("body"))],
|
||||
attrpath: vec![Attr::Str("body".into())],
|
||||
default: None,
|
||||
}
|
||||
.ir()
|
||||
@@ -997,7 +981,7 @@ impl Downgrade for ast::LetIn {
|
||||
let ident = unwrap_ident(path.next().unwrap())?;
|
||||
if path.len() > 1 {
|
||||
let mut attrs = bindings
|
||||
.entry(ident)
|
||||
.entry(ident.clone())
|
||||
.or_insert_with(|| {
|
||||
Attrs {
|
||||
stcs: HashMap::new(),
|
||||
@@ -1010,7 +994,7 @@ impl Downgrade for ast::LetIn {
|
||||
.map_err(|_| {
|
||||
Error::DowngradeError(format!(
|
||||
r#"attribute '{}' already defined"#,
|
||||
ctx.get_sym(ident)
|
||||
Symbol::from(ident)
|
||||
))
|
||||
})?;
|
||||
while path.len() > 1 {
|
||||
@@ -1037,14 +1021,14 @@ impl Downgrade for ast::LetIn {
|
||||
}
|
||||
}
|
||||
}
|
||||
let bindings = bindings.into_iter().sorted_by_key(|(k, _)| *k).collect();
|
||||
let bindings = bindings.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)).collect();
|
||||
Let { bindings, expr }.ir().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Let {
|
||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||
let map = self.bindings.iter().map(|(sym, _)| *sym).sorted().collect();
|
||||
let map = self.bindings.iter().map(|(sym, _)| sym.clone()).sorted().collect();
|
||||
let env = env.enter_let(&map);
|
||||
let bindings = self
|
||||
.bindings
|
||||
@@ -1091,15 +1075,15 @@ 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),
|
||||
let env = match self.param.clone() {
|
||||
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,
|
||||
alias,
|
||||
),
|
||||
};
|
||||
let body = self.body.resolve(ctx, &env)?.boxed();
|
||||
@@ -1135,143 +1119,3 @@ impl Call {
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
|
||||
match param {
|
||||
ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))),
|
||||
ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result<Param> {
|
||||
let formals = pattern
|
||||
.pat_entries()
|
||||
.map(|entry| {
|
||||
let ident = ctx.new_sym(entry.ident().unwrap().to_string());
|
||||
if entry.default().is_none() {
|
||||
Ok((ident, None))
|
||||
} else {
|
||||
entry
|
||||
.default()
|
||||
.unwrap()
|
||||
.downgrade(ctx)
|
||||
.map(|ok| (ident, Some(ctx.new_thunk(ok))))
|
||||
// .map(|ok| (ident, Some(ok)))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let ellipsis = pattern.ellipsis_token().is_some();
|
||||
let alias = pattern
|
||||
.pat_bind()
|
||||
.map(|alias| ctx.new_sym(alias.ident().unwrap().to_string()));
|
||||
Ok(Param::Formals {
|
||||
formals,
|
||||
ellipsis,
|
||||
alias,
|
||||
})
|
||||
}
|
||||
|
||||
fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result<Attrs> {
|
||||
let entires = has_entry.entries();
|
||||
let mut attrs = Attrs {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
};
|
||||
|
||||
for entry in entires {
|
||||
match entry {
|
||||
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?,
|
||||
ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(attrs)
|
||||
}
|
||||
|
||||
fn downgrade_inherit(
|
||||
inherit: ast::Inherit,
|
||||
stcs: &mut HashMap<usize, Ir>,
|
||||
ctx: &mut DowngradeContext,
|
||||
) -> Result<()> {
|
||||
let from = if let Some(from) = inherit.from() {
|
||||
let from = from.expr().unwrap().downgrade(ctx)?;
|
||||
Some(ctx.new_thunk(from))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for attr in inherit.attrs() {
|
||||
let ident = match downgrade_attr(attr, ctx)? {
|
||||
Attr::Str(ident) => ident,
|
||||
_ => {
|
||||
return Err(Error::DowngradeError(
|
||||
"dynamic attributes not allowed in inherit".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let expr = from.map_or_else(
|
||||
|| Var { sym: ident }.ir().ok(),
|
||||
|from| {
|
||||
Ok(Select {
|
||||
expr: from.ir().boxed(),
|
||||
attrpath: vec![Attr::Str(ident)],
|
||||
default: None,
|
||||
}
|
||||
.ir())
|
||||
},
|
||||
)?;
|
||||
assert!(stcs.insert(ident, expr).is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Attr> {
|
||||
use ast::Attr::*;
|
||||
use ast::InterpolPart::*;
|
||||
match attr {
|
||||
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))),
|
||||
Str(string) => {
|
||||
let parts = string.normalized_parts();
|
||||
if parts.is_empty() {
|
||||
Ok(Attr::Str(ctx.new_sym("")))
|
||||
} else if parts.len() == 1 {
|
||||
match parts.into_iter().next().unwrap() {
|
||||
Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))),
|
||||
Interpolation(interpol) => {
|
||||
Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let parts = parts
|
||||
.into_iter()
|
||||
.map(|part| match part {
|
||||
Literal(lit) => ctx.new_const(lit.into()).ir().ok(),
|
||||
Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx),
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok(Attr::Strs(ConcatStrings { parts }))
|
||||
}
|
||||
}
|
||||
Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)),
|
||||
}
|
||||
}
|
||||
|
||||
fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result<Vec<Attr>> {
|
||||
attrpath
|
||||
.attrs()
|
||||
.map(|attr| downgrade_attr(attr, ctx))
|
||||
.collect::<Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
fn downgrade_attrpathvalue(
|
||||
value: ast::AttrpathValue,
|
||||
attrs: &mut Attrs,
|
||||
ctx: &mut DowngradeContext,
|
||||
) -> Result<()> {
|
||||
let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?;
|
||||
let value = value.value().unwrap().downgrade(ctx)?;
|
||||
let value = match value {
|
||||
x @ Ir::Const(_) => x,
|
||||
x => ctx.new_thunk(x).ir(),
|
||||
};
|
||||
attrs.insert(path, value, ctx)
|
||||
}
|
||||
144
src/ir/utils.rs
Normal file
144
src/ir/utils.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use rnix::ast;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
|
||||
match param {
|
||||
ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())),
|
||||
ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result<Param> {
|
||||
let formals = pattern
|
||||
.pat_entries()
|
||||
.map(|entry| {
|
||||
let ident = entry.ident().unwrap().to_string().into();
|
||||
if entry.default().is_none() {
|
||||
Ok((ident, None))
|
||||
} else {
|
||||
entry
|
||||
.default()
|
||||
.unwrap()
|
||||
.downgrade(ctx)
|
||||
.map(|ok| (ident, Some(ctx.new_thunk(ok))))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let ellipsis = pattern.ellipsis_token().is_some();
|
||||
let alias = pattern
|
||||
.pat_bind()
|
||||
.map(|alias| alias.ident().unwrap().to_string().into());
|
||||
Ok(Param::Formals {
|
||||
formals,
|
||||
ellipsis,
|
||||
alias,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result<Attrs> {
|
||||
let entires = has_entry.entries();
|
||||
let mut attrs = Attrs {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
};
|
||||
|
||||
for entry in entires {
|
||||
match entry {
|
||||
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?,
|
||||
ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(attrs)
|
||||
}
|
||||
|
||||
pub fn downgrade_inherit(
|
||||
inherit: ast::Inherit,
|
||||
stcs: &mut HashMap<EcoString, Ir>,
|
||||
ctx: &mut DowngradeContext,
|
||||
) -> Result<()> {
|
||||
let from = if let Some(from) = inherit.from() {
|
||||
let from = from.expr().unwrap().downgrade(ctx)?;
|
||||
Some(ctx.new_thunk(from))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for attr in inherit.attrs() {
|
||||
let ident = match downgrade_attr(attr, ctx)? {
|
||||
Attr::Str(ident) => ident,
|
||||
_ => {
|
||||
return Err(Error::DowngradeError(
|
||||
"dynamic attributes not allowed in inherit".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let expr = from.map_or_else(
|
||||
|| Var { sym: ident.clone() }.ir().ok(),
|
||||
|from| {
|
||||
Ok(Select {
|
||||
expr: from.ir().boxed(),
|
||||
attrpath: vec![Attr::Str(ident.clone())],
|
||||
default: None,
|
||||
}
|
||||
.ir())
|
||||
},
|
||||
)?;
|
||||
// TODO: Error Handling
|
||||
assert!(stcs.insert(ident, expr).is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Attr> {
|
||||
use ast::Attr::*;
|
||||
use ast::InterpolPart::*;
|
||||
match attr {
|
||||
Ident(ident) => Ok(Attr::Str(ident.to_string().into())),
|
||||
Str(string) => {
|
||||
let parts = string.normalized_parts();
|
||||
if parts.is_empty() {
|
||||
Ok(Attr::Str("".into()))
|
||||
} else if parts.len() == 1 {
|
||||
match parts.into_iter().next().unwrap() {
|
||||
Literal(ident) => Ok(Attr::Str(ident.into())),
|
||||
Interpolation(interpol) => {
|
||||
Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let parts = parts
|
||||
.into_iter()
|
||||
.map(|part| match part {
|
||||
Literal(lit) => String { val: lit.into() }.ir().ok(),
|
||||
Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx),
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok(Attr::Strs(ConcatStrings { parts }))
|
||||
}
|
||||
}
|
||||
Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result<Vec<Attr>> {
|
||||
attrpath
|
||||
.attrs()
|
||||
.map(|attr| downgrade_attr(attr, ctx))
|
||||
.collect::<Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
pub fn downgrade_attrpathvalue(
|
||||
value: ast::AttrpathValue,
|
||||
attrs: &mut Attrs,
|
||||
ctx: &mut DowngradeContext,
|
||||
) -> Result<()> {
|
||||
let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?;
|
||||
let value = value.value().unwrap().downgrade(ctx)?;
|
||||
let value = match value {
|
||||
x @ Ir::Const(_) => x,
|
||||
x => ctx.new_thunk(x).ir(),
|
||||
};
|
||||
attrs.insert(path, value, ctx)
|
||||
}
|
||||
468
src/jit/mod.rs
468
src/jit/mod.rs
@@ -1,468 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![allow(dead_code)]
|
||||
#![feature(iter_collect_into)]
|
||||
#![feature(arbitrary_self_types)]
|
||||
|
||||
mod builtins;
|
||||
mod bytecode;
|
||||
mod env;
|
||||
mod stack;
|
||||
mod ty;
|
||||
|
||||
pub mod compile;
|
||||
pub mod error;
|
||||
pub mod ir;
|
||||
pub mod jit;
|
||||
pub mod vm;
|
||||
|
||||
pub mod eval;
|
||||
pub mod engine;
|
||||
pub use ty::public::Value;
|
||||
|
||||
@@ -6,12 +6,12 @@ use ecow::EcoString;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Constructor, Hash)]
|
||||
pub struct Catchable {
|
||||
msg: String,
|
||||
msg: EcoString,
|
||||
}
|
||||
|
||||
impl From<String> for Catchable {
|
||||
fn from(value: String) -> Self {
|
||||
Catchable { msg: value }
|
||||
impl<T: Into<EcoString>> From<T> for Catchable {
|
||||
fn from(value: T) -> Self {
|
||||
Catchable { msg: value.into() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ pub enum Const {
|
||||
Bool(bool),
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
String(EcoString),
|
||||
Null,
|
||||
}
|
||||
|
||||
@@ -38,7 +37,6 @@ impl Hash for Const {
|
||||
Int(x) => x.hash(state),
|
||||
Float(x) => x.to_bits().hash(state),
|
||||
Bool(x) => x.hash(state),
|
||||
String(x) => x.hash(state),
|
||||
Null => (),
|
||||
}
|
||||
}
|
||||
@@ -51,7 +49,6 @@ impl Display for Const {
|
||||
Int(x) => write!(f, "{x}"),
|
||||
Float(x) => write!(f, "{x}"),
|
||||
Bool(x) => write!(f, "{x}"),
|
||||
String(x) => write!(f, "{x:?}"),
|
||||
Null => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
@@ -75,24 +72,6 @@ impl From<f64> for Const {
|
||||
}
|
||||
}
|
||||
|
||||
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::*;
|
||||
@@ -102,7 +81,6 @@ impl PartialEq for Const {
|
||||
(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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ecow::EcoString;
|
||||
use derive_more::Constructor;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::vm::VM;
|
||||
use crate::engine::Engine;
|
||||
|
||||
use super::super::public as p;
|
||||
use super::Value;
|
||||
@@ -13,17 +14,17 @@ use super::Value;
|
||||
#[repr(transparent)]
|
||||
#[derive(Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet<'gc> {
|
||||
data: HashMap<usize, Value<'gc>>,
|
||||
data: HashMap<EcoString, Value<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> From<HashMap<usize, Value<'gc>>> for AttrSet<'gc> {
|
||||
fn from(data: HashMap<usize, Value<'gc>>) -> Self {
|
||||
impl<'gc> From<HashMap<EcoString, Value<'gc>>> for AttrSet<'gc> {
|
||||
fn from(data: HashMap<EcoString, Value<'gc>>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Deref for AttrSet<'gc> {
|
||||
type Target = HashMap<usize, Value<'gc>>;
|
||||
type Target = HashMap<EcoString, Value<'gc>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
@@ -36,57 +37,57 @@ impl<'gc> AttrSet<'gc> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_attr_force(&mut self, sym: usize, val: Value<'gc>) {
|
||||
pub fn push_attr_force(&mut self, sym: EcoString, val: Value<'gc>) {
|
||||
self.data.insert(sym, val);
|
||||
}
|
||||
|
||||
pub fn push_attr(&mut self, sym: usize, val: Value<'gc>) {
|
||||
pub fn push_attr(&mut self, sym: EcoString, val: Value<'gc>) {
|
||||
if self.data.get(&sym).is_some() {
|
||||
todo!()
|
||||
}
|
||||
self.data.insert(sym, val);
|
||||
}
|
||||
|
||||
pub fn select(&self, sym: usize) -> Option<Value<'gc>> {
|
||||
self.data.get(&sym).cloned()
|
||||
pub fn select(&self, sym: &EcoString) -> Option<Value<'gc>> {
|
||||
self.data.get(sym).cloned()
|
||||
}
|
||||
|
||||
pub fn has_attr(&self, sym: usize) -> bool {
|
||||
self.data.get(&sym).is_some()
|
||||
pub fn has_attr(&self, sym: &EcoString) -> bool {
|
||||
self.data.get(sym).is_some()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, other: &AttrSet<'gc>) {
|
||||
for (k, v) in other.data.iter() {
|
||||
self.push_attr_force(*k, v.clone())
|
||||
self.push_attr_force(k.clone(), v.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_inner(&self) -> &HashMap<usize, Value<'gc>> {
|
||||
pub fn as_inner(&self) -> &HashMap<EcoString, Value<'gc>> {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<usize, Value<'gc>>> {
|
||||
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<EcoString, Value<'gc>>> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
|
||||
pub fn from_inner(data: HashMap<usize, Value<'gc>>) -> Self {
|
||||
pub fn from_inner(data: HashMap<EcoString, Value<'gc>>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool {
|
||||
self.data.iter().len() == other.data.iter().len()
|
||||
&& std::iter::zip(
|
||||
self.data.iter().sorted_by_key(|(k, _)| **k),
|
||||
self.data.iter().sorted_by_key(|(k, _)| **k),
|
||||
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
|
||||
other.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
|
||||
)
|
||||
.all(|((_, v1), (_, v2))| v1.eq_impl(v2))
|
||||
}
|
||||
|
||||
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
p::Value::AttrSet(p::AttrSet::new(
|
||||
self.data
|
||||
.iter()
|
||||
.map(|(&sym, value)| (vm.get_sym(sym), value.to_public(vm, seen)))
|
||||
.map(|(sym, value)| (sym.as_str().into(), value.to_public(engine, seen)))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,67 +1,20 @@
|
||||
use std::cell::{Cell, OnceCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::bytecode::Func as BFunc;
|
||||
use crate::env::VmEnv;
|
||||
use crate::error::Result;
|
||||
|
||||
use crate::ir;
|
||||
use crate::jit::JITFunc;
|
||||
use crate::ty::internal::Value;
|
||||
use crate::vm::VM;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Param {
|
||||
Ident(usize),
|
||||
Formals {
|
||||
formals: Vec<(usize, Option<usize>)>,
|
||||
ellipsis: bool,
|
||||
alias: Option<usize>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ir::Param> for Param {
|
||||
fn from(value: ir::Param) -> Self {
|
||||
match value {
|
||||
ir::Param::Ident(ident) => Param::Ident(ident),
|
||||
ir::Param::Formals {
|
||||
formals,
|
||||
ellipsis,
|
||||
alias,
|
||||
} => Param::Formals {
|
||||
formals: formals
|
||||
.into_iter()
|
||||
.map(|(sym, default)| (sym, default.map(|default| default.idx)))
|
||||
.collect(),
|
||||
ellipsis,
|
||||
alias,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Func<'gc> {
|
||||
pub func: &'gc BFunc,
|
||||
pub func: &'gc ir::Func,
|
||||
pub env: Rc<VmEnv<'gc>>,
|
||||
pub compiled: OnceCell<JITFunc<'gc>>,
|
||||
pub count: Cell<usize>,
|
||||
}
|
||||
|
||||
impl<'gc> Func<'gc> {
|
||||
pub fn new(func: &'gc BFunc, env: Rc<VmEnv<'gc>>) -> Self {
|
||||
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv<'gc>>) -> Self {
|
||||
Self {
|
||||
func,
|
||||
env,
|
||||
compiled: OnceCell::new(),
|
||||
count: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Func<'_> {
|
||||
|
||||
@@ -4,7 +4,7 @@ use hashbrown::HashSet;
|
||||
|
||||
use crate::env::VmEnv;
|
||||
use crate::ty::public as p;
|
||||
use crate::vm::VM;
|
||||
use crate::engine::Engine;
|
||||
|
||||
use super::Value;
|
||||
|
||||
@@ -52,11 +52,11 @@ impl<'gc> List<'gc> {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
p::Value::List(p::List::new(
|
||||
self.data
|
||||
.iter()
|
||||
.map(|value| value.clone().to_public(vm, seen))
|
||||
.map(|value| value.clone().to_public(engine, seen))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@ use std::hash::Hash;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use derive_more::{IsVariant, Unwrap};
|
||||
use ecow::EcoString;
|
||||
use hashbrown::HashSet;
|
||||
use replace_with::replace_with_or_abort;
|
||||
|
||||
use super::common::*;
|
||||
use super::public as p;
|
||||
use super::public::{self as p, Symbol};
|
||||
|
||||
use crate::bytecode::OpCodes;
|
||||
use crate::env::VmEnv;
|
||||
use crate::error::*;
|
||||
use crate::vm::VM;
|
||||
use crate::engine::Engine;
|
||||
|
||||
mod attrset;
|
||||
mod func;
|
||||
@@ -30,12 +30,12 @@ pub enum Value<'gc> {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(Rc<String>),
|
||||
String(EcoString),
|
||||
Null,
|
||||
Thunk(Thunk<'gc>),
|
||||
AttrSet(Rc<AttrSet<'gc>>),
|
||||
List(Rc<List<'gc>>),
|
||||
Catchable(Rc<String>),
|
||||
Catchable(EcoString),
|
||||
PrimOp(Rc<PrimOp>),
|
||||
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
|
||||
Func(Rc<Func<'gc>>),
|
||||
@@ -151,7 +151,7 @@ impl<'gc> Value<'gc> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&mut self, arg: Self, vm: &VM) -> Result<()> {
|
||||
pub fn call(&mut self, arg: Self, vm: &Engine) -> Result<()> {
|
||||
use Value::*;
|
||||
if matches!(arg, Value::Catchable(_)) {
|
||||
*self = arg;
|
||||
@@ -239,7 +239,7 @@ impl<'gc> Value<'gc> {
|
||||
(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);
|
||||
a.push_str(&b);
|
||||
String(a)
|
||||
}
|
||||
(a @ Value::Catchable(_), _) => a,
|
||||
@@ -282,7 +282,7 @@ impl<'gc> Value<'gc> {
|
||||
pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
|
||||
match (self.coerce_to_string(), other.coerce_to_string()) {
|
||||
(Value::String(a), Value::String(b)) => {
|
||||
Rc::make_mut(a).push_str(b.as_str());
|
||||
a.push_str(b.as_str());
|
||||
}
|
||||
(_, Value::Catchable(_)) => *self = other,
|
||||
(Value::Catchable(_), _) => (),
|
||||
@@ -317,7 +317,7 @@ impl<'gc> Value<'gc> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self {
|
||||
pub fn push_attr(&mut self, sym: EcoString, val: Self) -> &mut Self {
|
||||
if let Value::AttrSet(attrs) = self {
|
||||
Rc::make_mut(attrs).push_attr(sym, val);
|
||||
} else if let Value::Catchable(_) = self {
|
||||
@@ -343,11 +343,11 @@ impl<'gc> Value<'gc> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&mut self, sym: usize, vm: &VM<'gc>) -> Result<&mut Self> {
|
||||
pub fn select(&mut self, sym: &EcoString) -> Result<&mut Self> {
|
||||
let val = match self {
|
||||
Value::AttrSet(attrs) => attrs
|
||||
.select(sym)
|
||||
.ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym)))),
|
||||
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))),
|
||||
Value::Catchable(_) => return Ok(self),
|
||||
_ => Err(Error::EvalError(format!(
|
||||
"cannot select from {:?}",
|
||||
@@ -358,7 +358,7 @@ impl<'gc> Value<'gc> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn select_with_default(&mut self, sym: usize, default: Self) -> Result<&mut Self> {
|
||||
pub fn select_with_default(&mut self, sym: &EcoString, default: Self) -> Result<&mut Self> {
|
||||
let val = match self {
|
||||
Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default),
|
||||
Value::Catchable(_) => return Ok(self),
|
||||
@@ -373,7 +373,7 @@ impl<'gc> Value<'gc> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn has_attr(&mut self, sym: usize) -> &mut Self {
|
||||
pub fn has_attr(&mut self, sym: &EcoString) -> &mut Self {
|
||||
if let Value::AttrSet(attrs) = self {
|
||||
let val = Value::Bool(attrs.has_attr(sym));
|
||||
*self = val;
|
||||
@@ -393,7 +393,7 @@ impl<'gc> Value<'gc> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
pub fn to_public(&self, vm: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
|
||||
use self::Value::*;
|
||||
use p::Value;
|
||||
if seen.contains(self) {
|
||||
@@ -408,11 +408,11 @@ impl<'gc> Value<'gc> {
|
||||
seen.insert(self.clone());
|
||||
list.to_public(vm, seen)
|
||||
}
|
||||
Catchable(catchable) => Value::Catchable(catchable.as_ref().clone().into()),
|
||||
Catchable(catchable) => Value::Catchable(catchable.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())),
|
||||
String(x) => Value::String(x.clone()),
|
||||
Null => Value::Const(Const::Null),
|
||||
Thunk(_) => Value::Thunk,
|
||||
PrimOp(primop) => Value::PrimOp(primop.name),
|
||||
@@ -428,6 +428,9 @@ pub struct Thunk<'gc> {
|
||||
pub thunk: Rc<RefCell<_Thunk<'gc>>>,
|
||||
}
|
||||
|
||||
// TODO: impl
|
||||
type OpCodes = ();
|
||||
|
||||
#[derive(IsVariant, Unwrap)]
|
||||
pub enum _Thunk<'gc> {
|
||||
Code(&'gc OpCodes, Option<Env<'gc>>),
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::rc::Rc;
|
||||
use derive_more::Constructor;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::vm::VM;
|
||||
use crate::engine::Engine;
|
||||
|
||||
use super::Value;
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::Value;
|
||||
pub struct PrimOp {
|
||||
pub name: &'static str,
|
||||
arity: usize,
|
||||
func: for<'gc> fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
|
||||
func: for<'gc> fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
|
||||
}
|
||||
|
||||
impl PartialEq for PrimOp {
|
||||
@@ -21,7 +21,7 @@ impl PartialEq for PrimOp {
|
||||
}
|
||||
|
||||
impl PrimOp {
|
||||
pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
|
||||
pub fn call<'gc>(&self, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> {
|
||||
let mut args = Vec::with_capacity(self.arity);
|
||||
args.push(arg);
|
||||
if self.arity > 1 {
|
||||
@@ -33,7 +33,7 @@ impl PrimOp {
|
||||
}))
|
||||
.ok()
|
||||
} else {
|
||||
(self.func)(args, vm)
|
||||
(self.func)(args, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ pub struct PartialPrimOp<'gc> {
|
||||
pub name: &'static str,
|
||||
arity: usize,
|
||||
args: Vec<Value<'gc>>,
|
||||
func: fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
|
||||
func: fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
|
||||
}
|
||||
|
||||
impl PartialEq for PartialPrimOp<'_> {
|
||||
@@ -53,14 +53,14 @@ impl PartialEq for PartialPrimOp<'_> {
|
||||
}
|
||||
|
||||
impl<'gc> PartialPrimOp<'gc> {
|
||||
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
|
||||
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> {
|
||||
let func = self.func;
|
||||
let Some(ret) = ({
|
||||
let self_mut = Rc::make_mut(self);
|
||||
self_mut.args.push(arg);
|
||||
self_mut.arity -= 1;
|
||||
if self_mut.arity == 0 {
|
||||
Some(func(std::mem::take(&mut self_mut.args), vm))
|
||||
Some(func(std::mem::take(&mut self_mut.args), ctx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ impl Display for List {
|
||||
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
Const(Const),
|
||||
String(EcoString),
|
||||
AttrSet(AttrSet),
|
||||
List(List),
|
||||
Catchable(Catchable),
|
||||
@@ -123,6 +124,7 @@ impl Display for Value {
|
||||
use Value::*;
|
||||
match self {
|
||||
Const(x) => write!(f, "{x}"),
|
||||
String(x) => write!(f, "{x}"),
|
||||
AttrSet(x) => write!(f, "{x}"),
|
||||
List(x) => write!(f, "{x}"),
|
||||
Catchable(x) => write!(f, "{x}"),
|
||||
|
||||
Reference in New Issue
Block a user