diff --git a/Cargo.lock b/Cargo.lock index 7cc6b9c..2cf4c7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bytes" @@ -78,6 +78,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + [[package]] name = "convert_case" version = "0.7.1" @@ -161,6 +176,39 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "gimli" version = "0.29.0" @@ -179,6 +227,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "itertools" version = "0.12.1" @@ -190,9 +247,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lock_api" @@ -204,6 +267,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + [[package]] name = "memchr" version = "2.7.4" @@ -239,6 +308,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nixjit" version = "0.0.0" @@ -252,6 +342,7 @@ dependencies = [ "rayon", "rnix", "rpds", + "rustyline", "tokio", ] @@ -300,7 +391,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -327,6 +418,16 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rayon" version = "1.10.0" @@ -399,6 +500,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustyline" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -501,12 +637,24 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -528,7 +676,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -548,18 +705,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -570,9 +727,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -582,9 +739,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -594,15 +751,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -612,9 +769,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -624,9 +781,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -636,9 +793,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -648,6 +805,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index 6990ac6..db05ba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,10 @@ name = "nixjit" version = "0.0.0" edition = "2024" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.perf] +opt-level = 3 +debug = 1 +inherits = "dev" [dependencies] rnix = "0.11" @@ -16,3 +19,5 @@ derive_more = { version = "2.0", features = [ "full" ] } crossbeam-channel = "0.5" ecow = "0.2" once_cell = "1.19" + +rustyline = "15.0" diff --git a/src/bin/repl.rs b/src/bin/repl.rs new file mode 100644 index 0000000..81baea6 --- /dev/null +++ b/src/bin/repl.rs @@ -0,0 +1,34 @@ +use rustyline::error::ReadlineError; +use rustyline::{DefaultEditor, Result}; + +use nixjit::*; + +fn main() -> Result<()> { + let mut rl = DefaultEditor::new()?; + loop { + let readline = rl.readline(">> "); + match readline { + Ok(expr) => { + if expr.trim().is_empty() { + continue; + } + let prog = compile(expr.as_str()).unwrap(); + println!("{}", run(prog).unwrap()); + rl.add_history_entry(expr.as_str())?; + } + Err(ReadlineError::Interrupted) => { + println!("CTRL-C"); + break; + } + Err(ReadlineError::Eof) => { + println!("CTRL-D"); + break; + } + Err(err) => { + println!("Error: {:?}", err); + break; + } + } + } + Ok(()) +} diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 344a6c7..6dc6a82 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,33 +1,45 @@ -use crate::vm::Env; -use crate::ty::internal::{Const, Value, AttrSet, PrimOp}; -use crate::ty::common::Symbol; +use std::sync::Arc; -pub fn env() -> Env { - let mut env = Env::empty(); +use crate::ty::common::Symbol; +use crate::ty::internal::{AttrSet, Const, PrimOp, Value}; +use crate::vm::Env; + +pub fn env() -> Arc { + let env = Arc::new(Env::empty()); env.insert(Symbol::from("true"), Value::Const(Const::Bool(true))); env.insert(Symbol::from("false"), Value::Const(Const::Bool(false))); let primops = [ - PrimOp::new("add", 2, |args| { + PrimOp::new("add", 2, |_, args| { let [first, second]: [Value; 2] = args.try_into().unwrap(); first.add(second) }), - PrimOp::new("sub", 2, |args| { + PrimOp::new("sub", 2, |_, args| { let [first, second]: [Value; 2] = args.try_into().unwrap(); first.add(second.neg()) }), - PrimOp::new("mul", 2, |args| { + PrimOp::new("mul", 2, |_, args| { let [first, second]: [Value; 2] = args.try_into().unwrap(); first.mul(second) }), - PrimOp::new("div", 2, |args| { + PrimOp::new("div", 2, |_, args| { let [first, second]: [Value; 2] = args.try_into().unwrap(); first.div(second) }), - PrimOp::new("lessThan", 2, |args| { + PrimOp::new("lessThan", 2, |_, args| { let [first, second]: [Value; 2] = args.try_into().unwrap(); first.lt(second) - }) + }), + PrimOp::new("seq", 2, |vm, args| { + let [mut first, second]: [Value; 2] = args.try_into().unwrap(); + first.force(vm).unwrap(); + second + }), + PrimOp::new("deepSeq", 2, |vm, args| { + let [mut first, second]: [Value; 2] = args.try_into().unwrap(); + first.force_deep(vm).unwrap(); + second + }), ]; let mut builtins = AttrSet::empty(); @@ -42,4 +54,3 @@ pub fn env() -> Env { env.insert(Symbol::from("builtins"), Value::AttrSet(builtins)); env } - diff --git a/src/bytecode.rs b/src/bytecode.rs index 7371145..2592755 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,3 +1,5 @@ +use std::fmt::{Debug, Formatter, Result}; + use ecow::EcoString; use crate::ty::internal::Const; @@ -57,6 +59,8 @@ pub enum OpCode { /// push an empty attribute set onto stack AttrSet, + /// push an empty recursive attribute set onto stack + RecAttrSet, /// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set` PushStaticAttr { name: EcoString }, /// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set` @@ -87,15 +91,14 @@ pub enum OpCode { /// [ .. set sym ] select `sym` from `set` SelectDynamic, /// [ .. set sym default ] select `sym` from `set` or `default` - SelectDynamicOrDefault, /// enter the environment of the attribute set at TOS EnterEnv, /// exit current envrironment LeaveEnv, - /// no operation, used as termporary placeholder - NoOp, + /// illegal operation, used as termporary placeholder + Illegal, } #[derive(Debug, Clone, Copy)] diff --git a/src/compile/compile.rs b/src/compile/compile.rs index a147f73..e731bf9 100644 --- a/src/compile/compile.rs +++ b/src/compile/compile.rs @@ -94,28 +94,11 @@ impl Compile for ir::Thunk { impl Compile for ir::Attrs { fn compile(self, comp: &mut Compiler) { - if self.rec { - comp.push(OpCode::AttrSet); - for dynamic in self.dyns.clone() { - dynamic.0.compile(comp); - dynamic.1.compile(comp); - comp.push(OpCode::PushDynamicAttr) - } - comp.push(OpCode::EnterEnv); - comp.push(OpCode::AttrSet); - for stc in self.stcs { - stc.1.compile(comp); - comp.push(OpCode::PushStaticAttr { name: stc.0 }); - } - for dynamic in self.dyns { - dynamic.0.compile(comp); - dynamic.1.compile(comp); - comp.push(OpCode::PushDynamicAttr) - } - comp.push(OpCode::LeaveEnv); - return - } - comp.push(OpCode::AttrSet); + comp.push(if self.rec { + OpCode::RecAttrSet + } else { + OpCode::AttrSet + }); for stc in self.stcs { stc.1.compile(comp); comp.push(OpCode::PushStaticAttr { name: stc.0 }); @@ -125,6 +108,9 @@ impl Compile for ir::Attrs { dynamic.1.compile(comp); comp.push(OpCode::PushDynamicAttr) } + if self.rec { + comp.push(OpCode::LeaveEnv); + } } } @@ -277,7 +263,7 @@ impl Compile for ir::HasAttr { ir::Attr::Str(sym) => { comp.push(OpCode::AttrSet); comp.push(OpCode::SelectOrDefault { sym }) - }, + } ir::Attr::Dynamic(dynamic) => { dynamic.compile(comp); comp.push(OpCode::AttrSet); @@ -297,7 +283,6 @@ impl Compile for ir::HasAttr { OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr), _ => unreachable!(), } - } } @@ -309,7 +294,7 @@ impl Compile for ir::Select { ir::Attr::Str(sym) => { comp.push(OpCode::AttrSet); comp.push(OpCode::SelectOrDefault { sym }) - }, + } ir::Attr::Dynamic(dynamic) => { dynamic.compile(comp); comp.push(OpCode::AttrSet); @@ -363,17 +348,22 @@ impl Compile for ir::If { let idx_jmp_if_false = comp.idx(); // place holder - comp.push(OpCode::NoOp); + comp.push(OpCode::Illegal); let consq_length = self.consq.compile_with_length(comp); let idx_jmp = comp.idx(); // place holder - comp.push(OpCode::NoOp); + comp.push(OpCode::Illegal); let alter_length = self.alter.compile_with_length(comp); - comp.modify(idx_jmp_if_false, OpCode::JmpIfFalse { step: consq_length }); + comp.modify( + idx_jmp_if_false, + OpCode::JmpIfFalse { + step: consq_length + 1, + }, + ); comp.modify(idx_jmp, OpCode::Jmp { step: alter_length }); } } @@ -410,7 +400,11 @@ impl Compile for ir::Func { use ir::Param::*; match self.param { Ident(sym) => comp.push(OpCode::PushIdentParam { sym }), - Formals { formals, ellipsis, alias } => { + Formals { + formals, + ellipsis, + alias, + } => { for (sym, default) in formals { comp.push(OpCode::PushFormalParam { sym }); if let Some(ir::Thunk { idx }) = default { @@ -420,7 +414,7 @@ impl Compile for ir::Func { if ellipsis { comp.push(OpCode::SetEllipsis); } - if let Some(sym) = alias { + if let Some(sym) = alias { comp.push(OpCode::SetAlias { sym }); } } diff --git a/src/compile/env.rs b/src/compile/env.rs deleted file mode 100644 index 5cef2bf..0000000 --- a/src/compile/env.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::collections::HashMap; - -use crate::bytecode::SymIdx; - -use super::ir::Ir; - -pub struct IrEnv { - pub stcs: HashMap, - pub dyns: Vec<(Ir, Ir)>, -} - -impl IrEnv { - pub fn new() -> IrEnv { - IrEnv { - stcs: HashMap::new(), - dyns: Vec::new(), - } - } -} diff --git a/src/compile/ir.rs b/src/compile/ir.rs index 0d353bb..7ddbe18 100644 --- a/src/compile/ir.rs +++ b/src/compile/ir.rs @@ -8,7 +8,6 @@ use crate::bytecode::{ConstIdx, Consts, ThunkIdx}; use crate::ty::internal as i; use super::compile::*; -use super::env::IrEnv; pub fn downgrade(expr: Expr) -> Result { let mut state = DowngradeState::new(); @@ -133,27 +132,6 @@ ir! { #[derive(Clone, Debug)] pub struct DynamicAttrPair(pub Ir, pub Ir); -enum Env { - Env(IrEnv), - With, -} - -impl Env { - fn env(&self) -> &IrEnv { - match self { - Env::Env(env) => env, - _ => panic!(), - } - } - - fn env_mut(&mut self) -> &mut IrEnv { - match self { - Env::Env(env) => env, - _ => panic!(), - } - } -} - #[derive(Debug)] pub struct DowngradeError { errno: u16, @@ -161,7 +139,6 @@ pub struct DowngradeError { } pub struct DowngradeState { - envs: Vec, thunks: Vec, consts: Vec, consts_table: HashMap, @@ -176,7 +153,6 @@ pub struct Downgraded { impl DowngradeState { fn new() -> DowngradeState { DowngradeState { - envs: Vec::new(), thunks: Vec::new(), consts: Vec::new(), consts_table: HashMap::new(), @@ -779,5 +755,5 @@ fn downgrade_attrpathvalue( ) -> Result<()> { let path = downgrade_attrpath(value.attrpath().unwrap(), state)?; let value = value.value().unwrap().downgrade(state)?; - attrs.insert(path, value) + attrs.insert(path, state.new_thunk(value).ir()) } diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 221cca4..9388a3e 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -1,5 +1,4 @@ mod compile; -mod env; mod ir; pub fn compile(expr: &str) -> anyhow::Result { diff --git a/src/lib.rs b/src/lib.rs index 844fb06..15cbb75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![feature(result_flattening)] mod builtins; mod bytecode; @@ -6,3 +7,7 @@ mod compile; mod downcast; mod ty; mod vm; + +pub use compile::compile; +pub use ty::public::Value; +pub use vm::run; diff --git a/src/ty/common.rs b/src/ty/common.rs index 7d17366..d66d839 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -1,13 +1,24 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; use std::ops::Deref; -use ecow::EcoString; use derive_more::Constructor; +use ecow::EcoString; #[derive(Clone, Debug, PartialEq, Constructor)] pub struct Catchable { msg: Option, } +impl Display for Catchable { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(ref msg) = self.msg { + write!(f, "") + } else { + write!(f, "") + } + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] pub struct Symbol(EcoString); @@ -17,6 +28,12 @@ impl> From for Symbol { } } +impl Display for Symbol { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, r#"Sym({})"#, self.0) + } +} + impl Deref for Symbol { type Target = str; fn deref(&self) -> &Self::Target { diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index b3e603b..faf255c 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -1,11 +1,15 @@ +use std::cell::RefCell; +use std::sync::Arc; + +use anyhow::Result; use derive_more::Constructor; use rpds::HashTrieMapSync; -use super::super::public as p; use super::super::common::Symbol; +use super::super::public as p; -use crate::vm::VM; use super::{ToPublic, Value}; +use crate::vm::VM; #[derive(Debug, Constructor, Clone, PartialEq)] pub struct AttrSet { @@ -14,7 +18,9 @@ pub struct AttrSet { impl AttrSet { pub fn empty() -> AttrSet { - AttrSet { data: HashTrieMapSync::new_sync() } + AttrSet { + data: HashTrieMapSync::new_sync(), + } } pub fn push_attr_force(&mut self, sym: Symbol, val: Value) { @@ -43,6 +49,13 @@ impl AttrSet { self } + pub fn update_rec(mut self, other: RecAttrSet) -> AttrSet { + for (k, v) in other.data.borrow().iter() { + self.push_attr_force(k.clone(), v.clone()) + } + self + } + pub fn into_inner(self) -> HashTrieMapSync { self.data } @@ -50,6 +63,20 @@ impl AttrSet { pub fn as_inner(&self) -> &HashTrieMapSync { &self.data } + + pub fn force_deep(&mut self, vm: &VM) -> Result<()> { + let mut map: Vec<_> = self + .data + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + map.iter_mut() + .map(|(_, v)| v.force_deep(vm).map(|_| ())) + .find(|v| matches!(v, Err(_))) + .map_or(Ok(()), |err| err)?; + self.data = map.into_iter().collect(); + Ok(()) + } } impl ToPublic for AttrSet { @@ -57,12 +84,91 @@ impl ToPublic for AttrSet { p::Value::AttrSet(p::AttrSet::new( self.data .iter() - .map(|(sym, value)| { - ( - sym.clone(), - value.clone().to_public(vm), - ) - }) + .map(|(sym, value)| (sym.clone(), value.clone().to_public(vm))) + .collect(), + )) + } +} + +#[derive(Debug, Constructor, Clone, PartialEq)] +pub struct RecAttrSet { + data: Arc>>, +} + +impl RecAttrSet { + pub fn empty() -> RecAttrSet { + RecAttrSet { + data: Arc::default(), + } + } + + pub fn push_attr_force(&mut self, sym: Symbol, val: Value) { + self.data.borrow_mut().insert_mut(sym, val); + } + + pub fn push_attr(&mut self, sym: Symbol, val: Value) { + if self.data.borrow().get(&sym).is_some() { + todo!() + } + self.data.borrow_mut().insert_mut(sym, val); + } + + pub fn select(&self, sym: Symbol) -> Option { + self.data.borrow().get(&sym).cloned() + } + + pub fn has_attr(&self, sym: Symbol) -> bool { + self.data.borrow().get(&sym).is_some() + } + + pub fn update(mut self, other: RecAttrSet) -> RecAttrSet { + for (k, v) in other.data.borrow().iter() { + self.push_attr_force(k.clone(), v.clone()) + } + self + } + + pub fn update_normal(self, other: AttrSet) -> AttrSet { + let map = self + .data + .borrow() + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + let mut new = AttrSet::new(map); + for (k, v) in other.data.iter() { + new.push_attr_force(k.clone(), v.clone()) + } + new + } + + pub fn into_inner(self) -> HashTrieMapSync { + self.data.borrow().clone() + } + + pub fn force_deep(&mut self, vm: &VM) -> Result<()> { + let mut map: Vec<_> = self + .data + .borrow() + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + map.iter_mut() + .map(|(_, v)| v.force_deep(vm).map(|_| ())) + .find(|v| matches!(v, Err(_))) + .map_or(Ok(()), |err| err)?; + *self.data.borrow_mut() = map.into_iter().collect(); + Ok(()) + } +} + +impl ToPublic for RecAttrSet { + fn to_public(self, vm: &VM) -> p::Value { + p::Value::AttrSet(p::AttrSet::new( + self.data + .borrow() + .iter() + .map(|(sym, value)| (sym.clone(), value.clone().to_public(vm))) .collect(), )) } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 44d0424..b5c2a26 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -5,7 +5,7 @@ use rpds::HashTrieMap; use crate::bytecode::{OpCodes, ThunkIdx}; use crate::ty::internal::{Thunk, Value}; -use crate::vm::{LockedEnv, VM}; +use crate::vm::{CapturedEnv, VM}; #[derive(Debug, Clone)] pub enum Param { @@ -19,13 +19,13 @@ pub enum Param { #[derive(Debug, Clone)] pub struct Func { - pub env: LockedEnv, + pub env: CapturedEnv, pub param: Option, pub opcodes: OpCodes, } impl Func { - pub fn new(env: LockedEnv, opcodes: OpCodes) -> Func { + pub fn new(env: CapturedEnv, opcodes: OpCodes) -> Func { Func { env, opcodes, @@ -36,7 +36,7 @@ impl Func { pub fn call(self, vm: &VM, arg: Value) -> Value { use Param::*; - let mut env = self.env.unlocked(); + let env = self.env.released(); match self.param.unwrap() { Ident(ident) => env.enter(HashTrieMap::new_sync().insert(ident.into(), arg)), @@ -58,9 +58,6 @@ impl Func { todo!() } for (formal, default) in formals { - // let arg = if let Some(default) = default { - // arg.select(format) - // } let arg = arg .select(formal.clone().into()) .or_else(|| default.map(|idx| Value::Thunk(Thunk(idx)))) @@ -74,7 +71,7 @@ impl Func { } } - vm.eval(self.opcodes, &mut env).unwrap() + vm.eval(self.opcodes, env).unwrap() } pub fn push_ident_param(&mut self, param: EcoString) { diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index 91582d2..6cb5e9c 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use derive_more::Constructor; use rpds::VectorSync; use crate::ty::public as p; -use crate::vm::VM; use super::{ToPublic, Value}; +use crate::vm::VM; #[derive(Debug, Constructor, Clone, PartialEq)] pub struct List { @@ -14,7 +15,7 @@ pub struct List { impl List { pub fn empty() -> List { List { - data: VectorSync::new_sync() + data: VectorSync::new_sync(), } } @@ -28,6 +29,16 @@ impl List { } self } + + pub fn force_deep(&mut self, vm: &VM) -> Result<()> { + let mut vec: Vec<_> = self.data.iter().cloned().collect(); + vec.iter_mut() + .map(|v| v.force_deep(vm).map(|_| ())) + .find(|v| matches!(v, Err(_))) + .map_or(Ok(()), |err| err)?; + self.data = vec.into_iter().collect(); + Ok(()) + } } impl ToPublic for List { diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 1c2ec15..c4d5605 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,27 +1,26 @@ use anyhow::Result; use derive_more::{Constructor, IsVariant, Unwrap}; -use super::public as p; use super::common as c; +use super::public as p; use c::Symbol; -use crate::vm::Env; use crate::vm::VM; mod attrset; -mod list; -mod string; -mod func; mod cnst; +mod func; +mod list; mod primop; +mod string; -pub use attrset::AttrSet; -pub use list::List; -pub use string::ContextfulString; -pub use func::*; -pub use primop::*; +pub use attrset::*; pub use cnst::Const; +pub use func::*; +pub use list::List; +pub use primop::*; +pub use string::ContextfulString; pub trait ToPublic { fn to_public(self, vm: &VM) -> p::Value; @@ -35,12 +34,12 @@ pub enum Value { Const(Const), Thunk(Thunk), AttrSet(AttrSet), + RecAttrSet(RecAttrSet), List(List), Catchable(c::Catchable), PrimOp(PrimOp), PartialPrimOp(PartialPrimOp), Func(Func), - // FuncWithEnv(Func) } #[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] @@ -48,6 +47,7 @@ pub enum ValueAsRef<'a> { Const(&'a Const), Thunk(&'a Thunk), AttrSet(&'a AttrSet), + RecAttrSet(&'a RecAttrSet), List(&'a List), Catchable(&'a c::Catchable), PrimOp(&'a PrimOp), @@ -60,6 +60,7 @@ pub enum ValueAsMut<'a> { Const(&'a mut Const), Thunk(&'a mut Thunk), AttrSet(&'a mut AttrSet), + RecAttrSet(&'a mut RecAttrSet), List(&'a mut List), Catchable(&'a mut c::Catchable), PrimOp(&'a mut PrimOp), @@ -75,6 +76,7 @@ impl Value { Const(x) => R::Const(x), Thunk(x) => R::Thunk(x), AttrSet(x) => R::AttrSet(x), + RecAttrSet(x) => R::RecAttrSet(x), List(x) => R::List(x), Catchable(x) => R::Catchable(x), PrimOp(x) => R::PrimOp(x), @@ -90,6 +92,7 @@ impl Value { Const(x) => M::Const(x), Thunk(x) => M::Thunk(x), AttrSet(x) => M::AttrSet(x), + RecAttrSet(x) => M::RecAttrSet(x), List(x) => M::List(x), Catchable(x) => M::Catchable(x), PrimOp(x) => M::PrimOp(x), @@ -104,7 +107,7 @@ impl Value { pub fn callable(&self) -> bool { match self { Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, - Value::AttrSet(_) => todo!(), + Value::AttrSet(_) | Value::RecAttrSet(_) => todo!(), _ => false, } } @@ -112,16 +115,20 @@ impl Value { pub fn call(self, vm: &VM, args: Vec) -> Value { use Value::*; match self { - PrimOp(func) => func.call(args), - PartialPrimOp(func) => func.call(args), + PrimOp(func) => func.call(vm, args), + PartialPrimOp(func) => func.call(vm, args), mut func @ Value::Func(_) => { let mut iter = args.into_iter(); while let Some(arg) = iter.next() { func = match func { - PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), - PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), + PrimOp(func) => { + return func.call(vm, [arg].into_iter().chain(iter).collect()); + } + PartialPrimOp(func) => { + return func.call(vm, [arg].into_iter().chain(iter).collect()); + } Func(func) => func.call(vm, arg), - _ => todo!() + _ => todo!(), } } func @@ -251,6 +258,8 @@ impl Value { pub fn push_attr(&mut self, sym: Symbol, val: Value) -> &mut Self { if let Value::AttrSet(attrs) = self { attrs.push_attr(sym, val) + } else if let Value::RecAttrSet(attrs) = self { + attrs.push_attr(sym, val) } else { todo!() } @@ -258,10 +267,11 @@ impl Value { } pub fn update(self, other: Value) -> Value { - if let (Value::AttrSet(a), Value::AttrSet(b)) = (self, other) { - Value::AttrSet(a.update(b)) - } else { - todo!() + match (self, other) { + (Value::AttrSet(a), Value::AttrSet(b)) => Value::AttrSet(a.update(b)), + (Value::RecAttrSet(a), Value::AttrSet(b)) => Value::AttrSet(a.update_normal(b)), + (Value::AttrSet(a), Value::RecAttrSet(b)) => Value::AttrSet(a.update_rec(b)), + _ => todo!(), } } @@ -273,6 +283,13 @@ impl Value { "{sym:?} not found" ))))); *self = val; + } else if let Value::RecAttrSet(attrs) = self { + let val = attrs + .select(sym.clone()) + .unwrap_or(Value::Catchable(c::Catchable::new(Some(format!( + "{sym:?} not found" + ))))); + *self = val; } else { todo!() } @@ -283,6 +300,9 @@ impl Value { if let Value::AttrSet(attrs) = self { let val = attrs.select(sym).unwrap_or(default); *self = val; + } else if let Value::RecAttrSet(attrs) = self { + let val = attrs.select(sym).unwrap_or(default); + *self = val; } else { todo!() } @@ -293,6 +313,9 @@ impl Value { if let Value::AttrSet(attrs) = self { let val = VmConst(Const::Bool(attrs.has_attr(sym))); *self = val; + } else if let Value::RecAttrSet(attrs) = self { + let val = VmConst(Const::Bool(attrs.has_attr(sym))); + *self = val; } else { *self = VmConst(Const::Bool(false)); } @@ -308,19 +331,35 @@ impl Value { self } - pub fn force(&mut self, vm: &VM, env: &mut Env) -> Result<&mut Self> { + pub fn force(&mut self, vm: &VM) -> Result<&mut Self> { if let Value::Thunk(thunk) = self { - let value = vm.get_thunk_value(thunk.0, env)?; + let value = vm.get_thunk_value(thunk.0)?; *self = value } Ok(self) } + + pub fn force_deep(&mut self, vm: &VM) -> Result<&mut Self> { + match self { + Value::Thunk(thunk) => { + let mut value = vm.get_thunk_value(thunk.0)?; + value.force_deep(vm)?; + *self = value; + } + Value::List(list) => list.force_deep(vm)?, + Value::AttrSet(attrs) => attrs.force_deep(vm)?, + Value::RecAttrSet(attrs) => attrs.force_deep(vm)?, + _ => (), + } + Ok(self) + } } impl ToPublic for Value { fn to_public(self, vm: &VM) -> p::Value { match self { Value::AttrSet(attrs) => attrs.to_public(vm), + Value::RecAttrSet(attrs) => attrs.to_public(vm), Value::List(list) => list.to_public(vm), Value::Catchable(catchable) => p::Value::Catchable(catchable), Value::Const(cnst) => p::Value::Const(cnst.into()), diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 8784022..ee45473 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -1,12 +1,14 @@ use derive_more::Constructor; +use crate::vm::VM; + use super::Value; #[derive(Debug, Clone, Constructor)] pub struct PrimOp { pub name: &'static str, arity: u8, - func: fn(Vec) -> Value, + func: fn(&VM, Vec) -> Value, } impl PartialEq for PrimOp { @@ -16,7 +18,7 @@ impl PartialEq for PrimOp { } impl PrimOp { - pub fn call(self, args: Vec) -> Value { + pub fn call(self, vm: &VM, args: Vec) -> Value { if (args.len() as u8) < self.arity { Value::PartialPrimOp(PartialPrimOp { name: self.name, @@ -25,7 +27,7 @@ impl PrimOp { func: self.func, }) } else if args.len() as u8 == self.arity { - (self.func)(args) + (self.func)(vm, args) } else { unimplemented!() } @@ -37,7 +39,7 @@ pub struct PartialPrimOp { pub name: &'static str, arity: u8, args: Vec, - func: fn(Vec) -> Value, + func: fn(&VM, Vec) -> Value, } impl PartialEq for PartialPrimOp { @@ -47,7 +49,7 @@ impl PartialEq for PartialPrimOp { } impl PartialPrimOp { - pub fn call(mut self, args: Vec) -> Value { + pub fn call(mut self, vm: &VM, args: Vec) -> Value { let len = args.len() as u8; self.args.extend(args); if len < self.arity { @@ -58,10 +60,9 @@ impl PartialPrimOp { func: self.func, }) } else if len == self.arity { - (self.func)(self.args) + (self.func)(vm, self.args) } else { unimplemented!() } } } - diff --git a/src/ty/mod.rs b/src/ty/mod.rs index c4fca9c..179e099 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -1,3 +1,3 @@ -pub mod public; -pub mod internal; pub mod common; +pub mod internal; +pub mod public; diff --git a/src/ty/public/cnst.rs b/src/ty/public/cnst.rs index 1b7044b..2c95aaa 100644 --- a/src/ty/public/cnst.rs +++ b/src/ty/public/cnst.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + use anyhow::Error; use derive_more::{IsVariant, Unwrap}; use ecow::EcoString; @@ -12,6 +14,18 @@ pub enum Const { String(EcoString), } +impl Display for Const { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use Const::*; + match self { + Bool(b) => write!(f, "{b}"), + Int(i) => write!(f, "{i}"), + Float(float) => write!(f, "{float}"), + String(s) => write!(f, "{s}"), + } + } +} + impl From for Const { fn from(value: i::Const) -> Self { use i::Const::*; diff --git a/src/ty/public/mod.rs b/src/ty/public/mod.rs index d579a3b..e5346b3 100644 --- a/src/ty/public/mod.rs +++ b/src/ty/public/mod.rs @@ -1,4 +1,4 @@ -use std::fmt::{Debug, Formatter, Result as FmtResult}; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use derive_more::{Constructor, IsVariant, Unwrap}; use rpds::{HashTrieMapSync, VectorSync}; @@ -24,11 +24,30 @@ impl Debug for AttrSet { } } +impl Display for AttrSet { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{{ ")?; + for (k, v) in self.data.iter() { + write!(f, "{k} = {v}; ")?; + } + write!(f, "}}") + } +} + #[derive(Constructor, Clone, Debug, PartialEq)] pub struct List { data: VectorSync, } +impl Display for List { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "[ ")?; + for v in self.data.iter() { + write!(f, "{v} ")?; + } + write!(f, "]") + } +} #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] pub enum Value { @@ -41,3 +60,19 @@ pub enum Value { PrimOp(&'static str), PartialPrimOp(&'static str), } + +impl Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use Value::*; + match self { + Const(x) => write!(f, "{x}"), + AttrSet(x) => write!(f, "{x}"), + List(x) => write!(f, "{x}"), + Catchable(x) => write!(f, "{x}"), + Thunk => write!(f, ""), + Func => write!(f, ""), + PrimOp(x) => write!(f, ""), + PartialPrimOp(x) => write!(f, ""), + } + } +} diff --git a/src/vm/env.rs b/src/vm/env.rs index a132f12..e515fc8 100644 --- a/src/vm/env.rs +++ b/src/vm/env.rs @@ -1,63 +1,84 @@ +use std::cell::RefCell; +use std::sync::Arc; + use rpds::HashTrieMapSync; use crate::ty::common::Symbol; use crate::ty::internal::Value; +#[derive(Debug, Default)] pub struct Env { - last: Option>, - map: HashTrieMapSync, + last: RefCell>>, + map: Arc>>, +} + +impl Clone for Env { + fn clone(&self) -> Self { + Env { + last: RefCell::new(self.last.borrow().clone().map(|e| Arc::new(e.as_ref().clone()))), + map: Arc::new(RefCell::new(self.map.borrow().clone())) + } + } } #[derive(Debug, Clone)] -pub struct LockedEnv { - map: HashTrieMapSync +pub struct CapturedEnv { + env: Arc, } impl Env { pub fn empty() -> Env { - Env { - last: None, - map: HashTrieMapSync::new_sync(), - } + Env::default() } pub fn lookup(&self, symbol: Symbol) -> Option { - self.map.get(&symbol).cloned() + self.map.borrow().get(&symbol).cloned() } - pub fn insert(&mut self, symbol: Symbol, value: Value) { - self.map.insert_mut(symbol, value); + pub fn insert(&self, symbol: Symbol, value: Value) { + self.map.borrow_mut().insert_mut(symbol, value); } - pub fn enter(&mut self, new: HashTrieMapSync) { - let mut map = self.map.clone(); + pub fn enter(&self, new: HashTrieMapSync) { + let mut map = self.map.borrow().clone(); for (k, v) in new.iter() { map.insert_mut(k.clone(), v.clone()); } - let last = std::mem::replace(self, Env { last: None, map }); - self.last = Some(Box::new(last)); + let last = Env { + last: self.last.clone(), + map: self.map.clone(), + }; + *self.last.borrow_mut() = Some(Arc::new(last)); + *self.map.borrow_mut() = map; } - pub fn leave(&mut self) { - let last = std::mem::replace(&mut self.last, None).unwrap(); - let _ = std::mem::replace(&mut self.last, last.last); - self.map = last.map; + pub fn enter_rec(&self) -> Arc>> { + let last = Env { + last: self.last.clone(), + map: self.map.clone(), + }; + *self.last.borrow_mut() = Some(Arc::new(last)); + self.map.clone() } - pub fn locked(&self) -> LockedEnv { - LockedEnv { map: self.map.clone() } + pub fn leave(&self) { + let last = std::mem::replace(&mut *self.last.borrow_mut(), None).unwrap(); + let _ = std::mem::replace(&mut *self.last.borrow_mut(), last.last.borrow().clone()); + let map = last.map.borrow().clone(); + *self.map.borrow_mut() = map; + } + + pub fn captured(self: Arc) -> CapturedEnv { + CapturedEnv { env: self } } } -impl LockedEnv { +impl CapturedEnv { pub fn lookup(&self, symbol: Symbol) -> Option { - self.map.get(&symbol).cloned() + self.env.lookup(symbol) } - pub fn unlocked(self) -> Env { - Env { - map: self.map, - last: None - } + pub fn released(self) -> Arc { + Arc::new(self.env.as_ref().clone()) } } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index f79ba37..d667f5b 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -6,5 +6,6 @@ mod vmthunk; #[cfg(test)] mod test; -pub use env::{Env, LockedEnv}; +pub use env::{Env, CapturedEnv}; pub use vm::VM; +pub use vm::run; diff --git a/src/vm/test.rs b/src/vm/test.rs index aedc548..2e5bce4 100644 --- a/src/vm/test.rs +++ b/src/vm/test.rs @@ -2,8 +2,8 @@ use ecow::EcoString; use rpds::{ht_map_sync, vector_sync}; use crate::compile::compile; -use crate::ty::public::*; use crate::ty::common::Symbol; +use crate::ty::public::*; use super::vm::run; @@ -14,6 +14,12 @@ fn test_expr(expr: &str, expected: Value) { assert_eq!(run(prog).unwrap(), expected); } +macro_rules! thunk { + () => { + Value::Thunk + }; +} + macro_rules! int { ($e:expr) => { Value::Const(Const::Int($e)) @@ -125,7 +131,7 @@ fn test_attrs() { test_expr( "{ a = 1; }", attrs! { - symbol!("a") => int!(1) + symbol!("a") => thunk!() }, ); test_expr("{ a = 1; }.a", int!(1)); @@ -133,18 +139,18 @@ fn test_attrs() { test_expr( "{ a = { a = 1; }; }.a", attrs! { - symbol!("a") => int!(1) + symbol!("a") => thunk!() }, ); 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) } }, + attrs! { symbol!("a") => attrs!{ symbol!("b") => thunk!(), symbol!("c") => thunk!() } }, ); 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) } }, + attrs! { symbol!("a") => attrs!{ symbol!("c") => thunk!() } }, ); } @@ -161,10 +167,11 @@ fn test_with() { #[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) } }, + r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#, + attrs! { symbol!("a") => attrs!{ symbol!("ac") => thunk!() } }, ); } @@ -175,5 +182,8 @@ fn test_func() { 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_expr( + "(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }", + int!(3), + ); } diff --git a/src/vm/vm.rs b/src/vm/vm.rs index d4c25f6..9a2463e 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use anyhow::{Result, anyhow}; use crate::builtins::env; @@ -12,7 +14,7 @@ use super::vmthunk::*; pub fn run(prog: Program) -> Result { let vm = VM::new(prog.thunks); - Ok(vm.eval(prog.top_level, &mut env())?.to_public(&vm)) + Ok(vm.eval(prog.top_level, env())?.to_public(&vm)) } pub struct VM { @@ -28,21 +30,25 @@ impl VM { VM { thunks } } - pub fn get_thunk_value(&self, idx: usize, env: &mut Env) -> Result { - self.thunks.get(idx).unwrap().force(self, env) + pub fn get_thunk_value(&self, idx: usize) -> Result { + self.thunks.get(idx).unwrap().force(self) } - pub fn eval(&self, opcodes: OpCodes, env: &mut Env) -> Result { + pub fn eval(&self, opcodes: OpCodes, env: Arc) -> Result { let mut stack = Stack::::new(); let mut iter = opcodes.into_iter(); while let Some(opcode) = iter.next() { - let jmp = self.single_op(opcode, &mut stack, env)?; + let jmp = self.single_op(opcode, &mut stack, env.clone())?; for _ in 0..jmp { iter.next().unwrap(); } } assert_eq!(stack.len(), 1); - stack.pop() + let mut ret = stack.pop(); + if let Ok(ref mut value) = ret { + value.force(self)?; + } + ret } #[inline] @@ -50,17 +56,20 @@ impl VM { &self, opcode: OpCode, stack: &mut Stack, - env: &mut Env, + env: Arc, ) -> Result { match opcode { - OpCode::NoOp => (), + OpCode::Illegal => return Err(anyhow!("illegal opcode")), OpCode::Const { value } => stack.push(Value::Const(value))?, - OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(idx)))?, + OpCode::LoadThunk { idx } => { + self.thunks[idx].capture(env); + stack.push(Value::Thunk(Thunk::new(idx)))? + } OpCode::LoadValue { idx } => { - stack.push(self.get_thunk_value(idx, env)?)?; + stack.push(self.get_thunk_value(idx)?)?; } OpCode::ForceValue => { - stack.tos_mut()?.force(self, env)?; + stack.tos_mut()?.force(self)?; } OpCode::Jmp { step } => return Ok(step), OpCode::JmpIfTrue { step } => { @@ -78,11 +87,15 @@ impl VM { for _ in 0..arity { args.insert(0, stack.pop()?); } - let func = stack.pop()?; + let mut func = stack.pop()?; + func.force(self)?; stack.push(func.call(self, args))?; } OpCode::Func { idx } => { - stack.push(Value::Func(Func::new(env.locked(), self.thunks[idx].unwrap_code())))?; + stack.push(Value::Func(Func::new( + env.captured(), + self.thunks[idx].unwrap_code(), + )))?; } OpCode::PushIdentParam { sym } => { stack @@ -113,15 +126,18 @@ impl VM { } OpCode::UnOp { op } => { use UnOp::*; - let value = stack.pop()?; + let mut value = stack.pop()?; + value.force(self)?; stack.push(match op { Not => value.not(), })?; } OpCode::BinOp { op } => { use BinOp::*; - let rhs = stack.pop()?; - let lhs = stack.pop()?; + let mut rhs = stack.pop()?; + let mut lhs = stack.pop()?; + lhs.force(self)?; + rhs.force(self)?; stack.push(match op { Add => lhs.add(rhs), And => lhs.and(rhs), @@ -132,7 +148,8 @@ impl VM { })?; } OpCode::ConcatString => { - let rhs = stack.pop()?; + let mut rhs = stack.pop()?; + rhs.force(self)?; stack.tos_mut()?.concat_string(rhs); } OpCode::Path => { @@ -148,6 +165,10 @@ impl VM { OpCode::AttrSet => { stack.push(Value::AttrSet(AttrSet::empty()))?; } + OpCode::RecAttrSet => { + let new = env.enter_rec(); + stack.push(Value::RecAttrSet(RecAttrSet::new(new)))?; + } OpCode::PushStaticAttr { name } => { let val = stack.pop()?; stack.tos_mut()?.push_attr(Symbol::new(name), val); @@ -160,7 +181,7 @@ impl VM { stack.tos_mut()?.push_attr(sym, val); } OpCode::Select { sym } => { - stack.tos_mut()?.select(Symbol::new(sym)).force(self, env)?; + stack.tos_mut()?.force(self)?.select(Symbol::new(sym)); } OpCode::SelectOrDefault { sym } => { let default = stack.pop()?; @@ -170,13 +191,15 @@ impl VM { } OpCode::SelectDynamic => { let mut val = stack.pop().unwrap(); + val.force(self)?; val.coerce_to_string(); let sym = val.unwrap_const().unwrap_string().into(); - stack.tos_mut()?.select(sym); + stack.tos_mut()?.force(self)?.select(sym); } OpCode::SelectDynamicOrDefault => { let default = stack.pop()?; let mut val = stack.pop().unwrap(); + val.force(self)?; val.coerce_to_string(); let sym = val.unwrap_const().unwrap_string().into(); stack.tos_mut()?.select_with_default(sym, default); @@ -196,9 +219,11 @@ impl VM { .ok_or(anyhow!(r#""{sym}" not found"#))?, )?; } - OpCode::EnterEnv => { - env.enter(stack.pop()?.unwrap_attr_set().into_inner()); - } + OpCode::EnterEnv => match stack.pop()? { + Value::AttrSet(attrs) => env.enter(attrs.into_inner()), + Value::RecAttrSet(attrs) => env.enter(attrs.into_inner()), + _ => unreachable!(), + }, OpCode::LeaveEnv => { env.leave(); } diff --git a/src/vm/vmthunk.rs b/src/vm/vmthunk.rs index dba80f1..92e9e58 100644 --- a/src/vm/vmthunk.rs +++ b/src/vm/vmthunk.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::sync::RwLock; +use std::sync::Arc; use anyhow::{Result, anyhow}; use derive_more::{IsVariant, Unwrap}; @@ -7,12 +7,12 @@ use derive_more::{IsVariant, Unwrap}; use crate::bytecode::OpCodes; use super::env::Env; -use crate::ty::internal::Value; use super::vm::VM; +use crate::ty::internal::Value; pub struct VmThunk { thunk: RefCell<_VmThunk>, - lock: RwLock<()>, + env: RefCell>>, } #[derive(IsVariant, Unwrap, Clone)] @@ -26,18 +26,20 @@ impl VmThunk { pub fn new(opcodes: OpCodes) -> VmThunk { VmThunk { thunk: RefCell::new(_VmThunk::Code(opcodes)), - lock: RwLock::new(()), + env: RefCell::new(None), } } pub fn unwrap_code(&self) -> OpCodes { - let _guard = self.lock.read().unwrap(); self.thunk.borrow().clone().unwrap_code() } - pub fn force(&self, vm: &VM, env: &mut Env) -> Result { + pub fn capture(&self, env: Arc) { + *self.env.borrow_mut() = Some(env); + } + + pub fn force(&self, vm: &VM) -> Result { { - let _guard = self.lock.read().unwrap(); match &*self.thunk.borrow() { _VmThunk::Value(value) => return Ok(value.clone()), _VmThunk::SuspendedFrom(from) => { @@ -48,13 +50,14 @@ impl VmThunk { _VmThunk::Code(_) => (), } } - let _guard = self.lock.write().unwrap(); let opcodes = std::mem::replace( &mut *self.thunk.borrow_mut(), _VmThunk::SuspendedFrom(self as *const VmThunk), ) .unwrap_code(); - let value = vm.eval(opcodes, env).unwrap(); + let value = vm + .eval(opcodes, self.env.borrow().clone().unwrap()) + .unwrap(); let _ = std::mem::replace( &mut *self.thunk.borrow_mut(), _VmThunk::Value(value.clone()), @@ -63,7 +66,6 @@ impl VmThunk { } pub fn value(&self) -> Option { - let _guard = self.lock.read(); match &*self.thunk.borrow() { _VmThunk::Value(value) => Some(value.clone()), _ => None,