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::{parse_prepared_builtin_call, PreparedCall},12};13use crate::{14 analyze::{LDestruct, LExpr, LFunction},15 evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},16 function::builtin::BuiltinFunc,17 Context, ContextBuilder, Result, Thunk, Val,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;28293031#[derive(Clone, Copy)]32pub struct CallLocation<'l>(pub Option<&'l Span>);33impl<'l> CallLocation<'l> {34 35 pub const fn new(loc: &'l Span) -> Self {36 Self(Some(loc))37 }38}39impl CallLocation<'static> {40 41 pub const fn native() -> Self {42 Self(None)43 }44}454647#[derive(Trace, Educe)]48#[educe(Debug, PartialEq)]49pub struct FuncDesc {50 51 52 53 54 55 56 57 58 59 pub name: IStr,60 61 62 63 64 65 66 67 68 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}130131132#[allow(clippy::module_name_repetitions)]133#[derive(Trace, Clone)]134pub enum FuncVal {135 136 Normal(Cc<FuncDesc>),137 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 168 pub fn params_len(&self) -> u32 {169 self.params().iter().filter(|p| !p.has_default()).count() as u32170 }171 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 197 198 199 200 201 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.destruct214 else {215 return false;216 };217 matches!(&*desc.func.body, LExpr::Local(v) if v == id)218 }219 }220 }221222 pub fn evaluate_trivial(&self) -> Option<Val> {223 match self {224 Self::Normal(n) => n.evaluate_trivial(),225 Self::Builtin(_) => None,226 }227 }228}229230impl<T> From<T> for FuncVal231where232 T: Builtin,233{234 fn from(value: T) -> Self {235 Self::builtin(value)236 }237}