difftreelog
feat FuncDesc helper for default context
in: master
2 files changed
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use crate::{2 error::{Error::*, LocError},3 evaluate, evaluate_named,4 gc::TraceBox,5 throw,6 typed::Typed,7 Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17 context: Context,18 expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21 fn get(self: Box<Self>) -> Result<Val> {22 evaluate(self.context, &self.expr)23 }24}2526pub trait ArgLike {27 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;28}29impl ArgLike for &LocExpr {30 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {31 Ok(if tailstrict {32 LazyVal::new_resolved(evaluate(ctx, self)?)33 } else {34 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {35 context: ctx,36 expr: (*self).clone(),37 })))38 })39 }40}41impl<T> ArgLike for T42where43 T: Typed + Clone,44 Val: TryFrom<T, Error = LocError>,45{46 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {47 let val: Val = Val::try_from(self.clone())?;48 Ok(LazyVal::new_resolved(val))49 }50}51pub enum TlaArg {52 String(IStr),53 Code(LocExpr),54 Val(Val),55}56impl ArgLike for TlaArg {57 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {58 match self {59 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),60 TlaArg::Code(code) => Ok(if tailstrict {61 LazyVal::new_resolved(evaluate(ctx, code)?)62 } else {63 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {64 context: ctx,65 expr: code.clone(),66 })))67 }),68 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),69 }70 }71}7273pub trait ArgsLike {74 fn unnamed_len(&self) -> usize;75 fn unnamed_iter(76 &self,77 ctx: Context,78 tailstrict: bool,79 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,80 ) -> Result<()>;81 fn named_iter(82 &self,83 ctx: Context,84 tailstrict: bool,85 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,86 ) -> Result<()>;87 fn named_names(&self, handler: &mut dyn FnMut(&IStr));88}8990impl ArgsLike for ArgsDesc {91 fn unnamed_len(&self) -> usize {92 self.unnamed.len()93 }9495 fn unnamed_iter(96 &self,97 ctx: Context,98 tailstrict: bool,99 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,100 ) -> Result<()> {101 for (id, arg) in self.unnamed.iter().enumerate() {102 handler(103 id,104 if tailstrict {105 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)106 } else {107 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {108 context: ctx.clone(),109 expr: arg.clone(),110 })))111 },112 )?;113 }114 Ok(())115 }116117 fn named_iter(118 &self,119 ctx: Context,120 tailstrict: bool,121 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,122 ) -> Result<()> {123 for (name, arg) in self.named.iter() {124 handler(125 name,126 if tailstrict {127 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)128 } else {129 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {130 context: ctx.clone(),131 expr: arg.clone(),132 })))133 },134 )?;135 }136 Ok(())137 }138139 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {140 for (name, _) in self.named.iter() {141 handler(name)142 }143 }144}145146impl<A: ArgLike> ArgsLike for [(IStr, A)] {147 fn unnamed_len(&self) -> usize {148 0149 }150151 fn unnamed_iter(152 &self,153 _ctx: Context,154 _tailstrict: bool,155 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,156 ) -> Result<()> {157 Ok(())158 }159160 fn named_iter(161 &self,162 ctx: Context,163 tailstrict: bool,164 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,165 ) -> Result<()> {166 for (name, val) in self.iter() {167 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;168 }169 Ok(())170 }171172 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {173 for (name, _) in self.iter() {174 handler(name);175 }176 }177}178179impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {180 fn unnamed_len(&self) -> usize {181 0182 }183184 fn unnamed_iter(185 &self,186 _ctx: Context,187 _tailstrict: bool,188 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,189 ) -> Result<()> {190 Ok(())191 }192193 fn named_iter(194 &self,195 ctx: Context,196 tailstrict: bool,197 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,198 ) -> Result<()> {199 for (name, value) in self.iter() {200 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;201 }202 Ok(())203 }204205 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {206 for (name, _) in self.iter() {207 handler(name);208 }209 }210}211212impl<A: ArgLike> ArgsLike for [A] {213 fn unnamed_len(&self) -> usize {214 self.len()215 }216217 fn unnamed_iter(218 &self,219 ctx: Context,220 tailstrict: bool,221 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,222 ) -> Result<()> {223 for (i, arg) in self.iter().enumerate() {224 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;225 }226 Ok(())227 }228229 fn named_iter(230 &self,231 _ctx: Context,232 _tailstrict: bool,233 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,234 ) -> Result<()> {235 Ok(())236 }237238 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}239}240impl<A: ArgLike> ArgsLike for &[A] {241 fn unnamed_len(&self) -> usize {242 (*self).unnamed_len()243 }244245 fn unnamed_iter(246 &self,247 ctx: Context,248 tailstrict: bool,249 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,250 ) -> Result<()> {251 (*self).unnamed_iter(ctx, tailstrict, handler)252 }253254 fn named_iter(255 &self,256 ctx: Context,257 tailstrict: bool,258 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,259 ) -> Result<()> {260 (*self).named_iter(ctx, tailstrict, handler)261 }262263 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {264 (*self).named_names(handler)265 }266}267268/// Creates correct [context](Context) for function body evaluation returning error on invalid call.269///270/// ## Parameters271/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)272/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)273/// * `params`: function parameters' definition274/// * `args`: passed function arguments275/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily276pub fn parse_function_call(277 ctx: Context,278 body_ctx: Context,279 params: &ParamsDesc,280 args: &dyn ArgsLike,281 tailstrict: bool,282) -> Result<Context> {283 let mut passed_args = GcHashMap::with_capacity(params.len());284 if args.unnamed_len() > params.len() {285 throw!(TooManyArgsFunctionHas(params.len()))286 }287288 let mut filled_args = 0;289290 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {291 let name = params[id].0.clone();292 passed_args.insert(name, arg);293 filled_args += 1;294 Ok(())295 })?;296297 args.named_iter(ctx, tailstrict, &mut |name, value| {298 // FIXME: O(n) for arg existence check299 if !params.iter().any(|p| &p.0 == name) {300 throw!(UnknownFunctionParameter((name as &str).to_owned()));301 }302 if passed_args.insert(name.clone(), value).is_some() {303 throw!(BindingParameterASecondTime(name.clone()));304 }305 filled_args += 1;306 Ok(())307 })?;308309 if filled_args < params.len() {310 // Some args are unset, but maybe we have defaults for them311 // Default values should be created in newly created context312 let future_context = FutureWrapper::<Context>::new();313 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);314315 for param in params.iter().filter(|p| p.1.is_some()) {316 if passed_args.contains_key(¶m.0.clone()) {317 continue;318 }319 #[derive(Trace)]320 struct LazyNamedBinding {321 future_context: FutureWrapper<Context>,322 name: IStr,323 value: LocExpr,324 }325 impl LazyValValue for LazyNamedBinding {326 fn get(self: Box<Self>) -> Result<Val> {327 evaluate_named(self.future_context.unwrap(), &self.value, self.name)328 }329 }330 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {331 future_context: future_context.clone(),332 name: param.0.clone(),333 value: param.1.clone().unwrap(),334 })));335336 defaults.insert(337 param.0.clone(),338 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {339 future_context: future_context.clone(),340 name: param.0.clone(),341 value: param.1.clone().unwrap(),342 }))),343 );344 filled_args += 1;345 }346347 // Some args still wasn't filled348 if filled_args != params.len() {349 for param in params.iter().skip(args.unnamed_len()) {350 let mut found = false;351 args.named_names(&mut |name| {352 if name == ¶m.0 {353 found = true;354 }355 });356 if !found {357 throw!(FunctionParameterNotBoundInCall(param.0.clone()));358 }359 }360 unreachable!();361 }362363 Ok(body_ctx364 .extend(passed_args, None, None, None)365 .extend_bound(defaults)366 .into_future(future_context))367 } else {368 let body_ctx = body_ctx.extend(passed_args, None, None, None);369 Ok(body_ctx)370 }371}372373type BuiltinParamName = Cow<'static, str>;374375#[derive(Clone, Trace)]376pub struct BuiltinParam {377 pub name: BuiltinParamName,378 pub has_default: bool,379}380381/// Do not implement it directly, instead use #[builtin] macro382pub trait Builtin: Trace {383 fn name(&self) -> &str;384 fn params(&self) -> &[BuiltinParam];385 fn call(386 &self,387 context: Context,388 loc: Option<&ExprLocation>,389 args: &dyn ArgsLike,390 ) -> Result<Val>;391}392393pub trait StaticBuiltin: Builtin + Send + Sync394where395 Self: 'static,396{397 // In impl, to make it object safe:398 // const INST: &'static Self;399}400401/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead402///403/// ## Parameters404/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)405/// * `params`: function parameters' definition406/// * `args`: passed function arguments407/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily408pub fn parse_builtin_call(409 ctx: Context,410 params: &[BuiltinParam],411 args: &dyn ArgsLike,412 tailstrict: bool,413) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {414 let mut passed_args = GcHashMap::with_capacity(params.len());415 if args.unnamed_len() > params.len() {416 throw!(TooManyArgsFunctionHas(params.len()))417 }418419 let mut filled_args = 0;420421 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {422 let name = params[id].name.clone();423 passed_args.insert(name, arg);424 filled_args += 1;425 Ok(())426 })?;427428 args.named_iter(ctx, tailstrict, &mut |name, arg| {429 // FIXME: O(n) for arg existence check430 let p = params431 .iter()432 .find(|p| p.name == name as &str)433 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;434 if passed_args.insert(p.name.clone(), arg).is_some() {435 throw!(BindingParameterASecondTime(name.clone()));436 }437 filled_args += 1;438 Ok(())439 })?;440441 if filled_args < params.len() {442 for param in params.iter().filter(|p| p.has_default) {443 if passed_args.contains_key(¶m.name) {444 continue;445 }446 filled_args += 1;447 }448449 // Some args still wasn't filled450 if filled_args != params.len() {451 for param in params.iter().skip(args.unnamed_len()) {452 let mut found = false;453 args.named_names(&mut |name| {454 if name as &str == ¶m.name as &str {455 found = true;456 }457 });458 if !found {459 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));460 }461 }462 unreachable!();463 }464 }465 Ok(passed_args)466}1use crate::{2 error::{Error::*, LocError},3 evaluate, evaluate_named,4 gc::TraceBox,5 throw,6 typed::Typed,7 Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17 context: Context,18 expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21 fn get(self: Box<Self>) -> Result<Val> {22 evaluate(self.context, &self.expr)23 }24}2526#[derive(Trace)]27struct EvaluateNamedLazyVal {28 future_context: FutureWrapper<Context>,29 name: IStr,30 value: LocExpr,31}32impl LazyValValue for EvaluateNamedLazyVal {33 fn get(self: Box<Self>) -> Result<Val> {34 evaluate_named(self.future_context.unwrap(), &self.value, self.name)35 }36}3738pub trait ArgLike {39 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;40}41impl ArgLike for &LocExpr {42 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {43 Ok(if tailstrict {44 LazyVal::new_resolved(evaluate(ctx, self)?)45 } else {46 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {47 context: ctx,48 expr: (*self).clone(),49 })))50 })51 }52}53impl<T> ArgLike for T54where55 T: Typed + Clone,56 Val: TryFrom<T, Error = LocError>,57{58 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {59 let val: Val = Val::try_from(self.clone())?;60 Ok(LazyVal::new_resolved(val))61 }62}63pub enum TlaArg {64 String(IStr),65 Code(LocExpr),66 Val(Val),67}68impl ArgLike for TlaArg {69 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {70 match self {71 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),72 TlaArg::Code(code) => Ok(if tailstrict {73 LazyVal::new_resolved(evaluate(ctx, code)?)74 } else {75 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {76 context: ctx,77 expr: code.clone(),78 })))79 }),80 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),81 }82 }83}8485pub trait ArgsLike {86 fn unnamed_len(&self) -> usize;87 fn unnamed_iter(88 &self,89 ctx: Context,90 tailstrict: bool,91 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,92 ) -> Result<()>;93 fn named_iter(94 &self,95 ctx: Context,96 tailstrict: bool,97 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,98 ) -> Result<()>;99 fn named_names(&self, handler: &mut dyn FnMut(&IStr));100}101102impl ArgsLike for ArgsDesc {103 fn unnamed_len(&self) -> usize {104 self.unnamed.len()105 }106107 fn unnamed_iter(108 &self,109 ctx: Context,110 tailstrict: bool,111 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,112 ) -> Result<()> {113 for (id, arg) in self.unnamed.iter().enumerate() {114 handler(115 id,116 if tailstrict {117 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)118 } else {119 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {120 context: ctx.clone(),121 expr: arg.clone(),122 })))123 },124 )?;125 }126 Ok(())127 }128129 fn named_iter(130 &self,131 ctx: Context,132 tailstrict: bool,133 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,134 ) -> Result<()> {135 for (name, arg) in self.named.iter() {136 handler(137 name,138 if tailstrict {139 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)140 } else {141 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {142 context: ctx.clone(),143 expr: arg.clone(),144 })))145 },146 )?;147 }148 Ok(())149 }150151 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {152 for (name, _) in self.named.iter() {153 handler(name)154 }155 }156}157158impl<A: ArgLike> ArgsLike for [(IStr, A)] {159 fn unnamed_len(&self) -> usize {160 0161 }162163 fn unnamed_iter(164 &self,165 _ctx: Context,166 _tailstrict: bool,167 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,168 ) -> Result<()> {169 Ok(())170 }171172 fn named_iter(173 &self,174 ctx: Context,175 tailstrict: bool,176 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,177 ) -> Result<()> {178 for (name, val) in self.iter() {179 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;180 }181 Ok(())182 }183184 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {185 for (name, _) in self.iter() {186 handler(name);187 }188 }189}190191impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {192 fn unnamed_len(&self) -> usize {193 0194 }195196 fn unnamed_iter(197 &self,198 _ctx: Context,199 _tailstrict: bool,200 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,201 ) -> Result<()> {202 Ok(())203 }204205 fn named_iter(206 &self,207 ctx: Context,208 tailstrict: bool,209 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,210 ) -> Result<()> {211 for (name, value) in self.iter() {212 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;213 }214 Ok(())215 }216217 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {218 for (name, _) in self.iter() {219 handler(name);220 }221 }222}223224impl<A: ArgLike> ArgsLike for [A] {225 fn unnamed_len(&self) -> usize {226 self.len()227 }228229 fn unnamed_iter(230 &self,231 ctx: Context,232 tailstrict: bool,233 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,234 ) -> Result<()> {235 for (i, arg) in self.iter().enumerate() {236 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;237 }238 Ok(())239 }240241 fn named_iter(242 &self,243 _ctx: Context,244 _tailstrict: bool,245 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,246 ) -> Result<()> {247 Ok(())248 }249250 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}251}252impl<A: ArgLike> ArgsLike for &[A] {253 fn unnamed_len(&self) -> usize {254 (*self).unnamed_len()255 }256257 fn unnamed_iter(258 &self,259 ctx: Context,260 tailstrict: bool,261 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,262 ) -> Result<()> {263 (*self).unnamed_iter(ctx, tailstrict, handler)264 }265266 fn named_iter(267 &self,268 ctx: Context,269 tailstrict: bool,270 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,271 ) -> Result<()> {272 (*self).named_iter(ctx, tailstrict, handler)273 }274275 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {276 (*self).named_names(handler)277 }278}279280/// Creates correct [context](Context) for function body evaluation returning error on invalid call.281///282/// ## Parameters283/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)284/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)285/// * `params`: function parameters' definition286/// * `args`: passed function arguments287/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily288pub fn parse_function_call(289 ctx: Context,290 body_ctx: Context,291 params: &ParamsDesc,292 args: &dyn ArgsLike,293 tailstrict: bool,294) -> Result<Context> {295 let mut passed_args = GcHashMap::with_capacity(params.len());296 if args.unnamed_len() > params.len() {297 throw!(TooManyArgsFunctionHas(params.len()))298 }299300 let mut filled_args = 0;301302 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {303 let name = params[id].0.clone();304 passed_args.insert(name, arg);305 filled_args += 1;306 Ok(())307 })?;308309 args.named_iter(ctx, tailstrict, &mut |name, value| {310 // FIXME: O(n) for arg existence check311 if !params.iter().any(|p| &p.0 == name) {312 throw!(UnknownFunctionParameter((name as &str).to_owned()));313 }314 if passed_args.insert(name.clone(), value).is_some() {315 throw!(BindingParameterASecondTime(name.clone()));316 }317 filled_args += 1;318 Ok(())319 })?;320321 if filled_args < params.len() {322 // Some args are unset, but maybe we have defaults for them323 // Default values should be created in newly created context324 let future_context = Context::new_future();325 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);326327 for param in params.iter().filter(|p| p.1.is_some()) {328 if passed_args.contains_key(¶m.0.clone()) {329 continue;330 }331 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {332 future_context: future_context.clone(),333 name: param.0.clone(),334 value: param.1.clone().unwrap(),335 })));336337 defaults.insert(338 param.0.clone(),339 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {340 future_context: future_context.clone(),341 name: param.0.clone(),342 value: param.1.clone().unwrap(),343 }))),344 );345 filled_args += 1;346 }347348 // Some args still wasn't filled349 if filled_args != params.len() {350 for param in params.iter().skip(args.unnamed_len()) {351 let mut found = false;352 args.named_names(&mut |name| {353 if name == ¶m.0 {354 found = true;355 }356 });357 if !found {358 throw!(FunctionParameterNotBoundInCall(param.0.clone()));359 }360 }361 unreachable!();362 }363364 Ok(body_ctx365 .extend(passed_args, None, None, None)366 .extend_bound(defaults)367 .into_future(future_context))368 } else {369 let body_ctx = body_ctx.extend(passed_args, None, None, None);370 Ok(body_ctx)371 }372}373374type BuiltinParamName = Cow<'static, str>;375376#[derive(Clone, Trace)]377pub struct BuiltinParam {378 pub name: BuiltinParamName,379 pub has_default: bool,380}381382/// Do not implement it directly, instead use #[builtin] macro383pub trait Builtin: Trace {384 fn name(&self) -> &str;385 fn params(&self) -> &[BuiltinParam];386 fn call(387 &self,388 context: Context,389 loc: Option<&ExprLocation>,390 args: &dyn ArgsLike,391 ) -> Result<Val>;392}393394pub trait StaticBuiltin: Builtin + Send + Sync395where396 Self: 'static,397{398 // In impl, to make it object safe:399 // const INST: &'static Self;400}401402/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead403///404/// ## Parameters405/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)406/// * `params`: function parameters' definition407/// * `args`: passed function arguments408/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily409pub fn parse_builtin_call(410 ctx: Context,411 params: &[BuiltinParam],412 args: &dyn ArgsLike,413 tailstrict: bool,414) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {415 let mut passed_args = GcHashMap::with_capacity(params.len());416 if args.unnamed_len() > params.len() {417 throw!(TooManyArgsFunctionHas(params.len()))418 }419420 let mut filled_args = 0;421422 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {423 let name = params[id].name.clone();424 passed_args.insert(name, arg);425 filled_args += 1;426 Ok(())427 })?;428429 args.named_iter(ctx, tailstrict, &mut |name, arg| {430 // FIXME: O(n) for arg existence check431 let p = params432 .iter()433 .find(|p| p.name == name as &str)434 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;435 if passed_args.insert(p.name.clone(), arg).is_some() {436 throw!(BindingParameterASecondTime(name.clone()));437 }438 filled_args += 1;439 Ok(())440 })?;441442 if filled_args < params.len() {443 for param in params.iter().filter(|p| p.has_default) {444 if passed_args.contains_key(¶m.name) {445 continue;446 }447 filled_args += 1;448 }449450 // Some args still wasn't filled451 if filled_args != params.len() {452 for param in params.iter().skip(args.unnamed_len()) {453 let mut found = false;454 args.named_names(&mut |name| {455 if name as &str == ¶m.name as &str {456 found = true;457 }458 });459 if !found {460 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));461 }462 }463 unreachable!();464 }465 }466 Ok(passed_args)467}468469/// Creates Context, which has all argument default values applied470/// and with unbound values causing error to be returned471pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {472 let ctx = Context::new_future();473474 let mut bindings = GcHashMap::new();475476 #[derive(Trace)]477 struct DependsOnUnbound(IStr);478 impl LazyValValue for DependsOnUnbound {479 fn get(self: Box<Self>) -> Result<Val> {480 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())481 }482 }483484 for param in params.iter() {485 if let Some(v) = ¶m.1 {486 bindings.insert(487 param.0.clone(),488 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {489 future_context: ctx.clone(),490 name: param.0.clone(),491 value: v.clone(),492 }))),493 );494 } else {495 bindings.insert(496 param.0.clone(),497 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),498 );499 }500 }501502 body_ctx.extend(bindings, None, None, None).into_future(ctx)503}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -5,7 +5,9 @@
cc_ptr_eq,
error::{Error::*, LocError},
evaluate,
- function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},
+ function::{
+ parse_default_function_call, parse_function_call, ArgsLike, Builtin, StaticBuiltin,
+ },
gc::TraceBox,
throw, Context, ObjValue, Result,
};
@@ -84,6 +86,22 @@
pub params: ParamsDesc,
pub body: LocExpr,
}
+impl FuncDesc {
+ /// Create body context, but fill arguments without defaults with lazy error
+ pub fn default_body_context(&self) -> Context {
+ parse_default_function_call(self.ctx.clone(), &self.params)
+ }
+
+ /// Create context, with which body code will run
+ pub fn call_body_context(
+ &self,
+ call_ctx: Context,
+ args: &dyn ArgsLike,
+ tailstrict: bool,
+ ) -> Result<Context> {
+ parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)
+ }
+}
#[derive(Trace, Clone)]
pub enum FuncVal {
@@ -140,16 +158,10 @@
) -> Result<Val> {
match self {
Self::Normal(func) => {
- let ctx = parse_function_call(
- call_ctx,
- func.ctx.clone(),
- &func.params,
- args,
- tailstrict,
- )?;
- evaluate(ctx, &func.body)
+ let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
+ evaluate(body_ctx, &func.body)
}
- Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),
+ Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),
Self::Builtin(b) => b.call(call_ctx, loc, args),
}
}