From 6ecd20854aef4875db45b36fc2bb6ac341ae0b10 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sun, 4 May 2025 13:58:34 +0800 Subject: [PATCH] feat: function (WIP) --- Cargo.lock | 64 ++++++++++++++++----------------- Cargo.toml | 2 +- src/bytecode.rs | 14 ++++++-- src/compile/compile.rs | 23 +++++++++--- src/compile/ir.rs | 5 +-- src/ty/internal/cnst.rs | 3 -- src/ty/internal/func.rs | 57 +++++++++++++++++++++++++++-- src/ty/internal/mod.rs | 79 +++++++++++++++++++++++++++++++++++------ src/ty/public/cnst.rs | 2 -- src/ty/public/mod.rs | 4 +-- src/vm/vm.rs | 21 +++++++++-- src/vm/vmthunk.rs | 33 +++++++++-------- 12 files changed, 228 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88979a7..7cc6b9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "convert_case" -version = "0.4.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "countme" @@ -126,15 +129,24 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "derive_more" -version = "0.99.17" +version = "2.0.1" 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 = [ "convert_case", "proc-macro2", "quote", - "rustc_version", - "syn 1.0.109", + "syn", + "unicode-xid", ] [[package]] @@ -387,27 +399,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -439,17 +436,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "syn" version = "2.0.58" @@ -494,7 +480,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -509,6 +495,18 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 0fda53c..6990ac6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ itertools = "0.12" rayon = "1.10" tokio = { version = "1.38", features = [ "full" ] } rpds = "1.1" -derive_more = "0.99" +derive_more = { version = "2.0", features = [ "full" ] } crossbeam-channel = "0.5" ecow = "0.2" once_cell = "1.19" diff --git a/src/bytecode.rs b/src/bytecode.rs index 73ec019..11bd434 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,6 +1,6 @@ use ecow::EcoString; -use crate::ty::internal::{Const, Param}; +use crate::ty::internal::Const; type Slice = Box<[T]>; @@ -35,7 +35,17 @@ pub enum OpCode { /// return a value Ret, /// 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 Assert, diff --git a/src/compile/compile.rs b/src/compile/compile.rs index 12649bd..a147f73 100644 --- a/src/compile/compile.rs +++ b/src/compile/compile.rs @@ -406,10 +406,25 @@ impl Compile for ir::Assert { impl Compile for ir::Func { fn compile(self, comp: &mut Compiler) { - let idx = comp.idx(); - comp.push(OpCode::NoOp); - let length = self.body.compile_with_length(comp); - comp.modify(idx, OpCode::Func { param: self.param.into(), length }); + comp.push(OpCode::Func { idx: self.body.idx }); + use ir::Param::*; + match self.param { + 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 }); + } + } + } } } diff --git a/src/compile/ir.rs b/src/compile/ir.rs index 606e523..0d353bb 100644 --- a/src/compile/ir.rs +++ b/src/compile/ir.rs @@ -116,7 +116,7 @@ ir! { UnOp => { rhs: Box, kind: UnOpKind }, Select => { expr: Box, attrpath: Vec, default: Option> }, If => { cond: Box, consq: Box, alter: Box }, - Func => { param: Param, body: Box }, + Func => { param: Param, body: Thunk }, Call => { func: Box, args: Vec }, Let => { attrs: Attrs, expr: Box }, @@ -630,7 +630,8 @@ impl Downgrade for ast::Lambda { fn downgrade(self, state: &mut DowngradeState) -> Result { let body = self.body().unwrap(); 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() } } diff --git a/src/ty/internal/cnst.rs b/src/ty/internal/cnst.rs index 22f79d3..f33f20a 100644 --- a/src/ty/internal/cnst.rs +++ b/src/ty/internal/cnst.rs @@ -3,15 +3,12 @@ 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 for Const { diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 6e904f1..defc542 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,7 +1,7 @@ use ecow::EcoString; -use crate::ty::internal::Value; use crate::bytecode::{OpCodes, ThunkIdx}; +use crate::ty::internal::Value; #[derive(Debug, Clone)] pub enum Param { @@ -15,14 +15,65 @@ pub enum Param { #[derive(Debug, Clone)] pub struct Func { - pub param: Param, - pub opcodes: OpCodes + pub param: Option, + pub opcodes: OpCodes, } impl Func { + pub fn new(opcodes: OpCodes) -> Func { + Func { + param: None, + opcodes, + } + } + pub fn call(self, _arg: Value) -> Value { 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 { diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index fa2ec2f..a995da9 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -39,30 +39,88 @@ pub enum Value { Catchable(c::Catchable), PrimOp(PrimOp), 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; impl Value { pub fn callable(&self) -> bool { match self { - Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Const(Const::Func(_)) => true, + Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, Value::AttrSet(_) => todo!(), _ => false, } } pub fn call(self, args: Vec) -> Value { - use Value as V; + use Value::*; match self { - V::PrimOp(func) => func.call(args), - V::PartialPrimOp(func) => func.call(args), - mut func @ Value::Const(Const::Func(_)) => { + PrimOp(func) => func.call(args), + PartialPrimOp(func) => func.call(args), + mut func @ Value::Func(_) => { let mut iter = args.into_iter(); while let Some(arg) = iter.next() { func = match func { - V::PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), - V::PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), - V::Const(Const::Func(func)) => func.call(arg), + PrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), + PartialPrimOp(func) => return func.call([arg].into_iter().chain(iter).collect()), + Func(func) => func.call(arg), _ => todo!() } } @@ -267,8 +325,9 @@ impl ToPublic for Value { Value::Catchable(catchable) => p::Value::Catchable(catchable), Value::Const(cnst) => p::Value::Const(cnst.into()), Value::Thunk(_) => p::Value::Thunk, - Value::PrimOp(_) => p::Value::PrimOp, - Value::PartialPrimOp(_) => p::Value::PartialPrimOp, + Value::PrimOp(primop) => p::Value::PrimOp(primop.name), + Value::PartialPrimOp(primop) => p::Value::PartialPrimOp(primop.name), + Value::Func(_) => p::Value::Func, } } } diff --git a/src/ty/public/cnst.rs b/src/ty/public/cnst.rs index 3d4788e..1b7044b 100644 --- a/src/ty/public/cnst.rs +++ b/src/ty/public/cnst.rs @@ -10,7 +10,6 @@ pub enum Const { Int(i64), Float(f64), String(EcoString), - Func } impl From for Const { @@ -21,7 +20,6 @@ impl From for Const { Int(int) => Const::Int(int), Float(float) => Const::Float(float), String(string) => Const::String(string), - Func(_) => Const::Func } } } diff --git a/src/ty/public/mod.rs b/src/ty/public/mod.rs index 38a72d5..d579a3b 100644 --- a/src/ty/public/mod.rs +++ b/src/ty/public/mod.rs @@ -38,6 +38,6 @@ pub enum Value { Catchable(Catchable), Thunk, Func, - PrimOp, - PartialPrimOp, + PrimOp(&'static str), + PartialPrimOp(&'static str), } diff --git a/src/vm/vm.rs b/src/vm/vm.rs index fb4c34c..3dca267 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -1,3 +1,5 @@ +use std::ops::Index; + use anyhow::Result; use crate::builtins::env; @@ -81,8 +83,23 @@ impl VM { let func = stack.pop()?; stack.push(func.call(args))?; } - OpCode::Func { param, length } => { - todo!() + OpCode::Func { idx } => { + 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 => { todo!() diff --git a/src/vm/vmthunk.rs b/src/vm/vmthunk.rs index 5b72812..dba80f1 100644 --- a/src/vm/vmthunk.rs +++ b/src/vm/vmthunk.rs @@ -15,7 +15,7 @@ pub struct VmThunk { lock: RwLock<()>, } -#[derive(IsVariant, Unwrap)] +#[derive(IsVariant, Unwrap, Clone)] enum _VmThunk { Code(OpCodes), 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 { { let _guard = self.lock.read().unwrap(); @@ -43,20 +48,18 @@ 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 _ = std::mem::replace( - &mut *self.thunk.borrow_mut(), - _VmThunk::Value(value.clone()), - ); - Ok(value) - } + 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 _ = std::mem::replace( + &mut *self.thunk.borrow_mut(), + _VmThunk::Value(value.clone()), + ); + Ok(value) } pub fn value(&self) -> Option {