1use std::{fmt::Debug, rc::Rc};23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use educe::Educe;5use jrsonnet_gcmodule::{Cc, Trace};6use jrsonnet_interner::IStr;7pub use jrsonnet_macros::builtin;8use jrsonnet_parser::{Destruct, Expr, ExprParams, Span, Spanned};910use self::{11 arglike::OptionalContext,12 builtin::{Builtin, StaticBuiltin},13 native::NativeDesc,14 parse::{parse_default_function_call, parse_function_call},15};16use crate::{17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, params,18 Context, ContextBuilder, Result, Thunk, Val,19};2021pub mod arglike;22pub mod builtin;23pub mod native;24pub mod parse;25pub mod prepared;2627pub use jrsonnet_parser::function::*;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 72 pub params: ExprParams,73 74 pub body: Rc<Spanned<Expr>>,75}76impl FuncDesc {77 78 pub fn default_body_context(&self) -> Result<Context> {79 parse_default_function_call(self.ctx.clone(), &self.params)80 }8182 83 pub fn call_body_context(84 &self,85 call_ctx: Context,86 args: &dyn ArgsLike,87 tailstrict: bool,88 ) -> Result<Context> {89 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)90 }9192 pub fn evaluate_trivial(&self) -> Option<Val> {93 evaluate_trivial(&self.body)94 }95}969798#[allow(clippy::module_name_repetitions)]99#[derive(Trace, Clone)]100pub enum FuncVal {101 102 Id,103 104 Normal(Cc<FuncDesc>),105 106 Thunk(Thunk<Val>),107 108 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),109 110 Builtin(BuiltinFunc),111}112113impl Debug for FuncVal {114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {115 match self {116 Self::Id => f.debug_tuple("Id").finish(),117 Self::Thunk(arg0) => f.debug_tuple("Thunk").field(arg0).finish(),118 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),119 Self::StaticBuiltin(arg0) => {120 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()121 }122 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),123 }124 }125}126127#[allow(clippy::unnecessary_wraps)]128#[builtin]129const fn builtin_id(x: Val) -> Val {130 x131}132static ID: &builtin_id = &builtin_id {};133134impl FuncVal {135 pub fn builtin(builtin: impl Builtin) -> Self {136 Self::Builtin(BuiltinFunc::new(builtin))137 }138 pub fn static_builtin(static_builtin: &'static dyn StaticBuiltin) -> Self {139 Self::StaticBuiltin(static_builtin)140 }141142 pub fn params(&self) -> FunctionSignature {143 match self {144 Self::Id => ID.params(),145 Self::StaticBuiltin(i) => i.params(),146 Self::Builtin(i) => i.params(),147 Self::Normal(p) => p.params.signature.clone(),148 Self::Thunk(_) => FunctionSignature::empty(),149 }150 }151 152 pub fn params_len(&self) -> usize {153 self.params().iter().filter(|p| !p.has_default()).count()154 }155 156 pub fn name(&self) -> IStr {157 match self {158 Self::Id => "id".into(),159 Self::Normal(normal) => normal.name.clone(),160 Self::StaticBuiltin(builtin) => builtin.name().into(),161 Self::Builtin(builtin) => builtin.name().into(),162 Self::Thunk(_) => "thunk".into(),163 }164 }165 166 167 168 pub fn evaluate(169 &self,170 call_ctx: Context,171 loc: CallLocation<'_>,172 args: &dyn ArgsLike,173 tailstrict: bool,174 ) -> Result<Val> {175 match self {176 Self::Id => ID.call(call_ctx, loc, args),177 Self::Normal(func) => {178 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;179 evaluate(body_ctx, &func.body)180 }181 Self::Thunk(thunk) => {182 if !args.is_empty() {183 bail!(TooManyArgsFunctionHas(0, FunctionSignature::empty()))184 }185 thunk.evaluate()186 }187 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),188 Self::Builtin(b) => b.call(call_ctx, loc, args),189 }190 }191 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(192 &self,193 args: &A,194 tailstrict: bool,195 ) -> Result<Val> {196 self.evaluate(197 ContextBuilder::new().build(),198 CallLocation::native(),199 args,200 tailstrict,201 )202 }203 204 pub fn into_native<D: NativeDesc>(self) -> D::Value {205 D::into_native(self)206 }207208 209 210 211 212 213 pub fn is_identity(&self) -> bool {214 match self {215 Self::Id => true,216 Self::Normal(desc) => {217 if desc.params.len() != 1 {218 return false;219 }220 let param = &desc.params.exprs[0];221 if param.default.is_some() {222 return false;223 }224225 #[allow(clippy::infallible_destructuring_match)]226 let id = match ¶m.destruct {227 Destruct::Full(id) => id,228 #[cfg(feature = "exp-destruct")]229 _ => return false,230 };231 **desc.body == 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 Self::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}