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, StaticBuiltin},12 native::NativeDesc,13 parse::{parse_default_function_call, parse_function_call},14};15use crate::{16 evaluate, evaluate_trivial, gc::TraceBox, typed::Any, Context, ContextBuilder, Result, Val,17};1819pub mod arglike;20pub mod builtin;21pub mod native;22pub mod parse;23242526#[derive(Clone, Copy)]27pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);28impl<'l> CallLocation<'l> {29 30 pub const fn new(loc: &'l ExprLocation) -> Self {31 Self(Some(loc))32 }33}34impl CallLocation<'static> {35 36 pub const fn native() -> Self {37 Self(None)38 }39}404142#[derive(Debug, PartialEq, Trace)]43pub struct FuncDesc {44 45 46 47 48 49 50 51 52 53 pub name: IStr,54 55 56 57 58 59 60 61 62 63 pub ctx: Context,6465 66 pub params: ParamsDesc,67 68 pub body: LocExpr,69}70impl FuncDesc {71 72 pub fn default_body_context(&self) -> Result<Context> {73 parse_default_function_call(self.ctx.clone(), &self.params)74 }7576 77 pub fn call_body_context(78 &self,79 call_ctx: Context,80 args: &dyn ArgsLike,81 tailstrict: bool,82 ) -> Result<Context> {83 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)84 }8586 pub fn evaluate_trivial(&self) -> Option<Val> {87 evaluate_trivial(&self.body)88 }89}909192#[allow(clippy::module_name_repetitions)]93#[derive(Trace, Clone)]94pub enum FuncVal {95 96 Id,97 98 Normal(Cc<FuncDesc>),99 100 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),101 102 Builtin(Cc<TraceBox<dyn Builtin>>),103}104105impl Debug for FuncVal {106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {107 match self {108 Self::Id => f.debug_tuple("Id").finish(),109 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),110 Self::StaticBuiltin(arg0) => {111 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()112 }113 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),114 }115 }116}117118impl FuncVal {119 120 pub fn params_len(&self) -> usize {121 match self {122 Self::Id => 1,123 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),124 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),125 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),126 }127 }128 129 pub fn name(&self) -> IStr {130 match self {131 Self::Id => "id".into(),132 Self::Normal(normal) => normal.name.clone(),133 Self::StaticBuiltin(builtin) => builtin.name().into(),134 Self::Builtin(builtin) => builtin.name().into(),135 }136 }137 138 139 140 pub fn evaluate(141 &self,142 call_ctx: Context,143 loc: CallLocation<'_>,144 args: &dyn ArgsLike,145 tailstrict: bool,146 ) -> Result<Val> {147 match self {148 Self::Id => {149 #[allow(clippy::unnecessary_wraps)]150 #[builtin]151 const fn builtin_id(v: Any) -> Result<Any> {152 Ok(v)153 }154 static ID: &builtin_id = &builtin_id {};155156 ID.call(call_ctx, loc, args)157 }158 Self::Normal(func) => {159 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;160 evaluate(body_ctx, &func.body)161 }162 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),163 Self::Builtin(b) => b.call(call_ctx, loc, args),164 }165 }166 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(&self, args: &A) -> Result<Val> {167 self.evaluate(168 ContextBuilder::dangerous_empty_state().build(),169 CallLocation::native(),170 args,171 true,172 )173 }174 175 pub fn into_native<D: NativeDesc>(self) -> D::Value {176 D::into_native(self)177 }178179 180 181 182 183 184 pub fn is_identity(&self) -> bool {185 match self {186 Self::Id => true,187 Self::Normal(desc) => {188 if desc.params.len() != 1 {189 return false;190 }191 let param = &desc.params[0];192 if param.1.is_some() {193 return false;194 }195 #[allow(clippy::infallible_destructuring_match)]196 let id = match ¶m.0 {197 Destruct::Full(id) => id,198 #[cfg(feature = "exp-destruct")]199 _ => return false,200 };201 &desc.body.0 as &Expr == &Expr::Var(id.clone())202 }203 _ => false,204 }205 }206 207 pub const fn identity() -> Self {208 Self::Id209 }210211 pub fn evaluate_trivial(&self) -> Option<Val> {212 match self {213 FuncVal::Normal(n) => n.evaluate_trivial(),214 _ => None,215 }216 }217}