1use std::{fmt::Debug, rc::Rc};23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_ir::{ArgsDesc, Destruct, Expr, ExprParams, Span, Spanned};89use self::{10 builtin::{Builtin, StaticBuiltin},11 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},12 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},13};14use crate::{15 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,16 Result, Thunk, Val,17};1819pub mod builtin;20mod native;21mod parse;22mod prepared;2324pub use native::NativeFn;25pub use prepared::PreparedFuncVal;2627pub use jrsonnet_ir::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(crate) fn call_body_context(84 &self,85 call_ctx: Context,86 args: &ArgsDesc,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: &ArgsDesc,173 tailstrict: bool,174 ) -> Result<Val> {175 match self {176 Self::Normal(func) => {177 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;178 evaluate(body_ctx, &func.body)179 }180 Self::Thunk(thunk) => {181 if !args.named.is_empty() || !args.unnamed.is_empty() {182 bail!(TooManyArgsFunctionHas(0, FunctionSignature::empty()))183 }184 thunk.evaluate()185 }186 Self::Id => {187 let args = parse_builtin_call(call_ctx, ID.params(), args, tailstrict)?;188 ID.call(loc, &args)189 }190 Self::StaticBuiltin(b) => {191 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;192 b.call(loc, &args)193 }194 Self::Builtin(b) => {195 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;196 b.call(loc, &args)197 }198 }199 }200201 pub(crate) fn evaluate_prepared(202 &self,203 prepared: &PreparedCall,204 loc: CallLocation<'_>,205 unnamed: &[Thunk<Val>],206 named: &[Thunk<Val>],207 _tailstrict: bool,208 ) -> Result<Val> {209 match self {210 FuncVal::Normal(func) => {211 let body_ctx = parse_prepared_function_call(212 func.ctx.clone(),213 prepared,214 &func.params,215 unnamed,216 named,217 )?;218 evaluate(body_ctx, &func.body)219 }220 FuncVal::Thunk(t) => t.evaluate(),221 FuncVal::Id => {222 let args = parse_prepared_builtin_call(prepared, ID.params(), unnamed, named);223 ID.call(loc, &args)224 }225 FuncVal::StaticBuiltin(b) => {226 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);227 b.call(loc, &args)228 }229 FuncVal::Builtin(b) => {230 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);231 b.call(loc, &args)232 }233 }234 }235236 237 238 239 240 241 pub fn is_identity(&self) -> bool {242 match self {243 Self::Id => true,244 Self::Normal(desc) => {245 if desc.params.len() != 1 {246 return false;247 }248 let param = &desc.params.exprs[0];249 if param.default.is_some() {250 return false;251 }252253 #[allow(clippy::infallible_destructuring_match)]254 let id = match ¶m.destruct {255 Destruct::Full(id) => id,256 #[cfg(feature = "exp-destruct")]257 _ => return false,258 };259 **desc.body == Expr::Var(id.clone())260 }261 _ => false,262 }263 }264 265 pub const fn identity() -> Self {266 Self::Id267 }268269 pub fn evaluate_trivial(&self) -> Option<Val> {270 match self {271 Self::Normal(n) => n.evaluate_trivial(),272 _ => None,273 }274 }275}276277impl<T> From<T> for FuncVal278where279 T: Builtin,280{281 fn from(value: T) -> Self {282 Self::builtin(value)283 }284}285impl From<&'static dyn StaticBuiltin> for FuncVal {286 fn from(value: &'static dyn StaticBuiltin) -> Self {287 Self::static_builtin(value)288 }289}