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 builtin::{Builtin, StaticBuiltin},12 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},13 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},14};15use crate::{16 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,17 Result, Thunk, Val,18};1920pub mod arglike;21pub mod builtin;22pub mod native;23pub mod parse;24mod prepared;2526pub use prepared::PreparedFuncVal;2728pub use jrsonnet_parser::function::*;29303132#[derive(Clone, Copy)]33pub struct CallLocation<'l>(pub Option<&'l Span>);34impl<'l> CallLocation<'l> {35 36 pub const fn new(loc: &'l Span) -> Self {37 Self(Some(loc))38 }39}40impl CallLocation<'static> {41 42 pub const fn native() -> Self {43 Self(None)44 }45}464748#[derive(Trace, Educe)]49#[educe(Debug, PartialEq)]50pub struct FuncDesc {51 52 53 54 55 56 57 58 59 60 pub name: IStr,61 62 63 64 65 66 67 68 69 70 pub ctx: Context,7172 73 pub params: ExprParams,74 75 pub body: Rc<Spanned<Expr>>,76}77impl FuncDesc {78 79 pub fn default_body_context(&self) -> Result<Context> {80 parse_default_function_call(self.ctx.clone(), &self.params)81 }8283 84 pub fn call_body_context(85 &self,86 call_ctx: Context,87 args: &dyn ArgsLike,88 tailstrict: bool,89 ) -> Result<Context> {90 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)91 }9293 pub fn evaluate_trivial(&self) -> Option<Val> {94 evaluate_trivial(&self.body)95 }96}979899#[allow(clippy::module_name_repetitions)]100#[derive(Trace, Clone)]101pub enum FuncVal {102 103 Id,104 105 Normal(Cc<FuncDesc>),106 107 Thunk(Thunk<Val>),108 109 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),110 111 Builtin(BuiltinFunc),112}113114impl Debug for FuncVal {115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {116 match self {117 Self::Id => f.debug_tuple("Id").finish(),118 Self::Thunk(arg0) => f.debug_tuple("Thunk").field(arg0).finish(),119 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),120 Self::StaticBuiltin(arg0) => {121 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()122 }123 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),124 }125 }126}127128#[allow(clippy::unnecessary_wraps)]129#[builtin]130const fn builtin_id(x: Val) -> Val {131 x132}133static ID: &builtin_id = &builtin_id {};134135impl FuncVal {136 pub fn builtin(builtin: impl Builtin) -> Self {137 Self::Builtin(BuiltinFunc::new(builtin))138 }139 pub fn static_builtin(static_builtin: &'static dyn StaticBuiltin) -> Self {140 Self::StaticBuiltin(static_builtin)141 }142143 pub fn params(&self) -> FunctionSignature {144 match self {145 Self::Id => ID.params(),146 Self::StaticBuiltin(i) => i.params(),147 Self::Builtin(i) => i.params(),148 Self::Normal(p) => p.params.signature.clone(),149 Self::Thunk(_) => FunctionSignature::empty(),150 }151 }152 153 pub fn params_len(&self) -> usize {154 self.params().iter().filter(|p| !p.has_default()).count()155 }156 157 pub fn name(&self) -> IStr {158 match self {159 Self::Id => "id".into(),160 Self::Normal(normal) => normal.name.clone(),161 Self::StaticBuiltin(builtin) => builtin.name().into(),162 Self::Builtin(builtin) => builtin.name().into(),163 Self::Thunk(_) => "thunk".into(),164 }165 }166 167 168 169 pub fn evaluate(170 &self,171 call_ctx: Context,172 loc: CallLocation<'_>,173 args: &dyn ArgsLike,174 tailstrict: bool,175 ) -> Result<Val> {176 match self {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::Id => {188 let args = parse_builtin_call(call_ctx, ID.params(), args, tailstrict)?;189 ID.call(loc, &args)190 }191 Self::StaticBuiltin(b) => {192 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;193 b.call(loc, &args)194 }195 Self::Builtin(b) => {196 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;197 b.call(loc, &args)198 }199 }200 }201202 pub(crate) fn evaluate_prepared(203 &self,204 prepared: &PreparedCall,205 loc: CallLocation<'_>,206 unnamed: &[Thunk<Val>],207 named: &[Thunk<Val>],208 _tailstrict: bool,209 ) -> Result<Val> {210 match self {211 FuncVal::Id => {212 let args = parse_prepared_builtin_call(prepared, ID.params(), unnamed, named)?;213 ID.call(loc, &args)214 }215 FuncVal::Normal(func) => {216 let body_ctx = parse_prepared_function_call(217 func.ctx.clone(),218 prepared,219 &func.params,220 unnamed,221 named,222 )?;223 evaluate(body_ctx, &func.body)224 }225 FuncVal::Thunk(t) => t.evaluate(),226 FuncVal::StaticBuiltin(b) => {227 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;228 b.call(loc, &args)229 }230 FuncVal::Builtin(b) => {231 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;232 b.call(loc, &args)233 }234 }235 }236237 238 239 240 241 242 pub fn is_identity(&self) -> bool {243 match self {244 Self::Id => true,245 Self::Normal(desc) => {246 if desc.params.len() != 1 {247 return false;248 }249 let param = &desc.params.exprs[0];250 if param.default.is_some() {251 return false;252 }253254 #[allow(clippy::infallible_destructuring_match)]255 let id = match ¶m.destruct {256 Destruct::Full(id) => id,257 #[cfg(feature = "exp-destruct")]258 _ => return false,259 };260 **desc.body == Expr::Var(id.clone())261 }262 _ => false,263 }264 }265 266 pub const fn identity() -> Self {267 Self::Id268 }269270 pub fn evaluate_trivial(&self) -> Option<Val> {271 match self {272 Self::Normal(n) => n.evaluate_trivial(),273 _ => None,274 }275 }276}277278impl<T> From<T> for FuncVal279where280 T: Builtin,281{282 fn from(value: T) -> Self {283 Self::builtin(value)284 }285}286impl From<&'static dyn StaticBuiltin> for FuncVal {287 fn from(value: &'static dyn StaticBuiltin) -> Self {288 Self::static_builtin(value)289 }290}