feat: refactor

This commit is contained in:
2025-05-03 16:22:05 +08:00
parent 43cfb9be62
commit f78c516d17
19 changed files with 361 additions and 345 deletions

5
Cargo.lock generated
View File

@@ -228,15 +228,14 @@ dependencies = [
]
[[package]]
name = "nix-rs"
version = "0.1.0"
name = "nixjit"
version = "0.0.0"
dependencies = [
"anyhow",
"crossbeam-channel",
"derive_more",
"ecow",
"itertools",
"num_cpus",
"once_cell",
"rayon",
"rnix",

View File

@@ -1,6 +1,6 @@
[package]
name = "nix-rs"
version = "0.1.0"
name = "nixjit"
version = "0.0.0"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,6 +14,5 @@ tokio = { version = "1.38", features = [ "full" ] }
rpds = "1.1"
derive_more = "0.99"
crossbeam-channel = "0.5"
num_cpus = "1.0"
ecow = "0.2"
once_cell = "1.19"

View File

@@ -3,39 +3,55 @@ use derive_more::Constructor;
use super::vm::Env;
use super::vm::Symbol;
use super::vm::Value;
use crate::bytecode::Const;
use crate::value::Const;
pub fn env() -> Env {
let mut env = Env::empty();
env.insert(Symbol::from("true"), Value::Const(Const::Bool(true)));
env.insert(Symbol::from("false"), Value::Const(Const::Bool(false)));
env.insert(Symbol::from("__add"), Value::PrimOp(PrimOp::new(2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second)
})));
env.insert(Symbol::from("__sub"), Value::PrimOp(PrimOp::new(2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second.neg())
})));
env.insert(Symbol::from("__mul"), Value::PrimOp(PrimOp::new(2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.mul(second)
})));
env.insert(Symbol::from("__div"), Value::PrimOp(PrimOp::new(2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.div(second)
})));
env.insert(Symbol::from("__lessThan"), Value::PrimOp(PrimOp::new(2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.lt(second)
})));
env.insert(
Symbol::from("__add"),
Value::PrimOp(PrimOp::new("add", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second)
})),
);
env.insert(
Symbol::from("__sub"),
Value::PrimOp(PrimOp::new("sub", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second.neg())
})),
);
env.insert(
Symbol::from("__mul"),
Value::PrimOp(PrimOp::new("mul", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.mul(second)
})),
);
env.insert(
Symbol::from("__div"),
Value::PrimOp(PrimOp::new("div", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.div(second)
})),
);
env.insert(
Symbol::from("__lessThan"),
Value::PrimOp(PrimOp::new("lessThan", 2, |args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.lt(second)
})),
);
env
}
#[derive(Debug, Clone, Constructor)]
pub struct PrimOp {
name: &'static str,
arity: u8,
func: fn(Vec<Value>) -> Value,
}
@@ -49,7 +65,12 @@ impl PartialEq for PrimOp {
impl PrimOp {
pub fn call(self, args: Vec<Value>) -> Value {
if (args.len() as u8) < self.arity {
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - args.len() as u8, args, func: self.func })
Value::PartialPrimOp(PartialPrimOp {
name: self.name,
arity: self.arity - args.len() as u8,
args,
func: self.func,
})
} else if args.len() as u8 == self.arity {
(self.func)(args)
} else {
@@ -60,9 +81,10 @@ impl PrimOp {
#[derive(Debug, Clone)]
pub struct PartialPrimOp {
name: &'static str,
arity: u8,
args: Vec<Value>,
func: fn(Vec<Value>) -> Value
func: fn(Vec<Value>) -> Value,
}
impl PartialEq for PartialPrimOp {
@@ -76,7 +98,12 @@ impl PartialPrimOp {
let len = args.len() as u8;
self.args.extend(args);
if len < self.arity {
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - len, args: self.args, func: self.func })
Value::PartialPrimOp(PartialPrimOp {
name: self.name,
arity: self.arity - len,
args: self.args,
func: self.func,
})
} else if len == self.arity {
(self.func)(self.args)
} else {

View File

@@ -1,11 +1,10 @@
use std::hash::{Hash, Hasher};
use std::hash::Hash;
use ecow::EcoString;
use anyhow::Error;
use derive_more::{IsVariant, Unwrap};
use crate::slice::Slice;
use crate::value::Func;
use crate::value::Const;
type Slice<T> = Box<[T]>;
pub type ThunkIdx = usize;
pub type ConstIdx = usize;
@@ -23,122 +22,6 @@ pub struct Thunk {
#[derive(Debug, Clone, Hash)]
pub enum Arg {}
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Func(Func),
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}
impl Hash for Const {
fn hash<H: Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Bool(b) => b.hash(state),
Int(int) => int.hash(state),
Float(float) => float.to_bits().hash(state),
String(string) => string.hash(state),
Func(func) => func.hash(state),
}
}
}
#[derive(Debug, Clone, Hash)]
pub enum OpCode {
/// load a constant onto stack
@@ -151,60 +34,58 @@ pub enum OpCode {
LoadValue { idx: ThunkIdx },
/// force TOS to value
ForceValue,
/// [ ... func, args @ .. ] call func with `arity` numbers of arg
/// [ .. func args @ .. ] consume (`arity` + 2) elements, call `func` with args` of length `arity`
/// Example: __add 1 2 => [ LookUp("__add") Const(1) Const(2) Call(2) ]
Call { arity: usize },
/// assert TOS is true then consume it
/// consume 1 element, assert TOS is true
Assert,
/// jump forward
Jmp { step: usize },
/// [ ... cond ] if (cond) is true, then jump forward
/// [ .. cond ] consume 1 element, if `cond`` is true, then jump forward
JmpIfTrue { step: usize },
/// [ ... cond ] if (cond) is false, then jump forward
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
JmpIfFalse { step: usize },
/// push an empty attribute set onto stack
AttrSet,
/// push an empty recursive attribute set onto stack
RecAttrSet,
/// [ ... set, value ] push the static kv pair (name, (value)) into (set)
/// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
PushStaticAttr { name: EcoString },
/// [ ... set, name, value ] push the dynamic kv pair ((name), (value)) in to (set)
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
PushDynamicAttr,
/// push an empty list onto stack
List,
/// [ ... list, elem ] push (elem) into (list)
/// [ .. list elem ] consume 1 element, push `elem` into `list`
PushElem,
/// [ ... a, b ] perform a binary operation ((a) `op` (b))
BinOp { op: BinOp },
/// [ ... a ] perform a unary operation (`op` (a))
UnOp { op: UnOp },
/// TODO:
/// [ .. a b ] consume 2 elements, perform a string concatenation `a` + `b`
ConcatString,
/// TODO:
/// [ .. 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: EcoString },
/// TODO:
/// [ .. set sym ] consume 2 elements, set TOS to the bool value of whether `set` contains `sym`
HasDynamicAttr,
// HasAttr { arity: usize },
/// TODO:
/// [ .. set ] select `sym` from `set`
Select { sym: EcoString },
// Select { arity: usize },
/// TODO:
SelectDynamic,
// SelectDynamic { arity: usize },
SelectWithDefault { sym: EcoString },
/// TODO:
SelectOrEmpty { sym: EcoString },
/// TODO:
SelectDynamicOrEmpty,
SelectDynamic,
/// TODO:
SelectWithDefault { sym: EcoString },
SelectDynamicOrEmpty,
/// TODO:
SelectDynamicWithDefault,
/// enter the environment of the attribute set at TOS
EnterEnv,
/// exit the envrironment
/// exit current envrironment
LeaveEnv,
/// return a value
Ret,
/// no-op
/// no operation
NoOp,
}

View File

@@ -1,4 +1,5 @@
use crate::bytecode::*;
use crate::value::*;
use super::ir;
@@ -93,6 +94,27 @@ 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);
for stc in self.stcs {
stc.1.compile(comp);
@@ -106,29 +128,6 @@ impl Compile for ir::Attrs {
}
}
impl Compile for ir::RecAttrs {
fn compile(self, comp: &mut Compiler) {
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);
}
}
impl Compile for ir::List {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::List);
@@ -144,8 +143,12 @@ impl Compile for ir::UnOp {
use ir::UnOpKind::*;
match self.kind {
Neg => {
comp.push(OpCode::LookUp { sym: "__sub".into() });
comp.push(OpCode::Const { value: Const::Int(0) });
comp.push(OpCode::LookUp {
sym: "__sub".into(),
});
comp.push(OpCode::Const {
value: Const::Int(0),
});
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
}
@@ -167,13 +170,17 @@ impl Compile for ir::BinOp {
comp.push(OpCode::BinOp { op: BinOp::Add });
}
Mul => {
comp.push(OpCode::LookUp { sym: "__mul".into() });
comp.push(OpCode::LookUp {
sym: "__mul".into(),
});
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
}
Div => {
comp.push(OpCode::LookUp { sym: "__div".into() });
comp.push(OpCode::LookUp {
sym: "__div".into(),
});
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
@@ -194,7 +201,9 @@ impl Compile for ir::BinOp {
comp.push(OpCode::BinOp { op: BinOp::Eq });
}
Lt => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
@@ -211,7 +220,9 @@ impl Compile for ir::BinOp {
}
Sub => {
comp.push(OpCode::LookUp { sym: "__sub".into() });
comp.push(OpCode::LookUp {
sym: "__sub".into(),
});
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
@@ -229,20 +240,26 @@ impl Compile for ir::BinOp {
comp.push(OpCode::UnOp { op: UnOp::Not });
}
Gt => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.rhs.compile(comp);
self.lhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
}
Leq => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.rhs.compile(comp);
self.lhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
comp.push(OpCode::UnOp { op: UnOp::Not });
}
Geq => {
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
comp.push(OpCode::LookUp {
sym: "__lessThan".into(),
});
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 2 });
@@ -372,19 +389,19 @@ impl Compile for ir::Assert {
}
impl Compile for ir::Func {
fn compile(self, comp: &mut Compiler) {
fn compile(self, _comp: &mut Compiler) {
todo!()
}
}
impl Compile for ir::Call {
fn compile(self, comp: &mut Compiler) {
fn compile(self, _comp: &mut Compiler) {
todo!()
}
}
impl Compile for ir::Path {
fn compile(self, comp: &mut Compiler) {
fn compile(self, _comp: &mut Compiler) {
todo!()
}
}

View File

@@ -1,17 +1,14 @@
// TODO: Error Handling
use std::collections::HashMap;
use anyhow::{anyhow, Result};
use rnix::ast::{self, Expr};
use anyhow::{Result, anyhow};
use ecow::EcoString;
use rnix::ast::{self, Expr};
use crate::bytecode::{Const as ByteCodeConst, ConstIdx, Consts, ThunkIdx};
use crate::slice::Slice;
use crate::bytecode::{ConstIdx, Consts, ThunkIdx};
use crate::value::Const as Const_;
use super::compile::*;
use super::env::IrEnv;
use super::symtable::*;
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let mut state = DowngradeState::new();
@@ -112,8 +109,7 @@ macro_rules! ir {
}
ir! {
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
RecAttrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair>, rec: bool },
List => { items: Vec<Ir> },
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
@@ -127,7 +123,7 @@ ir! {
With => { namespace: Box<Ir>, expr: Box<Ir> },
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
ConcatStrings => { parts: Vec<Ir> },
Const => { value: ByteCodeConst },
Const => { value: Const_ },
Var => { sym: EcoString },
#[derive(Copy)]
Thunk => { idx: ThunkIdx },
@@ -165,23 +161,21 @@ pub struct DowngradeError {
}
pub struct DowngradeState {
sym_table: SymTable,
envs: Vec<Env>,
thunks: Vec<Ir>,
consts: Vec<ByteCodeConst>,
consts_table: HashMap<ByteCodeConst, ConstIdx>,
consts: Vec<Const_>,
consts_table: HashMap<Const_, ConstIdx>,
}
pub struct Downgraded {
pub top_level: Ir,
pub consts: Consts,
pub thunks: Slice<Ir>,
pub thunks: Box<[Ir]>,
}
impl DowngradeState {
fn new() -> DowngradeState {
DowngradeState {
sym_table: SymTable::new(),
envs: Vec::new(),
thunks: Vec::new(),
consts: Vec::new(),
@@ -214,6 +208,7 @@ impl Attrs {
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
} else {
let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(),
dyns: Vec::new(),
};
@@ -224,6 +219,7 @@ impl Attrs {
}
Attr::Strs(string) => {
let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(),
dyns: Vec::new(),
};
@@ -233,6 +229,7 @@ impl Attrs {
}
Attr::Dynamic(dynamic) => {
let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(),
dyns: Vec::new(),
};
@@ -317,8 +314,8 @@ pub enum BinOpKind {
impl From<ast::BinOpKind> for BinOpKind {
fn from(op: ast::BinOpKind) -> Self {
use ast::BinOpKind as astkind;
use BinOpKind::*;
use ast::BinOpKind as astkind;
match op {
astkind::Concat => Con,
astkind::Update => Upd,
@@ -366,7 +363,7 @@ pub enum Param {
trait Downgrade
where
Self: Sized
Self: Sized,
{
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir>;
}
@@ -427,9 +424,11 @@ impl Downgrade for ast::Path {
.parts()
.into_iter()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => Const { value: lit.to_string().into() }
.ir()
.ok(),
ast::InterpolPart::Literal(lit) => Const {
value: lit.to_string().into(),
}
.ir()
.ok(),
ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state)
}
@@ -455,9 +454,7 @@ impl Downgrade for ast::Str {
.normalized_parts()
.into_iter()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => {
Const { value: lit.into() }.ir().ok()
}
ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(),
ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state)
}
@@ -472,11 +469,17 @@ impl Downgrade for ast::Str {
}
impl Downgrade for ast::Literal {
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> {
match self.kind() {
ast::LiteralKind::Integer(int) => Const { value: int.value().unwrap().into() },
ast::LiteralKind::Float(float) => Const { value: float.value().unwrap().into() },
ast::LiteralKind::Uri(uri) => Const { value: uri.to_string().into() },
ast::LiteralKind::Integer(int) => Const {
value: int.value().unwrap().into(),
},
ast::LiteralKind::Float(float) => Const {
value: float.value().unwrap().into(),
},
ast::LiteralKind::Uri(uri) => Const {
value: uri.to_string().into(),
},
}
.ir()
.ok()
@@ -486,7 +489,7 @@ impl Downgrade for ast::Literal {
impl Downgrade for ast::Ident {
fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> {
Var {
sym: self.to_string().into()
sym: self.to_string().into(),
}
.ir()
.ok()
@@ -584,12 +587,7 @@ impl Downgrade for ast::LetIn {
let body = self.body().unwrap();
let attrs = downgrade_has_entry(self, true, state)?;
let expr = body.downgrade(state)?.boxed();
Let {
attrs,
expr
}
.ir()
.ok()
Let { attrs, expr }.ir().ok()
}
}
@@ -636,9 +634,7 @@ impl Downgrade for ast::Apply {
fn downgrade_param(param: ast::Param, state: &mut DowngradeState) -> Result<Param> {
match param {
ast::Param::IdentParam(ident) => {
Ok(Param::Ident(ident.to_string().into()))
}
ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string().into())),
ast::Param::Pattern(pattern) => downgrade_pattern(pattern, state),
}
}
@@ -677,6 +673,7 @@ fn downgrade_has_entry(
) -> Result<Attrs> {
let entires = has_entry.entries();
let mut attrs = Attrs {
rec,
stcs: HashMap::new(),
dyns: Vec::new(),
};
@@ -684,9 +681,7 @@ fn downgrade_has_entry(
for entry in entires {
match entry {
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, state)?,
ast::Entry::AttrpathValue(value) => {
downgrade_attrpathvalue(value, &mut attrs, state)?
}
ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, state)?,
}
}
@@ -710,7 +705,7 @@ fn downgrade_inherit(
_ => return Err(anyhow!("dynamic attributes not allowed in inherit")),
};
let expr = from.map_or_else(
|| Var { sym: ident.clone() }.ir().ok(),
|| Var { sym: ident.clone() }.ir().ok(),
|from| {
Ok(Select {
expr: from.ir().boxed(),
@@ -739,9 +734,7 @@ fn downgrade_attr(attr: ast::Attr, state: &mut DowngradeState) -> Result<Attr> {
let parts = parts
.into_iter()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => {
Const { value: lit.into() }.ir().ok()
}
ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(),
ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(state)
}

View File

@@ -1,7 +1,6 @@
mod compile;
mod env;
mod ir;
mod symtable;
pub fn compile(expr: &str) -> anyhow::Result<crate::bytecode::Program> {
let expr = rnix::Root::parse(expr).tree().expr().unwrap();

View File

@@ -1,35 +0,0 @@
use std::collections::HashMap;
use crate::bytecode::SymIdx;
use crate::slice::Slice;
// FIXME: don't store syms twice to make it more memory efficient?
pub struct SymTable {
syms: Vec<String>,
syms_table: HashMap<String, SymIdx>,
}
impl SymTable {
pub fn new() -> SymTable {
SymTable {
syms: Vec::new(),
syms_table: HashMap::new(),
}
}
pub fn lookup(&mut self, name: String) -> SymIdx {
if let Some(sym) = self.syms_table.get(&name) {
*sym
} else {
let sym = self.syms.len();
self.syms.push(name.clone());
self.syms_table.insert(name, sym);
sym
}
}
pub fn syms(self) -> Slice<String> {
self.syms.into()
}
}

View File

@@ -4,6 +4,5 @@ mod builtins;
mod bytecode;
mod compile;
mod downcast;
mod slice;
mod value;
mod vm;

View File

@@ -1,2 +0,0 @@
pub type Slice<T> = Box<[T]>;

124
src/value/cnst.rs Normal file
View File

@@ -0,0 +1,124 @@
use std::hash::{Hash, Hasher};
use anyhow::Error;
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
use super::Func;
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Func(Func),
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}
impl Hash for Const {
fn hash<H: Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Bool(b) => b.hash(state),
Int(int) => int.hash(state),
Float(float) => float.to_bits().hash(state),
String(string) => string.hash(state),
Func(func) => func.hash(state),
}
}
}

View File

@@ -1,12 +1,15 @@
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::ops::Deref;
use std::sync::Arc;
use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString;
use rpds::{HashTrieMapSync, VectorSync};
use crate::bytecode::{Args, Const, OpCodes};
use crate::bytecode::{Args, OpCodes};
mod cnst;
pub use cnst::Const;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)]
pub struct Symbol(EcoString);
@@ -93,7 +96,7 @@ pub struct List {
#[derive(Clone, Debug, PartialEq, Constructor)]
pub struct Catchable {
msg: Option<String>
msg: Option<String>,
}
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]

View File

@@ -3,7 +3,7 @@ use rpds::HashTrieMapSync;
use super::value::{Symbol, VmValue};
pub struct Env {
last: Option<*mut Env>,
last: Option<Box<Env>>,
map: HashTrieMapSync<Symbol, VmValue>,
}
@@ -19,7 +19,7 @@ impl Env {
if let Some(value) = self.map.get(&symbol) {
value.clone()
} else {
let last = unsafe { &*self.last.unwrap() };
let last = self.last.as_ref().unwrap();
last.lookup(symbol)
}
}
@@ -29,19 +29,13 @@ impl Env {
}
pub fn enter(&mut self, map: HashTrieMapSync<Symbol, VmValue>) {
let last = std::mem::replace(
self,
Env {
last: None,
map,
},
);
self.last = Some(Box::leak(Box::new(last)) as *mut Env);
let last = std::mem::replace(self, Env { last: None, map });
self.last = Some(Box::new(last));
}
pub fn leave(&mut self) {
let last = unsafe { &*self.last.unwrap() };
self.last = last.last;
let last = std::mem::replace(&mut self.last, None).unwrap();
let _ = std::mem::replace(&mut self.last, last.last);
self.map = last.map.clone();
}
}

View File

@@ -1,7 +1,7 @@
use std::mem::{size_of, transmute, MaybeUninit};
use std::mem::{MaybeUninit, size_of, transmute};
use std::ops::Deref;
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use super::value::VmValue;

View File

@@ -3,7 +3,6 @@ use rpds::{ht_map_sync, vector_sync};
use crate::compile::compile;
use crate::value::*;
use crate::bytecode::Const;
use super::vm::run;

View File

@@ -12,7 +12,14 @@ pub struct AttrSet {
}
impl AttrSet {
pub fn push_attr_force(&mut self, sym: Symbol, val: VmValue) {
self.data.insert_mut(sym, val);
}
pub fn push_attr(&mut self, sym: Symbol, val: VmValue) {
if self.data.get(&sym).is_some() {
todo!()
}
self.data.insert_mut(sym, val);
}
@@ -26,7 +33,7 @@ impl AttrSet {
pub fn update(mut self, other: AttrSet) -> AttrSet {
for (k, v) in other.data.iter() {
self.push_attr(k.clone(), v.clone())
self.push_attr_force(k.clone(), v.clone())
}
self
}
@@ -41,7 +48,12 @@ impl ToValue for AttrSet {
Value::AttrSet(value::AttrSet::new(
self.data
.iter()
.map(|(sym, value)| (value::Symbol::new(sym.0.clone()), value.clone().to_value(vm)))
.map(|(sym, value)| {
(
value::Symbol::new(sym.0.clone()),
value.clone().to_value(vm),
)
})
.collect(),
))
}

View File

@@ -1,12 +1,11 @@
use derive_more::{Constructor, IsVariant, Unwrap};
use anyhow::Result;
use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString;
use crate::value::*;
use crate::bytecode::Const;
use super::vm::VM;
use super::env::Env;
use super::vm::VM;
mod attrset;
mod list;
@@ -40,7 +39,7 @@ pub enum VmValue {
List(List),
Catchable(crate::value::Catchable),
PrimOp(crate::builtins::PrimOp),
PartialPrimOp(crate::builtins::PartialPrimOp)
PartialPrimOp(crate::builtins::PartialPrimOp),
}
use VmValue::Const as VmConst;
@@ -49,7 +48,7 @@ impl VmValue {
match self {
VmValue::PrimOp(func) => func.call(args),
VmValue::PartialPrimOp(func) => func.call(args),
_ => todo!()
_ => todo!(),
}
}
@@ -90,7 +89,7 @@ impl VmValue {
(VmConst(Float(a)), VmConst(Int(b))) => a < b as f64,
(VmConst(Float(a)), VmConst(Float(b))) => a < b,
(VmConst(String(a)), VmConst(String(b))) => a < b,
_ => todo!()
_ => todo!(),
}))
}
@@ -144,7 +143,9 @@ impl VmValue {
}
pub fn concat_string(&mut self, mut other: VmValue) -> &mut Self {
if let (VmConst(Const::String(a)), VmConst(Const::String(b))) = (self.coerce_to_string(), other.coerce_to_string()) {
if let (VmConst(Const::String(a)), VmConst(Const::String(b))) =
(self.coerce_to_string(), other.coerce_to_string())
{
a.push_str(b.as_str());
} else {
todo!()
@@ -190,7 +191,9 @@ impl VmValue {
if let VmValue::AttrSet(attrs) = self {
let val = attrs
.select(sym.clone())
.unwrap_or(VmValue::Catchable(Catchable::new(Some(format!("{sym:?} not found")))));
.unwrap_or(VmValue::Catchable(Catchable::new(Some(format!(
"{sym:?} not found"
)))));
*self = val;
} else {
todo!()

View File

@@ -1,13 +1,12 @@
use anyhow::{anyhow, Result};
use anyhow::Result;
use rpds::{HashTrieMap, HashTrieMapSync, Vector};
use crate::bytecode::{self, *};
use crate::slice::*;
use crate::value::{self, Value};
use crate::builtins::env;
use crate::bytecode::{self, *};
use crate::value::{Const, Value};
use super::env::Env;
use super::stack::{Stack, STACK_SIZE};
use super::stack::{STACK_SIZE, Stack};
use super::value::{self as vmValue, *};
use super::vmthunk::*;
@@ -17,7 +16,7 @@ pub fn run(prog: Program) -> Result<Value> {
}
pub struct VM {
thunks: Slice<VmThunk>,
thunks: Box<[VmThunk]>,
}
impl VM {
@@ -26,9 +25,7 @@ impl VM {
.into_iter()
.map(|bytecode::Thunk { opcodes }| VmThunk::new(opcodes))
.collect();
VM {
thunks,
}
VM { thunks }
}
pub fn get_thunk_value(&self, idx: usize, env: &mut Env) -> Result<VmValue> {
@@ -77,7 +74,7 @@ impl VM {
}
}
OpCode::Call { arity } => {
let mut args = Vec::with_capacity(arity);
let mut args = Vec::with_capacity(arity);
for _ in 0..arity {
args.insert(0, stack.pop()?);
}
@@ -139,9 +136,10 @@ impl VM {
.select_with_default(Symbol::new(sym), default.clone());
}
OpCode::SelectOrEmpty { sym } => {
stack
.tos_mut()?
.select_with_default(Symbol::new(sym), VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())));
stack.tos_mut()?.select_with_default(
Symbol::new(sym),
VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())),
);
}
OpCode::SelectDynamic => {
let mut val = stack.pop().unwrap();
@@ -160,7 +158,10 @@ impl VM {
let mut val = stack.pop().unwrap();
val.coerce_to_string();
let sym = val.unwrap_const().unwrap_string().into();
stack.tos_mut()?.select_with_default(sym, VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())));
stack.tos_mut()?.select_with_default(
sym,
VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())),
);
}
OpCode::HasAttr { sym } => {
stack.tos_mut()?.has_attr(Symbol::new(sym));
@@ -185,4 +186,3 @@ impl VM {
Ok(0)
}
}

View File

@@ -1,18 +1,18 @@
use std::cell::RefCell;
use std::sync::RwLock;
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use derive_more::{IsVariant, Unwrap};
use crate::bytecode::OpCodes;
use super::vm::VM;
use super::env::Env;
use super::value::VmValue;
use super::vm::VM;
pub struct VmThunk {
thunk: RefCell<_VmThunk>,
lock: RwLock<()>
lock: RwLock<()>,
}
#[derive(IsVariant, Unwrap)]
@@ -26,7 +26,7 @@ impl VmThunk {
pub fn new(opcodes: OpCodes) -> VmThunk {
VmThunk {
thunk: RefCell::new(_VmThunk::Code(opcodes)),
lock: RwLock::new(())
lock: RwLock::new(()),
}
}
@@ -37,8 +37,8 @@ impl VmThunk {
_VmThunk::Value(value) => return Ok(value.clone()),
_VmThunk::SuspendedFrom(from) => {
return Err(anyhow!(
"already suspended from {from:p} (infinite recursion encountered)"
))
"already suspended from {from:p} (infinite recursion encountered)"
));
}
_VmThunk::Code(_) => (),
}
@@ -48,9 +48,13 @@ impl VmThunk {
let opcodes = std::mem::replace(
&mut *self.thunk.borrow_mut(),
_VmThunk::SuspendedFrom(self as *const VmThunk),
).unwrap_code();
)
.unwrap_code();
let value = vm.eval(opcodes, env).unwrap();
let _ = std::mem::replace(&mut *self.thunk.borrow_mut(), _VmThunk::Value(value.clone()));
let _ = std::mem::replace(
&mut *self.thunk.borrow_mut(),
_VmThunk::Value(value.clone()),
);
Ok(value)
}
}