feat: stash

This commit is contained in:
2025-05-10 16:29:55 +08:00
parent 14045f7924
commit f86c088e97
21 changed files with 222 additions and 219 deletions

26
Cargo.lock generated
View File

@@ -177,7 +177,7 @@ dependencies = [
"libc", "libc",
"llvm-sys", "llvm-sys",
"once_cell", "once_cell",
"thiserror", "thiserror 1.0.69",
] ]
[[package]] [[package]]
@@ -278,7 +278,6 @@ dependencies = [
name = "nixjit" name = "nixjit"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow",
"derive_more", "derive_more",
"ecow", "ecow",
"inkwell", "inkwell",
@@ -287,6 +286,7 @@ dependencies = [
"rnix", "rnix",
"rpds", "rpds",
"rustyline", "rustyline",
"thiserror 2.0.12",
] ]
[[package]] [[package]]
@@ -477,7 +477,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
] ]
[[package]] [[package]]
@@ -491,6 +500,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "triomphe" name = "triomphe"
version = "0.1.11" version = "0.1.11"

View File

@@ -3,20 +3,29 @@ name = "nixjit"
version = "0.0.0" version = "0.0.0"
edition = "2024" edition = "2024"
[features]
default = ["vm", "jit", "repl"]
vm = []
jit = ["dep:inkwell"]
repl = ["dep:rustyline"]
[[bin]]
name = "repl-vm"
required-features = ["vm", "repl"]
[profile.perf] [profile.perf]
opt-level = 3
debug = 1 debug = 1
inherits = "dev" inherits = "release"
[dependencies] [dependencies]
rnix = "0.11" rnix = "0.11"
anyhow = "1.0" thiserror = "2.0"
itertools = "0.12" itertools = "0.12"
rpds = "1.1" rpds = "1.1"
derive_more = { version = "2.0", features = [ "full" ] } derive_more = { version = "2.0", features = [ "full" ] }
ecow = "0.2" ecow = "0.2"
regex = "1.11" regex = "1.11"
inkwell = { version = "0.6.0", features = ["llvm18-1"] } inkwell = { version = "0.6.0", features = ["llvm18-1"], optional = true }
rustyline = "15.0" rustyline = { version = "15.0", optional = true }

View File

@@ -23,7 +23,23 @@
"rustfmt" "rustfmt"
"rust-analyzer" "rust-analyzer"
]) ])
llvm_18
libffi
libxml2
ncurses
]; ];
LLVM_SYS_181_PREFIX = toString pkgs.llvm_18.dev;
LD_LIBRARY_PATH = let
libs = with pkgs; [
llvm_18.lib
stdenv.cc.cc.lib
libffi
libxml2
ncurses
];
in
builtins.concatStringsSep ":" (map (lib: "${lib}/lib") libs)
;
}; };
} }
); );

View File

@@ -1,7 +1,21 @@
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{DefaultEditor, Result}; use rustyline::{DefaultEditor, Result};
use nixjit::*; use nixjit::compile::compile;
use nixjit::ir::downgrade;
use nixjit::vm::run;
macro_rules! unwrap {
($e:expr) => {
match $e {
Ok(ok) => ok,
Err(err) => {
println!("{err}");
continue;
}
}
};
}
fn main() -> Result<()> { fn main() -> Result<()> {
let mut rl = DefaultEditor::new()?; let mut rl = DefaultEditor::new()?;
@@ -12,8 +26,11 @@ fn main() -> Result<()> {
if expr.trim().is_empty() { if expr.trim().is_empty() {
continue; continue;
} }
let prog = compile(expr.as_str()).unwrap(); let downgraded = unwrap!(downgrade(
println!("{}", run(prog).unwrap()); rnix::Root::parse(expr.as_str()).tree().expr().unwrap()
));
let prog = compile(downgraded);
println!("{}", unwrap!(run(prog)));
rl.add_history_entry(expr.as_str())?; rl.add_history_entry(expr.as_str())?;
} }
Err(ReadlineError::Interrupted) => { Err(ReadlineError::Interrupted) => {

View File

@@ -1,8 +1,7 @@
use crate::bytecode::*; use crate::bytecode::*;
use crate::ir;
use crate::ty::internal::Const; use crate::ty::internal::Const;
use super::ir;
pub struct Compiler { pub struct Compiler {
opcodes: Vec<OpCode>, opcodes: Vec<OpCode>,
} }

View File

@@ -1,17 +0,0 @@
use itertools::Itertools;
mod compile;
mod ir;
pub fn compile(expr: &str) -> anyhow::Result<crate::bytecode::Program> {
let root = rnix::Root::parse(expr);
if !root.errors().is_empty() {
return Err(anyhow::anyhow!(
root.errors().iter().map(|err| err.to_string()).join(";")
));
}
assert!(root.errors().is_empty());
let expr = root.tree().expr().unwrap();
let ir = ir::downgrade(expr)?;
Ok(compile::compile(ir))
}

View File

@@ -1,8 +0,0 @@
pub trait Downcast<T: Sized>
where
Self: Sized,
{
fn downcast_ref(&self) -> Option<&T>;
fn downcast_mut(&mut self) -> Option<&mut T>;
fn downcast(self) -> Result<T, Self>;
}

13
src/error.rs Normal file
View File

@@ -0,0 +1,13 @@
use thiserror::Error;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("error occurred during downgrade stage: {0}")]
DowngradeError(String),
#[error("error occurred during evaluation stage: {0}")]
EvalError(String),
#[error("unknown error")]
Unknown,
}

View File

@@ -1,14 +1,13 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::{Result, anyhow};
use ecow::EcoString; use ecow::EcoString;
use rnix::ast::{self, Expr}; use rnix::ast::{self, Expr};
use crate::bytecode::{ConstIdx, Consts, ThunkIdx}; use crate::bytecode::{ConstIdx, Consts, ThunkIdx};
use crate::compile::*;
use crate::error::*;
use crate::ty::internal as i; use crate::ty::internal as i;
use super::compile::*;
pub fn downgrade(expr: Expr) -> Result<Downgraded> { pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let mut state = DowngradeState::new(); let mut state = DowngradeState::new();
let ir = expr.downgrade(&mut state)?; let ir = expr.downgrade(&mut state)?;
@@ -19,7 +18,15 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
}) })
} }
#[macro_export] trait Downcast<T: Sized>
where
Self: Sized,
{
fn downcast_ref(&self) -> Option<&T>;
fn downcast_mut(&mut self) -> Option<&mut T>;
fn downcast(self) -> core::result::Result<T, Self>;
}
macro_rules! ir { macro_rules! ir {
( (
$( $(
@@ -30,8 +37,6 @@ macro_rules! ir {
) )
,*$(,)? ,*$(,)?
) => { ) => {
use crate::downcast::Downcast;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Ir { pub enum Ir {
$( $(
@@ -73,16 +78,6 @@ macro_rules! ir {
} }
} }
impl TryFrom<Ir> for $ty {
type Error = anyhow::Error;
fn try_from(value: Ir) -> Result<Self> {
match value {
Ir::$ty(value) => Ok(value),
_ => Err(anyhow!("")),
}
}
}
impl Downcast<$ty> for Ir { impl Downcast<$ty> for Ir {
fn downcast_ref(&self) -> Option<&$ty> { fn downcast_ref(&self) -> Option<&$ty> {
match self { match self {
@@ -132,12 +127,6 @@ ir! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DynamicAttrPair(pub Ir, pub Ir); pub struct DynamicAttrPair(pub Ir, pub Ir);
#[derive(Debug)]
pub struct DowngradeError {
errno: u16,
text: String,
}
pub struct DowngradeState { pub struct DowngradeState {
thunks: Vec<Ir>, thunks: Vec<Ir>,
consts: Vec<i::Const>, consts: Vec<i::Const>,
@@ -180,7 +169,9 @@ impl Attrs {
.get_mut(&ident) .get_mut(&ident)
.unwrap() .unwrap()
.downcast_mut() .downcast_mut()
.ok_or(anyhow!(r#""{ident}" already exsists in this set"#)) .ok_or(Error::DowngradeError(format!(
r#""{ident}" already exsists in this set"#
)))
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value)) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
} else { } else {
let mut attrs = Attrs { let mut attrs = Attrs {
@@ -218,7 +209,9 @@ impl Attrs {
match name { match name {
Attr::Str(ident) => { Attr::Str(ident) => {
if self.stcs.get(&ident).is_some() { if self.stcs.get(&ident).is_some() {
return Err(anyhow!(r#""{ident}" already exsists in this set"#)); return Err(Error::DowngradeError(format!(
r#""{ident}" already exsists in this set"#
)));
} }
self.stcs.insert(ident, value); self.stcs.insert(ident, value);
} }
@@ -349,7 +342,7 @@ impl Downgrade for Expr {
match self { match self {
Expr::Apply(apply) => apply.downgrade(state), Expr::Apply(apply) => apply.downgrade(state),
Expr::Assert(assert) => assert.downgrade(state), Expr::Assert(assert) => assert.downgrade(state),
Expr::Error(error) => return Err(anyhow!(error.to_string())), Expr::Error(error) => return Err(Error::DowngradeError(error.to_string())),
Expr::IfElse(ifelse) => ifelse.downgrade(state), Expr::IfElse(ifelse) => ifelse.downgrade(state),
Expr::Select(select) => select.downgrade(state), Expr::Select(select) => select.downgrade(state),
Expr::Str(str) => str.downgrade(state), Expr::Str(str) => str.downgrade(state),
@@ -675,7 +668,11 @@ fn downgrade_inherit(
for attr in inherit.attrs() { for attr in inherit.attrs() {
let ident: EcoString = match downgrade_attr(attr, state)? { let ident: EcoString = match downgrade_attr(attr, state)? {
Attr::Str(ident) => ident.to_string().into(), Attr::Str(ident) => ident.to_string().into(),
_ => return Err(anyhow!("dynamic attributes not allowed in inherit")), _ => {
return Err(Error::DowngradeError(
"dynamic attributes not allowed in inherit".to_string(),
));
}
}; };
let expr = from.map_or_else( let expr = from.map_or_else(
|| Var { sym: ident.clone() }.ir().ok(), || Var { sym: ident.clone() }.ir().ok(),
@@ -694,29 +691,31 @@ fn downgrade_inherit(
} }
fn downgrade_attr(attr: ast::Attr, state: &mut DowngradeState) -> Result<Attr> { fn downgrade_attr(attr: ast::Attr, state: &mut DowngradeState) -> Result<Attr> {
use ast::Attr::*;
use ast::InterpolPart::*;
match attr { match attr {
ast::Attr::Ident(ident) => Ok(Attr::Str(ident.to_string().into())), Ident(ident) => Ok(Attr::Str(ident.to_string().into())),
ast::Attr::Str(string) => { Str(string) => {
let parts = string.normalized_parts(); let parts = string.normalized_parts();
if parts.len() == 1 { if parts.len() == 1 {
let ast::InterpolPart::Literal(ident) = parts.into_iter().next().unwrap() else { match parts.into_iter().next().unwrap() {
unreachable!() Literal(ident) => Ok(Attr::Str(ident.into())),
}; Interpolation(interpol) => {
Ok(Attr::Str(ident.into())) Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(state)?))
}
}
} else { } else {
let parts = parts let parts = parts
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(), Literal(lit) => Const { value: lit.into() }.ir().ok(),
ast::InterpolPart::Interpolation(interpol) => { Interpolation(interpol) => interpol.expr().unwrap().downgrade(state),
interpol.expr().unwrap().downgrade(state)
}
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
Ok(Attr::Strs(ConcatStrings { parts })) Ok(Attr::Strs(ConcatStrings { parts }))
} }
} }
ast::Attr::Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(state)?)), Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(state)?)),
} }
} }

5
src/jit/codegen.rs Normal file
View File

@@ -0,0 +1,5 @@
use super::JITContext;
pub trait CodeGen {
fn codegen(self, ctx: JITContext);
}

View File

@@ -1,21 +1,27 @@
use inkwell::OptimizationLevel;
use inkwell::builder::Builder; use inkwell::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::OptimizationLevel;
use inkwell::execution_engine::ExecutionEngine; use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
pub struct JIT<'ctx> { mod codegen;
pub use codegen::CodeGen;
pub struct JITContext<'ctx> {
context: &'ctx Context, context: &'ctx Context,
module: Module<'ctx>, module: Module<'ctx>,
builder: Builder<'ctx>, builder: Builder<'ctx>,
execution_engine: ExecutionEngine<'ctx>, execution_engine: ExecutionEngine<'ctx>,
} }
impl<'ctx> JIT<'ctx> { impl<'ctx> JITContext<'ctx> {
pub fn new(context: &Context) -> JIT { pub fn new(context: &Context) -> JITContext {
let module = context.create_module("nixjit"); let module = context.create_module("nixjit");
JIT { JITContext {
execution_engine: module.create_jit_execution_engine(OptimizationLevel::None).unwrap(), execution_engine: module
.create_jit_execution_engine(OptimizationLevel::None)
.unwrap(),
builder: context.create_builder(), builder: context.create_builder(),
context, context,
module, module,

View File

@@ -2,12 +2,14 @@
mod builtins; mod builtins;
mod bytecode; mod bytecode;
mod compile;
mod downcast;
mod ty; mod ty;
mod vm;
mod jit;
pub use compile::compile; pub mod compile;
pub mod error;
pub mod ir;
#[cfg(feature = "jit")]
pub mod jit;
#[cfg(feature = "vm")]
pub mod vm;
pub use ty::public::Value; pub use ty::public::Value;
pub use vm::run;

View File

@@ -1,15 +1,15 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result;
use derive_more::Constructor; use derive_more::Constructor;
use rpds::HashTrieMapSync; use rpds::HashTrieMapSync;
use crate::error::Result;
use crate::vm::VM;
use super::super::common::Symbol; use super::super::common::Symbol;
use super::super::public as p; use super::super::public as p;
use super::{ToPublic, Value}; use super::{ToPublic, Value};
use crate::vm::VM;
#[derive(Debug, Constructor, Clone, PartialEq)] #[derive(Debug, Constructor, Clone, PartialEq)]
pub struct AttrSet { pub struct AttrSet {
@@ -28,7 +28,7 @@ impl AttrSet {
} }
pub fn push_attr(&mut self, sym: Symbol, val: Value) { pub fn push_attr(&mut self, sym: Symbol, val: Value) {
if self.data.get(&sym).is_some() { if let Some(_) = self.data.get_mut(&sym) {
todo!() todo!()
} }
self.data.insert_mut(sym, val); self.data.insert_mut(sym, val);

View File

@@ -1,6 +1,4 @@
use anyhow::Error;
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
#[derive(Debug, Clone, IsVariant, Unwrap)] #[derive(Debug, Clone, IsVariant, Unwrap)]
@@ -47,49 +45,6 @@ impl From<&str> for Const {
} }
} }
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const { impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use Const::*; use Const::*;

View File

@@ -3,8 +3,8 @@ use itertools::Itertools;
use rpds::HashTrieMap; use rpds::HashTrieMap;
use crate::bytecode::OpCodes; use crate::bytecode::OpCodes;
use crate::error::Result;
use crate::ty::internal::{Thunk, Value}; use crate::ty::internal::{Thunk, Value};
use crate::vm::{CapturedEnv, VM}; use crate::vm::{CapturedEnv, VM};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -33,7 +33,7 @@ impl Func {
} }
} }
pub fn call(self, vm: &VM, arg: Value) -> Value { pub fn call(self, vm: &VM, arg: Value) -> Result<Value> {
use Param::*; use Param::*;
let env = self.env.released(); let env = self.env.released();
@@ -71,7 +71,7 @@ impl Func {
} }
} }
vm.eval(self.opcodes, env).unwrap() vm.eval(self.opcodes, env)
} }
pub fn push_ident_param(&mut self, param: EcoString) { pub fn push_ident_param(&mut self, param: EcoString) {

View File

@@ -1,11 +1,11 @@
use anyhow::Result;
use derive_more::Constructor; use derive_more::Constructor;
use rpds::VectorSync; use rpds::VectorSync;
use crate::error::Result;
use crate::ty::public as p; use crate::ty::public as p;
use crate::vm::VM;
use super::{ToPublic, Value}; use super::{ToPublic, Value};
use crate::vm::VM;
#[derive(Debug, Constructor, Clone, PartialEq)] #[derive(Debug, Constructor, Clone, PartialEq)]
pub struct List { pub struct List {

View File

@@ -1,7 +1,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{Result, anyhow};
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use super::common as c; use super::common as c;
@@ -11,6 +10,7 @@ use super::public as p;
use c::Symbol; use c::Symbol;
use crate::bytecode::OpCodes; use crate::bytecode::OpCodes;
use crate::error::*;
use crate::vm::{Env, VM}; use crate::vm::{Env, VM};
mod attrset; mod attrset;
@@ -65,9 +65,10 @@ impl Thunk {
match &*self.thunk.borrow() { match &*self.thunk.borrow() {
_Thunk::Value(value) => return Ok(value.as_ref().clone()), _Thunk::Value(value) => return Ok(value.as_ref().clone()),
_Thunk::SuspendedFrom(from) => { _Thunk::SuspendedFrom(from) => {
return Err(anyhow!( return Err(Error::EvalError(format!(
"already suspended from {from:p} (infinite recursion encountered)" "thunk {:p} already suspended from {from:p} (infinite recursion encountered)",
)); self as *const Thunk
)));
} }
_Thunk::Code(_) => (), _Thunk::Code(_) => (),
} }
@@ -77,9 +78,7 @@ impl Thunk {
_Thunk::SuspendedFrom(self as *const Thunk), _Thunk::SuspendedFrom(self as *const Thunk),
) )
.unwrap_code(); .unwrap_code();
let value = vm let value = vm.eval(opcodes, self.env.borrow().clone().unwrap())?;
.eval(opcodes, self.env.borrow().clone().unwrap())
.unwrap();
let _ = std::mem::replace( let _ = std::mem::replace(
&mut *self.thunk.borrow_mut(), &mut *self.thunk.borrow_mut(),
_Thunk::Value(value.clone().into()), _Thunk::Value(value.clone().into()),
@@ -199,9 +198,9 @@ impl Value {
} }
} }
pub fn call(self, vm: &VM, args: Vec<Value>) -> Value { pub fn call(self, vm: &VM, args: Vec<Value>) -> Result<Value> {
use Value::*; use Value::*;
match self { Ok(match self {
PrimOp(func) => func.call(vm, args), PrimOp(func) => func.call(vm, args),
PartialPrimOp(func) => func.call(vm, args), PartialPrimOp(func) => func.call(vm, args),
mut func @ Value::Func(_) => { mut func @ Value::Func(_) => {
@@ -209,12 +208,12 @@ impl Value {
while let Some(arg) = iter.next() { while let Some(arg) = iter.next() {
func = match func { func = match func {
PrimOp(func) => { PrimOp(func) => {
return func.call(vm, [arg].into_iter().chain(iter).collect()); return Ok(func.call(vm, [arg].into_iter().chain(iter).collect()));
} }
PartialPrimOp(func) => { PartialPrimOp(func) => {
return func.call(vm, [arg].into_iter().chain(iter).collect()); return Ok(func.call(vm, [arg].into_iter().chain(iter).collect()));
} }
Func(func) => func.call(vm, arg), Func(func) => func.call(vm, arg)?,
_ => todo!(), _ => todo!(),
} }
} }
@@ -222,7 +221,7 @@ impl Value {
} }
x @ Catchable(_) => x, x @ Catchable(_) => x,
_ => todo!(), _ => todo!(),
} })
} }
pub fn not(self) -> Value { pub fn not(self) -> Value {

View File

@@ -1,9 +1,10 @@
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
use anyhow::Error;
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
use crate::error::Error;
use super::super::internal as i; use super::super::internal as i;
#[derive(Debug, Clone, IsVariant, Unwrap)] #[derive(Debug, Clone, IsVariant, Unwrap)]

View File

@@ -1,8 +1,7 @@
use std::mem::{MaybeUninit, size_of, transmute}; use std::mem::{MaybeUninit, replace, size_of, transmute};
use std::ops::Deref; use std::ops::Deref;
use anyhow::{Result, anyhow}; use crate::error::*;
use crate::ty::internal::Value; use crate::ty::internal::Value;
pub const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>(); pub const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
@@ -12,6 +11,12 @@ pub struct Stack<const CAP: usize> {
top: usize, top: usize,
} }
macro_rules! into {
($e:expr) => {
unsafe { transmute($e) }
};
}
impl<const CAP: usize> Stack<CAP> { impl<const CAP: usize> Stack<CAP> {
pub fn new() -> Self { pub fn new() -> Self {
Stack { Stack {
@@ -27,51 +32,34 @@ impl<const CAP: usize> Stack<CAP> {
pub fn push(&mut self, item: Value) -> Result<()> { pub fn push(&mut self, item: Value) -> Result<()> {
self.items self.items
.get_mut(self.top) .get_mut(self.top)
.map_or(Err(anyhow!("stack overflow")), |ok| Ok(ok))? .map_or(Err(Error::EvalError("stack overflow".to_string())), |ok| {
Ok(ok)
})?
.write(item); .write(item);
self.top += 1; self.top += 1;
Ok(()) Ok(())
} }
pub fn pop(&mut self) -> Result<Value> { pub fn pop(&mut self) -> Value {
self.top -= 1; self.top -= 1;
let item = self let item = self.items.get_mut(self.top).unwrap();
.items
.get_mut(self.top) unsafe { replace(item, MaybeUninit::uninit()).assume_init() }
.map_or(Err(anyhow!("stack empty")), |ok| Ok(ok))?;
unsafe { Ok(std::mem::replace(item, MaybeUninit::uninit()).assume_init()) }
} }
pub fn tos(&self) -> Result<&Value> { pub fn tos(&self) -> Result<&Value> {
if self.top == 0 { if self.top == 0 {
Err(anyhow!("")) panic!("stack empty")
} else { } else {
unsafe { Ok(transmute(self.items.get(self.top - 1).unwrap())) } Ok(into!(&self.items[self.top - 1]))
} }
} }
pub fn tos_mut(&mut self) -> Result<&mut Value> { pub fn tos_mut(&mut self) -> Result<&mut Value> {
if self.top == 0 { if self.top == 0 {
Err(anyhow!("")) panic!("stack empty")
} else { } else {
unsafe { Ok(transmute(self.items.get_mut(self.top - 1).unwrap())) } Ok(into!(&mut self.items[self.top - 1]))
}
}
pub fn with_tos(&self, func: impl Fn(&Value)) -> Result<()> {
if self.top != 0 {
Err(anyhow!(""))
} else {
unsafe { func(transmute(self.items.get(self.top - 1).unwrap())) }
Ok(())
}
}
pub fn with_tos_mut(&mut self, func: impl Fn(&mut Value)) -> Result<()> {
if self.top != 0 {
Err(anyhow!(""))
} else {
unsafe { func(transmute(self.items.get_mut(self.top - 1).unwrap())) }
Ok(())
} }
} }
} }
@@ -79,7 +67,7 @@ impl<const CAP: usize> Stack<CAP> {
impl<const CAP: usize> Deref for Stack<CAP> { impl<const CAP: usize> Deref for Stack<CAP> {
type Target = [Value]; type Target = [Value];
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
unsafe { transmute(&self.items[0..self.top]) } into!(&self.items[0..self.top])
} }
} }

View File

@@ -2,6 +2,7 @@ use ecow::EcoString;
use rpds::{ht_map_sync, vector_sync}; use rpds::{ht_map_sync, vector_sync};
use crate::compile::compile; use crate::compile::compile;
use crate::ir::downgrade;
use crate::ty::common::Symbol; use crate::ty::common::Symbol;
use crate::ty::public::*; use crate::ty::public::*;
@@ -9,7 +10,8 @@ use super::vm::run;
#[inline] #[inline]
fn test_expr(expr: &str, expected: Value) { fn test_expr(expr: &str, expected: Value) {
let prog = compile(expr).unwrap(); let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
let prog = compile(downgraded);
dbg!(&prog); dbg!(&prog);
assert_eq!(run(prog).unwrap(), expected); assert_eq!(run(prog).unwrap(), expected);
} }

View File

@@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use anyhow::{Result, anyhow};
use crate::builtins::env; use crate::builtins::env;
use crate::bytecode::{self, BinOp, OpCode, OpCodes, Program, Thunks, UnOp}; use crate::bytecode::{self, BinOp, OpCode, OpCodes, Program, Thunks, UnOp};
use crate::error::*;
use crate::ty::common::Symbol; use crate::ty::common::Symbol;
use crate::ty::internal::*; use crate::ty::internal::*;
use crate::ty::public as p; use crate::ty::public as p;
@@ -44,10 +43,8 @@ impl VM {
} }
assert_eq!(stack.len(), 1); assert_eq!(stack.len(), 1);
let mut ret = stack.pop(); let mut ret = stack.pop();
if let Ok(ref mut value) = ret { ret.force(self)?;
value.force(self)?; Ok(ret)
}
ret
} }
#[inline] #[inline]
@@ -58,7 +55,7 @@ impl VM {
env: Arc<Env>, env: Arc<Env>,
) -> Result<usize> { ) -> Result<usize> {
match opcode { match opcode {
OpCode::Illegal => return Err(anyhow!("illegal opcode")), OpCode::Illegal => panic!("illegal opcode"),
OpCode::Const { value } => stack.push(Value::Const(value))?, OpCode::Const { value } => stack.push(Value::Const(value))?,
OpCode::LoadThunk { idx } => { OpCode::LoadThunk { idx } => {
self.thunks[idx].capture(env); self.thunks[idx].capture(env);
@@ -72,23 +69,23 @@ impl VM {
} }
OpCode::Jmp { step } => return Ok(step), OpCode::Jmp { step } => return Ok(step),
OpCode::JmpIfTrue { step } => { OpCode::JmpIfTrue { step } => {
if let Value::Const(Const::Bool(true)) = stack.pop()? { if let Value::Const(Const::Bool(true)) = stack.pop() {
return Ok(step); return Ok(step);
} }
} }
OpCode::JmpIfFalse { step } => { OpCode::JmpIfFalse { step } => {
if let Value::Const(Const::Bool(false)) = stack.pop()? { if let Value::Const(Const::Bool(false)) = stack.pop() {
return Ok(step); return Ok(step);
} }
} }
OpCode::Call { arity } => { OpCode::Call { arity } => {
let mut args = Vec::with_capacity(arity); let mut args = Vec::with_capacity(arity);
for _ in 0..arity { for _ in 0..arity {
args.insert(0, stack.pop()?); args.insert(0, stack.pop());
} }
let mut func = stack.pop()?; let mut func = stack.pop();
func.force(self)?; func.force(self)?;
stack.push(func.call(self, args))?; stack.push(func.call(self, args)?)?;
} }
OpCode::Func { idx } => { OpCode::Func { idx } => {
stack.push(Value::Func(Func::new( stack.push(Value::Func(Func::new(
@@ -125,7 +122,7 @@ impl VM {
} }
OpCode::UnOp { op } => { OpCode::UnOp { op } => {
use UnOp::*; use UnOp::*;
let mut value = stack.pop()?; let mut value = stack.pop();
value.force(self)?; value.force(self)?;
stack.push(match op { stack.push(match op {
Not => value.not(), Not => value.not(),
@@ -133,8 +130,8 @@ impl VM {
} }
OpCode::BinOp { op } => { OpCode::BinOp { op } => {
use BinOp::*; use BinOp::*;
let mut rhs = stack.pop()?; let mut rhs = stack.pop();
let mut lhs = stack.pop()?; let mut lhs = stack.pop();
lhs.force(self)?; lhs.force(self)?;
rhs.force(self)?; rhs.force(self)?;
stack.push(match op { stack.push(match op {
@@ -147,7 +144,7 @@ impl VM {
})?; })?;
} }
OpCode::ConcatString => { OpCode::ConcatString => {
let mut rhs = stack.pop()?; let mut rhs = stack.pop();
rhs.force(self)?; rhs.force(self)?;
stack.tos_mut()?.concat_string(rhs); stack.tos_mut()?.concat_string(rhs);
} }
@@ -158,7 +155,7 @@ impl VM {
stack.push(Value::List(List::empty()))?; stack.push(Value::List(List::empty()))?;
} }
OpCode::PushElem => { OpCode::PushElem => {
let elem = stack.pop()?; let elem = stack.pop();
stack.tos_mut()?.push(elem); stack.tos_mut()?.push(elem);
} }
OpCode::AttrSet => { OpCode::AttrSet => {
@@ -169,13 +166,13 @@ impl VM {
stack.push(Value::RecAttrSet(RecAttrSet::new(new)))?; stack.push(Value::RecAttrSet(RecAttrSet::new(new)))?;
} }
OpCode::PushStaticAttr { name } => { OpCode::PushStaticAttr { name } => {
let val = stack.pop()?; let val = stack.pop();
stack.tos_mut()?.push_attr(Symbol::new(name), val); stack.tos_mut()?.push_attr(Symbol::new(name), val);
} }
OpCode::PushDynamicAttr => { OpCode::PushDynamicAttr => {
let val = stack.pop()?; let val = stack.pop();
let mut sym = stack.pop().unwrap(); let mut sym = stack.pop();
sym.coerce_to_string(); sym.force(self)?.coerce_to_string();
let sym = sym.unwrap_const().unwrap_string().into(); let sym = sym.unwrap_const().unwrap_string().into();
stack.tos_mut()?.push_attr(sym, val); stack.tos_mut()?.push_attr(sym, val);
} }
@@ -183,22 +180,22 @@ impl VM {
stack.tos_mut()?.force(self)?.select(Symbol::new(sym)); stack.tos_mut()?.force(self)?.select(Symbol::new(sym));
} }
OpCode::SelectOrDefault { sym } => { OpCode::SelectOrDefault { sym } => {
let default = stack.pop()?; let default = stack.pop();
stack stack
.tos_mut()? .tos_mut()?
.force(self)? .force(self)?
.select_with_default(Symbol::new(sym), default); .select_with_default(Symbol::new(sym), default);
} }
OpCode::SelectDynamic => { OpCode::SelectDynamic => {
let mut val = stack.pop().unwrap(); let mut val = stack.pop();
val.force(self)?; val.force(self)?;
val.coerce_to_string(); val.coerce_to_string();
let sym = val.unwrap_const().unwrap_string().into(); let sym = val.unwrap_const().unwrap_string().into();
stack.tos_mut()?.force(self)?.select(sym); stack.tos_mut()?.force(self)?.select(sym);
} }
OpCode::SelectDynamicOrDefault => { OpCode::SelectDynamicOrDefault => {
let default = stack.pop()?; let default = stack.pop();
let mut val = stack.pop().unwrap(); let mut val = stack.pop();
val.force(self)?; val.force(self)?;
val.coerce_to_string(); val.coerce_to_string();
let sym = val.unwrap_const().unwrap_string().into(); let sym = val.unwrap_const().unwrap_string().into();
@@ -211,7 +208,7 @@ impl VM {
stack.tos_mut()?.force(self)?.has_attr(Symbol::new(sym)); stack.tos_mut()?.force(self)?.has_attr(Symbol::new(sym));
} }
OpCode::HasDynamicAttr => { OpCode::HasDynamicAttr => {
let mut val = stack.pop().unwrap(); let mut val = stack.pop();
val.coerce_to_string(); val.coerce_to_string();
let sym = val.unwrap_const().unwrap_string().into(); let sym = val.unwrap_const().unwrap_string().into();
stack.tos_mut()?.force(self)?.has_attr(sym); stack.tos_mut()?.force(self)?.has_attr(sym);
@@ -219,10 +216,10 @@ impl VM {
OpCode::LookUp { sym } => { OpCode::LookUp { sym } => {
stack.push( stack.push(
env.lookup(Symbol::new(sym.clone())) env.lookup(Symbol::new(sym.clone()))
.ok_or(anyhow!(r#""{sym}" not found"#))?, .ok_or(Error::EvalError(format!(r#""{sym}" not found"#)))?,
)?; )?;
} }
OpCode::EnterEnv => match stack.pop()? { OpCode::EnterEnv => match stack.pop() {
Value::AttrSet(attrs) => env.enter(attrs.into_inner()), Value::AttrSet(attrs) => env.enter(attrs.into_inner()),
Value::RecAttrSet(attrs) => env.enter(attrs.into_inner()), Value::RecAttrSet(attrs) => env.enter(attrs.into_inner()),
_ => unreachable!(), _ => unreachable!(),
@@ -231,7 +228,7 @@ impl VM {
env.leave(); env.leave();
} }
OpCode::Assert => { OpCode::Assert => {
if !stack.pop()?.unwrap_const().unwrap_bool() { if !stack.pop().unwrap_const().unwrap_bool() {
todo!() todo!()
} }
} }