1use std::fmt::Debug;23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_parser::{Destruct, Expr, ExprLocation, LocExpr, ParamsDesc};89use self::{10 arglike::OptionalContext,11 builtin::{Builtin, BuiltinParam, ParamName, StaticBuiltin},12 native::NativeDesc,13 parse::{parse_default_function_call, parse_function_call},14};15use crate::{16 evaluate, evaluate_trivial, gc::TraceBox, tb, Context, ContextBuilder, Result,17 Val,18};1920pub mod arglike;21pub mod builtin;22pub mod native;23pub mod parse;24252627#[derive(Clone, Copy)]28pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);29impl<'l> CallLocation<'l> {30 31 pub const fn new(loc: &'l ExprLocation) -> Self {32 Self(Some(loc))33 }34}35impl CallLocation<'static> {36 37 pub const fn native() -> Self {38 Self(None)39 }40}414243#[derive(Debug, PartialEq, Trace)]44pub struct FuncDesc {45 46 47 48 49 50 51 52 53 54 pub name: IStr,55 56 57 58 59 60 61 62 63 64 pub ctx: Context,6566 67 pub params: ParamsDesc,68 69 pub body: LocExpr,70}71impl FuncDesc {72 73 pub fn default_body_context(&self) -> Result<Context> {74 parse_default_function_call(self.ctx.clone(), &self.params)75 }7677 78 pub fn call_body_context(79 &self,80 call_ctx: Context,81 args: &dyn ArgsLike,82 tailstrict: bool,83 ) -> Result<Context> {84 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)85 }8687 pub fn evaluate_trivial(&self) -> Option<Val> {88 evaluate_trivial(&self.body)89 }90}919293#[allow(clippy::module_name_repetitions)]94#[derive(Trace, Clone)]95pub enum FuncVal {96 97 Id,98 99 Normal(Cc<FuncDesc>),100 101 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),102 103 Builtin(Cc<TraceBox<dyn Builtin>>),104}105106impl Debug for FuncVal {107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {108 match self {109 Self::Id => f.debug_tuple("Id").finish(),110 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),111 Self::StaticBuiltin(arg0) => {112 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()113 }114 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),115 }116 }117}118119#[allow(clippy::unnecessary_wraps)]120#[builtin]121const fn builtin_id(x: Val) -> Val {122 x123}124static ID: &builtin_id = &builtin_id {};125126impl FuncVal {127 pub fn builtin(builtin: impl Builtin) -> Self {128 Self::Builtin(Cc::new(tb!(builtin)))129 }130 pub fn static_builtin(static_builtin: &'static dyn StaticBuiltin) -> Self {131 Self::StaticBuiltin(static_builtin)132 }133134 pub fn params(&self) -> Vec<BuiltinParam> {135 match self {136 Self::Id => ID.params().to_vec(),137 Self::StaticBuiltin(i) => i.params().to_vec(),138 Self::Builtin(i) => i.params().to_vec(),139 Self::Normal(p) => p140 .params141 .iter()142 .map(|p| {143 BuiltinParam::new(144 p.0.name()145 .as_ref()146 .map(IStr::to_string)147 .map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),148 p.1.is_some(),149 )150 })151 .collect(),152 }153 }154 155 pub fn params_len(&self) -> usize {156 match self {157 Self::Id => 1,158 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),159 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),160 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),161 }162 }163 164 pub fn name(&self) -> IStr {165 match self {166 Self::Id => "id".into(),167 Self::Normal(normal) => normal.name.clone(),168 Self::StaticBuiltin(builtin) => builtin.name().into(),169 Self::Builtin(builtin) => builtin.name().into(),170 }171 }172 173 174 175 pub fn evaluate(176 &self,177 call_ctx: Context,178 loc: CallLocation<'_>,179 args: &dyn ArgsLike,180 tailstrict: bool,181 ) -> Result<Val> {182 match self {183 Self::Id => ID.call(call_ctx, loc, args),184 Self::Normal(func) => {185 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;186 evaluate(body_ctx, &func.body)187 }188 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),189 Self::Builtin(b) => b.call(call_ctx, loc, args),190 }191 }192 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(193 &self,194 args: &A,195 tailstrict: bool,196 ) -> Result<Val> {197 self.evaluate(198 ContextBuilder::dangerous_empty_state().build(),199 CallLocation::native(),200 args,201 tailstrict,202 )203 }204 205 pub fn into_native<D: NativeDesc>(self) -> D::Value {206 D::into_native(self)207 }208209 210 211 212 213 214 pub fn is_identity(&self) -> bool {215 match self {216 Self::Id => true,217 Self::Normal(desc) => {218 if desc.params.len() != 1 {219 return false;220 }221 let param = &desc.params[0];222 if param.1.is_some() {223 return false;224 }225 #[allow(clippy::infallible_destructuring_match)]226 let id = match ¶m.0 {227 Destruct::Full(id) => id,228 #[cfg(feature = "exp-destruct")]229 _ => return false,230 };231 &desc.body.0 as &Expr == &Expr::Var(id.clone())232 }233 _ => false,234 }235 }236 237 pub const fn identity() -> Self {238 Self::Id239 }240241 pub fn evaluate_trivial(&self) -> Option<Val> {242 match self {243 FuncVal::Normal(n) => n.evaluate_trivial(),244 _ => None,245 }246 }247}248249impl<T> From<T> for FuncVal250where251 T: Builtin,252{253 fn from(value: T) -> Self {254 Self::builtin(value)255 }256}257impl From<&'static dyn StaticBuiltin> for FuncVal {258 fn from(value: &'static dyn StaticBuiltin) -> Self {259 Self::static_builtin(value)260 }261}