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, ParamsDesc, Span, Spanned};910use self::{11 arglike::OptionalContext,12 builtin::{Builtin, ParamParse, 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, Context,18 ContextBuilder, Result, Thunk, Val,19};2021pub mod arglike;22pub mod builtin;23pub mod native;24pub mod parse;25262728#[derive(Clone, Copy)]29pub struct CallLocation<'l>(pub Option<&'l Span>);30impl<'l> CallLocation<'l> {31 32 pub const fn new(loc: &'l Span) -> Self {33 Self(Some(loc))34 }35}36impl CallLocation<'static> {37 38 pub const fn native() -> Self {39 Self(None)40 }41}424344#[derive(Trace, Educe)]45#[educe(Debug, PartialEq)]46pub struct FuncDesc {47 48 49 50 51 52 53 54 55 56 pub name: IStr,57 58 59 60 61 62 63 64 65 66 pub ctx: Context,6768 69 pub params: ParamsDesc,70 71 pub body: Rc<Spanned<Expr>>,7273 #[educe(PartialEq = false, Debug = false)]74 pub(crate) params_parse: Vec<ParamParse>,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) -> &[ParamParse] {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_parse,148 Self::Thunk(_) => &[],149 }150 }151 152 pub fn params_len(&self) -> usize {153 match self {154 Self::Id => 1,155 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),156 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),157 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),158 Self::Thunk(_) => 0,159 }160 }161 162 pub fn name(&self) -> IStr {163 match self {164 Self::Id => "id".into(),165 Self::Normal(normal) => normal.name.clone(),166 Self::StaticBuiltin(builtin) => builtin.name().into(),167 Self::Builtin(builtin) => builtin.name().into(),168 Self::Thunk(_) => "thunk".into(),169 }170 }171 172 173 174 pub fn evaluate(175 &self,176 call_ctx: Context,177 loc: CallLocation<'_>,178 args: &dyn ArgsLike,179 tailstrict: bool,180 ) -> Result<Val> {181 match self {182 Self::Id => ID.call(call_ctx, loc, args),183 Self::Normal(func) => {184 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;185 evaluate(body_ctx, &func.body)186 }187 Self::Thunk(thunk) => {188 if args.is_empty() {189 bail!(TooManyArgsFunctionHas(0, vec![],))190 }191 thunk.evaluate()192 }193 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),194 Self::Builtin(b) => b.call(call_ctx, loc, args),195 }196 }197 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(198 &self,199 args: &A,200 tailstrict: bool,201 ) -> Result<Val> {202 self.evaluate(203 ContextBuilder::new().build(),204 CallLocation::native(),205 args,206 tailstrict,207 )208 }209 210 pub fn into_native<D: NativeDesc>(self) -> D::Value {211 D::into_native(self)212 }213214 215 216 217 218 219 pub fn is_identity(&self) -> bool {220 match self {221 Self::Id => true,222 Self::Normal(desc) => {223 if desc.params.len() != 1 {224 return false;225 }226 let param = &desc.params[0];227 if param.1.is_some() {228 return false;229 }230 #[allow(clippy::infallible_destructuring_match)]231 let id = match ¶m.0 {232 Destruct::Full(id) => id,233 #[cfg(feature = "exp-destruct")]234 _ => return false,235 };236 **desc.body == Expr::Var(id.clone())237 }238 _ => false,239 }240 }241 242 pub const fn identity() -> Self {243 Self::Id244 }245246 pub fn evaluate_trivial(&self) -> Option<Val> {247 match self {248 Self::Normal(n) => n.evaluate_trivial(),249 _ => None,250 }251 }252}253254impl<T> From<T> for FuncVal255where256 T: Builtin,257{258 fn from(value: T) -> Self {259 Self::builtin(value)260 }261}262impl From<&'static dyn StaticBuiltin> for FuncVal {263 fn from(value: &'static dyn StaticBuiltin) -> Self {264 Self::static_builtin(value)265 }266}