feat: function (WIP)

This commit is contained in:
2025-05-04 13:58:34 +08:00
parent 63fd380514
commit 6ecd20854a
12 changed files with 228 additions and 79 deletions

64
Cargo.lock generated
View File

@@ -80,9 +80,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.4.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "countme" name = "countme"
@@ -126,15 +129,24 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustc_version", "syn",
"syn 1.0.109", "unicode-xid",
] ]
[[package]] [[package]]
@@ -387,27 +399,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.2" version = "1.4.2"
@@ -439,17 +436,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.58" version = "2.0.58"
@@ -494,7 +480,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.58", "syn",
] ]
[[package]] [[package]]
@@ -509,6 +495,18 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View File

@@ -12,7 +12,7 @@ itertools = "0.12"
rayon = "1.10" rayon = "1.10"
tokio = { version = "1.38", features = [ "full" ] } tokio = { version = "1.38", features = [ "full" ] }
rpds = "1.1" rpds = "1.1"
derive_more = "0.99" derive_more = { version = "2.0", features = [ "full" ] }
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
ecow = "0.2" ecow = "0.2"
once_cell = "1.19" once_cell = "1.19"

View File

@@ -1,6 +1,6 @@
use ecow::EcoString; use ecow::EcoString;
use crate::ty::internal::{Const, Param}; use crate::ty::internal::Const;
type Slice<T> = Box<[T]>; type Slice<T> = Box<[T]>;
@@ -35,7 +35,17 @@ pub enum OpCode {
/// return a value /// return a value
Ret, Ret,
/// make a function /// make a function
Func { param: Param, length: usize }, Func { idx: ThunkIdx },
/// push param `sym` into TOS
PushIdentParam { sym: EcoString },
/// push formal param `sym` into TOS
PushFormalParam { sym: EcoString },
/// push default formal param with thunkidx `idx` into TOS
PushDefaultParam { idx: ThunkIdx },
/// TODO:
SetEllipsis,
/// TODO:
SetAlias { sym: EcoString },
/// consume 1 element, assert TOS is true /// consume 1 element, assert TOS is true
Assert, Assert,

View File

@@ -406,10 +406,25 @@ impl Compile for ir::Assert {
impl Compile for ir::Func { impl Compile for ir::Func {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
let idx = comp.idx(); comp.push(OpCode::Func { idx: self.body.idx });
comp.push(OpCode::NoOp); use ir::Param::*;
let length = self.body.compile_with_length(comp); match self.param {
comp.modify(idx, OpCode::Func { param: self.param.into(), length }); Ident(sym) => comp.push(OpCode::PushIdentParam { sym }),
Formals { formals, ellipsis, alias } => {
for (sym, default) in formals {
comp.push(OpCode::PushFormalParam { sym });
if let Some(ir::Thunk { idx }) = default {
comp.push(OpCode::PushDefaultParam { idx });
}
}
if ellipsis {
comp.push(OpCode::SetEllipsis);
}
if let Some(sym) = alias {
comp.push(OpCode::SetAlias { sym });
}
}
}
} }
} }

View File

@@ -116,7 +116,7 @@ ir! {
UnOp => { rhs: Box<Ir>, kind: UnOpKind }, UnOp => { rhs: Box<Ir>, kind: UnOpKind },
Select => { expr: Box<Ir>, attrpath: Vec<Attr>, default: Option<Box<Ir>> }, Select => { expr: Box<Ir>, attrpath: Vec<Attr>, default: Option<Box<Ir>> },
If => { cond: Box<Ir>, consq: Box<Ir>, alter: Box<Ir> }, If => { cond: Box<Ir>, consq: Box<Ir>, alter: Box<Ir> },
Func => { param: Param, body: Box<Ir> }, Func => { param: Param, body: Thunk },
Call => { func: Box<Ir>, args: Vec<Ir> }, Call => { func: Box<Ir>, args: Vec<Ir> },
Let => { attrs: Attrs, expr: Box<Ir> }, Let => { attrs: Attrs, expr: Box<Ir> },
@@ -630,7 +630,8 @@ impl Downgrade for ast::Lambda {
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> { fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
let body = self.body().unwrap(); let body = self.body().unwrap();
let param = downgrade_param(self.param().unwrap(), state)?; let param = downgrade_param(self.param().unwrap(), state)?;
let body = body.downgrade(state)?.boxed(); let body = body.downgrade(state)?;
let body = state.new_thunk(body);
Func { param, body }.ir().ok() Func { param, body }.ir().ok()
} }
} }

View File

@@ -3,15 +3,12 @@ use derive_more::{IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
use super::Func;
#[derive(Debug, Clone, IsVariant, Unwrap)] #[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const { pub enum Const {
Bool(bool), Bool(bool),
Int(i64), Int(i64),
Float(f64), Float(f64),
String(EcoString), String(EcoString),
Func(Func),
} }
impl From<bool> for Const { impl From<bool> for Const {

View File

@@ -1,7 +1,7 @@
use ecow::EcoString; use ecow::EcoString;
use crate::ty::internal::Value;
use crate::bytecode::{OpCodes, ThunkIdx}; use crate::bytecode::{OpCodes, ThunkIdx};
use crate::ty::internal::Value;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Param { pub enum Param {
@@ -15,14 +15,65 @@ pub enum Param {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Func { pub struct Func {
pub param: Param, pub param: Option<Param>,
pub opcodes: OpCodes pub opcodes: OpCodes,
} }
impl Func { impl Func {
pub fn new(opcodes: OpCodes) -> Func {
Func {
param: None,
opcodes,
}
}
pub fn call(self, _arg: Value) -> Value { pub fn call(self, _arg: Value) -> Value {
todo!() todo!()
} }
pub fn push_ident_param(&mut self, param: EcoString) {
self.param = Some(Param::Ident(param))
}
pub fn push_formal_param(&mut self, param: EcoString) {
let Param::Formals { formals, .. } = self.param.get_or_insert_with(|| Param::Formals {
formals: Vec::with_capacity(1),
ellipsis: false,
alias: None,
}) else {
panic!()
};
formals.push((param, None));
}
pub fn push_default_param(&mut self, default: ThunkIdx) {
let Param::Formals { formals, .. } = self.param.as_mut().unwrap() else {
panic!()
};
formals.last_mut().unwrap().1 = Some(default)
}
pub fn set_ellipsis(&mut self) {
let Param::Formals { ellipsis, .. } = self.param.get_or_insert_with(|| Param::Formals {
formals: Vec::new(),
ellipsis: false,
alias: None,
}) else {
panic!()
};
*ellipsis = true;
}
pub fn set_alias(&mut self, sym: EcoString) {
let Param::Formals { alias, .. } = self.param.get_or_insert_with(|| Param::Formals {
formals: Vec::new(),
ellipsis: false,
alias: None,
}) else {
panic!()
};
*alias = Some(sym);
}
} }
impl PartialEq for Func { impl PartialEq for Func {

View File

@@ -39,30 +39,88 @@ pub enum Value {
Catchable(c::Catchable), Catchable(c::Catchable),
PrimOp(PrimOp), PrimOp(PrimOp),
PartialPrimOp(PartialPrimOp), PartialPrimOp(PartialPrimOp),
Func(Func),
// FuncWithEnv(Func)
}
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)]
pub enum ValueAsRef<'a> {
Const(&'a Const),
Thunk(&'a Thunk),
AttrSet(&'a AttrSet),
List(&'a List),
Catchable(&'a c::Catchable),
PrimOp(&'a PrimOp),
PartialPrimOp(&'a PartialPrimOp),
Func(&'a Func),
}
#[derive(Debug, IsVariant, Unwrap, PartialEq)]
pub enum ValueAsMut<'a> {
Const(&'a mut Const),
Thunk(&'a mut Thunk),
AttrSet(&'a mut AttrSet),
List(&'a mut List),
Catchable(&'a mut c::Catchable),
PrimOp(&'a mut PrimOp),
PartialPrimOp(&'a mut PartialPrimOp),
Func(&'a mut Func),
}
impl Value {
pub fn as_ref(&self) -> ValueAsRef<'_> {
use Value::*;
use ValueAsRef as R;
match self {
Const(x) => R::Const(x),
Thunk(x) => R::Thunk(x),
AttrSet(x) => R::AttrSet(x),
List(x) => R::List(x),
Catchable(x) => R::Catchable(x),
PrimOp(x) => R::PrimOp(x),
PartialPrimOp(x) => R::PartialPrimOp(x),
Func(x) => R::Func(x),
}
}
pub fn as_mut(&mut self) -> ValueAsMut<'_> {
use Value::*;
use ValueAsMut as M;
match self {
Const(x) => M::Const(x),
Thunk(x) => M::Thunk(x),
AttrSet(x) => M::AttrSet(x),
List(x) => M::List(x),
Catchable(x) => M::Catchable(x),
PrimOp(x) => M::PrimOp(x),
PartialPrimOp(x) => M::PartialPrimOp(x),
Func(x) => M::Func(x),
}
}
} }
use Value::Const as VmConst; use Value::Const as VmConst;
impl Value { impl Value {
pub fn callable(&self) -> bool { pub fn callable(&self) -> bool {
match self { match self {
Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Const(Const::Func(_)) => true, Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true,
Value::AttrSet(_) => todo!(), Value::AttrSet(_) => todo!(),
_ => false, _ => false,
} }
} }
pub fn call(self, args: Vec<Value>) -> Value { pub fn call(self, args: Vec<Value>) -> Value {
use Value as V; use Value::*;
match self { match self {
V::PrimOp(func) => func.call(args), PrimOp(func) => func.call(args),
V::PartialPrimOp(func) => func.call(args), PartialPrimOp(func) => func.call(args),
mut func @ Value::Const(Const::Func(_)) => { mut func @ Value::Func(_) => {
let mut iter = args.into_iter(); let mut iter = args.into_iter();
while let Some(arg) = iter.next() { while let Some(arg) = iter.next() {
func = match func { func = match func {
V::PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()),
V::PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()),
V::Const(Const::Func(func)) => func.call(arg), Func(func) => func.call(arg),
_ => todo!() _ => todo!()
} }
} }
@@ -267,8 +325,9 @@ impl ToPublic for Value {
Value::Catchable(catchable) => p::Value::Catchable(catchable), Value::Catchable(catchable) => p::Value::Catchable(catchable),
Value::Const(cnst) => p::Value::Const(cnst.into()), Value::Const(cnst) => p::Value::Const(cnst.into()),
Value::Thunk(_) => p::Value::Thunk, Value::Thunk(_) => p::Value::Thunk,
Value::PrimOp(_) => p::Value::PrimOp, Value::PrimOp(primop) => p::Value::PrimOp(primop.name),
Value::PartialPrimOp(_) => p::Value::PartialPrimOp, Value::PartialPrimOp(primop) => p::Value::PartialPrimOp(primop.name),
Value::Func(_) => p::Value::Func,
} }
} }
} }

View File

@@ -10,7 +10,6 @@ pub enum Const {
Int(i64), Int(i64),
Float(f64), Float(f64),
String(EcoString), String(EcoString),
Func
} }
impl From<i::Const> for Const { impl From<i::Const> for Const {
@@ -21,7 +20,6 @@ impl From<i::Const> for Const {
Int(int) => Const::Int(int), Int(int) => Const::Int(int),
Float(float) => Const::Float(float), Float(float) => Const::Float(float),
String(string) => Const::String(string), String(string) => Const::String(string),
Func(_) => Const::Func
} }
} }
} }

View File

@@ -38,6 +38,6 @@ pub enum Value {
Catchable(Catchable), Catchable(Catchable),
Thunk, Thunk,
Func, Func,
PrimOp, PrimOp(&'static str),
PartialPrimOp, PartialPrimOp(&'static str),
} }

View File

@@ -1,3 +1,5 @@
use std::ops::Index;
use anyhow::Result; use anyhow::Result;
use crate::builtins::env; use crate::builtins::env;
@@ -81,8 +83,23 @@ impl VM {
let func = stack.pop()?; let func = stack.pop()?;
stack.push(func.call(args))?; stack.push(func.call(args))?;
} }
OpCode::Func { param, length } => { OpCode::Func { idx } => {
todo!() stack.push(Value::Func(Func::new(self.thunks[idx].unwrap_code())))?;
}
OpCode::PushIdentParam { sym } => {
stack.tos_mut()?.as_mut().unwrap_func().push_ident_param(sym);
}
OpCode::PushFormalParam { sym } => {
stack.tos_mut()?.as_mut().unwrap_func().push_formal_param(sym);
}
OpCode::PushDefaultParam { idx } => {
stack.tos_mut()?.as_mut().unwrap_func().push_default_param(idx);
}
OpCode::SetEllipsis => {
stack.tos_mut()?.as_mut().unwrap_func().set_ellipsis();
}
OpCode::SetAlias { sym } => {
stack.tos_mut()?.as_mut().unwrap_func().set_alias(sym);
} }
OpCode::Ret => { OpCode::Ret => {
todo!() todo!()

View File

@@ -15,7 +15,7 @@ pub struct VmThunk {
lock: RwLock<()>, lock: RwLock<()>,
} }
#[derive(IsVariant, Unwrap)] #[derive(IsVariant, Unwrap, Clone)]
enum _VmThunk { enum _VmThunk {
Code(OpCodes), Code(OpCodes),
SuspendedFrom(*const VmThunk), SuspendedFrom(*const VmThunk),
@@ -30,6 +30,11 @@ impl VmThunk {
} }
} }
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<Value> { pub fn force(&self, vm: &VM, env: &mut Env) -> Result<Value> {
{ {
let _guard = self.lock.read().unwrap(); let _guard = self.lock.read().unwrap();
@@ -43,20 +48,18 @@ impl VmThunk {
_VmThunk::Code(_) => (), _VmThunk::Code(_) => (),
} }
} }
{ let _guard = self.lock.write().unwrap();
let _guard = self.lock.write().unwrap(); let opcodes = std::mem::replace(
let opcodes = std::mem::replace( &mut *self.thunk.borrow_mut(),
&mut *self.thunk.borrow_mut(), _VmThunk::SuspendedFrom(self as *const VmThunk),
_VmThunk::SuspendedFrom(self as *const VmThunk), )
) .unwrap_code();
.unwrap_code(); let value = vm.eval(opcodes, env).unwrap();
let value = vm.eval(opcodes, env).unwrap(); let _ = std::mem::replace(
let _ = std::mem::replace( &mut *self.thunk.borrow_mut(),
&mut *self.thunk.borrow_mut(), _VmThunk::Value(value.clone()),
_VmThunk::Value(value.clone()), );
); Ok(value)
Ok(value)
}
} }
pub fn value(&self) -> Option<Value> { pub fn value(&self) -> Option<Value> {