difftreelog
refactor drop default args parser
in: master
2 files changed
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth1use std::{fmt::Debug, rc::Rc};23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use jrsonnet_ir::Span;7pub use jrsonnet_macros::builtin;89use self::{10 builtin::Builtin,11 prepared::{PreparedCall, parse_prepared_builtin_call},12};13use crate::{14 Context, ContextBuilder, Result, Thunk, Val,15 analyze::{LDestruct, LExpr, LFunction},16 evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},17 function::builtin::BuiltinFunc,18};1920pub mod builtin;21mod native;22mod parse;23pub(crate) mod prepared;2425pub use jrsonnet_ir::function::*;26pub use native::NativeFn;27pub(crate) use prepared::PreparedFuncVal;2829/// Function callsite location.30/// Either from other jsonnet code, specified by expression location, or from native (without location).31#[derive(Clone, Copy)]32pub struct CallLocation<'l>(pub Option<&'l Span>);33impl<'l> CallLocation<'l> {34 /// Construct new location for calls coming from specified jsonnet expression location.35 pub const fn new(loc: &'l Span) -> Self {36 Self(Some(loc))37 }38}39impl CallLocation<'static> {40 /// Construct new location for calls coming from native code.41 pub const fn native() -> Self {42 Self(None)43 }44}4546/// Represents Jsonnet function defined in code.47#[derive(Trace, Educe)]48#[educe(Debug, PartialEq)]49pub struct FuncDesc {50 /// # Example51 ///52 /// In expressions like this, deducted to `a`, unspecified otherwise.53 /// ```jsonnet54 /// local a = function() ...55 /// local a() ...56 /// { a: function() ... }57 /// { a() = ... }58 /// ```59 pub name: IStr,60 /// Context, in which this function was evaluated.61 ///62 /// # Example63 /// In64 /// ```jsonnet65 /// local a = 2;66 /// function() ...67 /// ```68 /// context will contain `a`.69 pub ctx: Context,7071 #[educe(PartialEq(method = Rc::ptr_eq))]72 pub func: Rc<LFunction>,73}7475impl FuncDesc {76 pub fn signature(&self) -> FunctionSignature {77 self.func.signature.clone()78 }7980 pub fn call(81 &self,82 unnamed: &[Thunk<Val>],83 named: &[Thunk<Val>],84 prepared: &PreparedCall,85 ) -> Result<Val> {86 let has_defaults = !prepared.defaults().is_empty();87 let mut builder = ContextBuilder::extend(self.ctx.clone(), self.func.params.len());8889 let fctx = Context::new_future();90 for (param_idx, thunk) in unnamed.iter().enumerate() {91 destruct(92 &self.func.params[param_idx].destruct,93 thunk.clone(),94 fctx.clone(),95 &mut builder,96 );97 }9899 for &(param_idx, arg_idx) in prepared.named() {100 destruct(101 &self.func.params[param_idx].destruct,102 named[arg_idx].clone(),103 fctx.clone(),104 &mut builder,105 );106 }107108 if has_defaults {109 for ¶m_idx in prepared.defaults() {110 let param = &self.func.params[param_idx];111 if let Some(default_expr) = ¶m.default {112 let default_expr = default_expr.clone();113 let fctxc = fctx.clone();114 let thunk = Thunk!(move || {115 let ctx = fctxc.unwrap();116 evaluate(ctx, &default_expr)117 });118 destruct(¶m.destruct, thunk, fctx.clone(), &mut builder);119 }120 }121 };122 let ctx = builder.build().into_future(fctx);123 ensure_sufficient_stack(|| evaluate(ctx, &self.func.body))124 }125126 pub fn evaluate_trivial(&self) -> Option<Val> {127 evaluate_trivial(&self.func.body)128 }129}130131/// Represents a Jsonnet function value, including plain functions and user-provided builtins.132#[allow(clippy::module_name_repetitions)]133#[derive(Trace, Clone)]134pub enum FuncVal {135 /// Plain function implemented in jsonnet.136 Normal(Cc<FuncDesc>),137 /// User-provided function.138 Builtin(BuiltinFunc),139}140141impl Debug for FuncVal {142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {143 match self {144 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),145 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),146 }147 }148}149150#[allow(clippy::unnecessary_wraps)]151#[builtin]152pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {153 x154}155156impl FuncVal {157 pub fn builtin(builtin: impl Builtin) -> Self {158 Self::Builtin(BuiltinFunc::new(builtin))159 }160161 pub fn params(&self) -> FunctionSignature {162 match self {163 Self::Builtin(i) => i.params(),164 Self::Normal(p) => p.signature(),165 }166 }167 /// Amount of non-default required arguments168 pub fn params_len(&self) -> u32 {169 self.params().iter().filter(|p| !p.has_default()).count() as u32170 }171 /// Function name, as defined in code.172 pub fn name(&self) -> IStr {173 match self {174 Self::Normal(normal) => normal.name.clone(),175 Self::Builtin(builtin) => builtin.name().into(),176 }177 }178179 pub(crate) fn evaluate_prepared(180 &self,181 prepared: &PreparedCall,182 loc: CallLocation<'_>,183 unnamed: &[Thunk<Val>],184 named: &[Thunk<Val>],185 _tailstrict: bool,186 ) -> Result<Val> {187 match self {188 FuncVal::Normal(func) => func.call(unnamed, named, prepared),189 FuncVal::Builtin(b) => {190 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);191 b.call(loc, &args)192 }193 }194 }195196 /// Is this function an identity function.197 ///198 /// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.199 ///200 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too201 pub fn is_identity(&self) -> bool {202 match self {203 Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),204 Self::Normal(desc) => {205 if desc.func.params.len() != 1 {206 return false;207 }208 let param = &desc.func.params[0];209 if param.default.is_some() {210 return false;211 }212 #[allow(irrefutable_let_patterns, reason = "refutable with exp-destruct")]213 let LDestruct::Full(id) = ¶m.destruct else {214 return false;215 };216 matches!(&*desc.func.body, LExpr::Local(v) if v == id)217 }218 }219 }220221 pub fn evaluate_trivial(&self) -> Option<Val> {222 match self {223 Self::Normal(n) => n.evaluate_trivial(),224 Self::Builtin(_) => None,225 }226 }227}228229impl<T> From<T> for FuncVal230where231 T: Builtin,232{233 fn from(value: T) -> Self {234 Self::builtin(value)235 }236}1use std::{fmt::Debug, rc::Rc};23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use jrsonnet_ir::Span;7pub use jrsonnet_macros::builtin;89use self::{10 builtin::Builtin,11 prepared::{PreparedCall, parse_prepared_builtin_call},12};13use crate::{14 Context, ContextBuilder, Result, Thunk, Val,15 analyze::{LDestruct, LExpr, LFunction},16 evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},17 function::builtin::BuiltinFunc,18};1920pub mod builtin;21mod native;22pub(crate) mod prepared;2324pub use jrsonnet_ir::function::*;25pub use native::NativeFn;26pub(crate) use prepared::PreparedFuncVal;2728/// Function callsite location.29/// Either from other jsonnet code, specified by expression location, or from native (without location).30#[derive(Clone, Copy)]31pub struct CallLocation<'l>(pub Option<&'l Span>);32impl<'l> CallLocation<'l> {33 /// Construct new location for calls coming from specified jsonnet expression location.34 pub const fn new(loc: &'l Span) -> Self {35 Self(Some(loc))36 }37}38impl CallLocation<'static> {39 /// Construct new location for calls coming from native code.40 pub const fn native() -> Self {41 Self(None)42 }43}4445/// Represents Jsonnet function defined in code.46#[derive(Trace, Educe)]47#[educe(Debug, PartialEq)]48pub struct FuncDesc {49 /// # Example50 ///51 /// In expressions like this, deducted to `a`, unspecified otherwise.52 /// ```jsonnet53 /// local a = function() ...54 /// local a() ...55 /// { a: function() ... }56 /// { a() = ... }57 /// ```58 pub name: IStr,59 /// Context, in which this function was evaluated.60 ///61 /// # Example62 /// In63 /// ```jsonnet64 /// local a = 2;65 /// function() ...66 /// ```67 /// context will contain `a`.68 pub ctx: Context,6970 #[educe(PartialEq(method = Rc::ptr_eq))]71 pub func: Rc<LFunction>,72}7374impl FuncDesc {75 pub fn signature(&self) -> FunctionSignature {76 self.func.signature.clone()77 }7879 pub fn call(80 &self,81 unnamed: &[Thunk<Val>],82 named: &[Thunk<Val>],83 prepared: &PreparedCall,84 ) -> Result<Val> {85 let has_defaults = !prepared.defaults().is_empty();86 let mut builder = ContextBuilder::extend(self.ctx.clone(), self.func.params.len());8788 let fctx = Context::new_future();89 for (param_idx, thunk) in unnamed.iter().enumerate() {90 destruct(91 &self.func.params[param_idx].destruct,92 thunk.clone(),93 fctx.clone(),94 &mut builder,95 );96 }9798 for &(param_idx, arg_idx) in prepared.named() {99 destruct(100 &self.func.params[param_idx].destruct,101 named[arg_idx].clone(),102 fctx.clone(),103 &mut builder,104 );105 }106107 if has_defaults {108 for ¶m_idx in prepared.defaults() {109 let param = &self.func.params[param_idx];110 if let Some(default_expr) = ¶m.default {111 let default_expr = default_expr.clone();112 let fctxc = fctx.clone();113 let thunk = Thunk!(move || {114 let ctx = fctxc.unwrap();115 evaluate(ctx, &default_expr)116 });117 destruct(¶m.destruct, thunk, fctx.clone(), &mut builder);118 }119 }120 };121 let ctx = builder.build().into_future(fctx);122 ensure_sufficient_stack(|| evaluate(ctx, &self.func.body))123 }124125 pub fn evaluate_trivial(&self) -> Option<Val> {126 evaluate_trivial(&self.func.body)127 }128}129130/// Represents a Jsonnet function value, including plain functions and user-provided builtins.131#[allow(clippy::module_name_repetitions)]132#[derive(Trace, Clone)]133pub enum FuncVal {134 /// Plain function implemented in jsonnet.135 Normal(Cc<FuncDesc>),136 /// User-provided function.137 Builtin(BuiltinFunc),138}139140impl Debug for FuncVal {141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {142 match self {143 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),144 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),145 }146 }147}148149#[allow(clippy::unnecessary_wraps)]150#[builtin]151pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {152 x153}154155impl FuncVal {156 pub fn builtin(builtin: impl Builtin) -> Self {157 Self::Builtin(BuiltinFunc::new(builtin))158 }159160 pub fn params(&self) -> FunctionSignature {161 match self {162 Self::Builtin(i) => i.params(),163 Self::Normal(p) => p.signature(),164 }165 }166 /// Amount of non-default required arguments167 pub fn params_len(&self) -> u32 {168 self.params().iter().filter(|p| !p.has_default()).count() as u32169 }170 /// Function name, as defined in code.171 pub fn name(&self) -> IStr {172 match self {173 Self::Normal(normal) => normal.name.clone(),174 Self::Builtin(builtin) => builtin.name().into(),175 }176 }177178 pub(crate) fn evaluate_prepared(179 &self,180 prepared: &PreparedCall,181 loc: CallLocation<'_>,182 unnamed: &[Thunk<Val>],183 named: &[Thunk<Val>],184 _tailstrict: bool,185 ) -> Result<Val> {186 match self {187 FuncVal::Normal(func) => func.call(unnamed, named, prepared),188 FuncVal::Builtin(b) => {189 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);190 b.call(loc, &args)191 }192 }193 }194195 /// Is this function an identity function.196 ///197 /// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.198 ///199 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too200 pub fn is_identity(&self) -> bool {201 match self {202 Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),203 Self::Normal(desc) => {204 if desc.func.params.len() != 1 {205 return false;206 }207 let param = &desc.func.params[0];208 if param.default.is_some() {209 return false;210 }211 #[allow(irrefutable_let_patterns, reason = "refutable with exp-destruct")]212 let LDestruct::Full(id) = ¶m.destruct else {213 return false;214 };215 matches!(&*desc.func.body, LExpr::Local(v) if v == id)216 }217 }218 }219220 pub fn evaluate_trivial(&self) -> Option<Val> {221 match self {222 Self::Normal(n) => n.evaluate_trivial(),223 Self::Builtin(_) => None,224 }225 }226}227228impl<T> From<T> for FuncVal229where230 T: Builtin,231{232 fn from(value: T) -> Self {233 Self::builtin(value)234 }235}crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use std::rc::Rc;
-
-use crate::{
- Context, ContextBuilder, Result, Thunk,
- analyze::LFunction,
- evaluate::{destructure::destruct, evaluate},
-};
-
-/// Creates Context with all argument default values applied
-/// and with unbound values causing error to be returned.
-pub fn parse_default_function_call(body_ctx: Context, func: &Rc<LFunction>) -> Result<Context> {
- let fctx = Context::new_future();
- let mut builder = ContextBuilder::extend(body_ctx, func.params.len());
-
- for param in &func.params {
- if let Some(default_expr) = ¶m.default {
- let default_expr = default_expr.clone();
- let fctxc = fctx.clone();
- let thunk = Thunk!(move || {
- let ctx = fctxc.unwrap();
- evaluate(ctx, &default_expr)
- });
- destruct(¶m.destruct, thunk, fctx.clone(), &mut builder);
- } else {
- let name = param.name.clone().unwrap_or_else(|| "<param>".into());
- let thunk = Thunk::errored(
- crate::error::ErrorKind::FunctionParameterNotBoundInCall(
- jrsonnet_ir::function::ParamName::Named(name),
- jrsonnet_ir::function::FunctionSignature::empty(),
- )
- .into(),
- );
- destruct(¶m.destruct, thunk, fctx.clone(), &mut builder);
- }
- }
-
- let ctx = builder.build().into_future(fctx);
- Ok(ctx)
-}