1use crate::{Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val, error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw};2use gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};5use std::collections::HashMap;67const NO_DEFAULT_CONTEXT: &str =8 "no default context set for call with defined default parameter value";910#[derive(Trace)]11struct EvaluateLazyVal {12 context: Context,13 expr: LocExpr,14}15impl LazyValValue for EvaluateLazyVal {16 fn get(self: Box<Self>) -> Result<Val> {17 evaluate(self.context, &self.expr)18 }19}20212223242526272829pub fn parse_function_call(30 ctx: Context,31 body_ctx: Context,32 params: &ParamsDesc,33 args: &ArgsDesc,34 tailstrict: bool,35) -> Result<Context> {36 let mut passed_args = GcHashMap::with_capacity(params.len());37 if args.unnamed.len() > params.len() {38 throw!(TooManyArgsFunctionHas(params.len()))39 }4041 let mut filled_args = 0;4243 for (id, arg) in args.unnamed.iter().enumerate() {44 let name = params[id].0.clone();45 passed_args.insert(46 name,47 if tailstrict {48 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)49 } else {50 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {51 context: ctx.clone(),52 expr: arg.clone(),53 })))54 },55 );56 filled_args += 1;57 }5859 for (name, value) in args.named.iter() {60 61 if !params.iter().any(|p| &p.0 == name) {62 throw!(UnknownFunctionParameter((name as &str).to_owned()));63 }64 if passed_args65 .insert(66 name.clone(),67 if tailstrict {68 LazyVal::new_resolved(evaluate(ctx.clone(), value)?)69 } else {70 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {71 context: ctx.clone(),72 expr: value.clone(),73 })))74 },75 )76 .is_some()77 {78 throw!(BindingParameterASecondTime(name.clone()));79 }80 filled_args += 1;81 }8283 if filled_args < params.len() {84 85 86 let future_context = FutureWrapper::<Context>::new();87 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);8889 for param in params.iter().filter(|p| p.1.is_some()) {90 if passed_args.contains_key(¶m.0.clone()) {91 continue;92 }93 #[derive(Trace)]94 struct LazyNamedBinding {95 future_context: FutureWrapper<Context>,96 name: IStr,97 value: LocExpr,98 }99 impl LazyValValue for LazyNamedBinding {100 fn get(self: Box<Self>) -> Result<Val> {101 evaluate_named(self.future_context.unwrap(), &self.value, self.name)102 }103 }104 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {105 future_context: future_context.clone(),106 name: param.0.clone(),107 value: param.1.clone().unwrap(),108 })));109110 defaults.insert(111 param.0.clone(),112 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {113 future_context: future_context.clone(),114 name: param.0.clone(),115 value: param.1.clone().unwrap(),116 }))),117 );118 filled_args += 1;119 }120121 122 if filled_args != params.len() {123 for param in params.iter().skip(args.unnamed.len()) {124 if !args.named.iter().any(|a| a.0 == param.0) {125 throw!(FunctionParameterNotBoundInCall(param.0.clone()));126 }127 }128 unreachable!();129 }130131 Ok(body_ctx132 .extend(passed_args, None, None, None)133 .extend_bound(defaults)134 .into_future(future_context))135 } else {136 let body_ctx = body_ctx.extend(passed_args, None, None, None);137 Ok(body_ctx)138 }139}140141pub fn parse_function_call_map(142 ctx: Context,143 body_ctx: Option<Context>,144 params: &ParamsDesc,145 args: &HashMap<IStr, Val>,146 tailstrict: bool,147) -> Result<Context> {148 let mut out = GcHashMap::with_capacity(params.len());149 let mut positioned_args = vec![None; params.0.len()];150 for (name, val) in args.iter() {151 let idx = params152 .iter()153 .position(|p| *p.0 == **name)154 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;155156 if idx >= params.len() {157 throw!(TooManyArgsFunctionHas(params.len()));158 }159 if positioned_args[idx].is_some() {160 throw!(BindingParameterASecondTime(params[idx].0.clone()));161 }162 positioned_args[idx] = Some(val.clone());163 }164 165 for (id, p) in params.iter().enumerate() {166 let val = if let Some(arg) = positioned_args[id].take() {167 LazyVal::new_resolved(arg)168 } else if let Some(default) = &p.1 {169 if tailstrict {170 LazyVal::new_resolved(evaluate(171 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),172 default,173 )?)174 } else {175 let body_ctx = body_ctx.clone();176 let default = default.clone();177 #[derive(Trace)]178 struct EvaluateLazyVal {179 body_ctx: Option<Context>,180 default: LocExpr,181 }182 impl LazyValValue for EvaluateLazyVal {183 fn get(self: Box<Self>) -> Result<Val> {184 evaluate(185 self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),186 &self.default,187 )188 }189 }190 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {191 body_ctx,192 default,193 })))194 }195 } else {196 throw!(FunctionParameterNotBoundInCall(p.0.clone()));197 };198 out.insert(p.0.clone(), val);199 }200201 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))202}203204pub fn place_args(205 ctx: Context,206 body_ctx: Option<Context>,207 params: &ParamsDesc,208 args: &[Val],209) -> Result<Context> {210 let mut out = GcHashMap::with_capacity(params.len());211 let mut positioned_args = vec![None; params.0.len()];212 for (id, arg) in args.iter().enumerate() {213 if id >= params.len() {214 throw!(TooManyArgsFunctionHas(params.len()));215 }216 positioned_args[id] = Some(arg);217 }218 219 for (id, p) in params.iter().enumerate() {220 let val = if let Some(arg) = &positioned_args[id] {221 (*arg).clone()222 } else if let Some(default) = &p.1 {223 evaluate(ctx.clone(), default)?224 } else {225 throw!(FunctionParameterNotBoundInCall(p.0.clone()));226 };227 out.insert(p.0.clone(), LazyVal::new_resolved(val));228 }229230 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))231}232233#[macro_export]234macro_rules! parse_args {235 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [236 $($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?237 ], $handler:block) => {{238 use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};239240 let args = $args;241 if args.unnamed.len() + args.named.len() > $total_args {242 throw!(TooManyArgsFunctionHas($total_args));243 }244 $(245 if args.unnamed.len() + args.named.len() <= $id {246 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));247 }248 249 let $name = if $id >= $args.unnamed.len() {250 let named = &args.named[$id - $args.unnamed.len()];251 if &named.0 != stringify!($name) {252 throw!(IntrinsicArgumentReorderingIsNotSupportedYet);253 }254 &named.1255 } else {256 &$args.unnamed[$id]257 };258 let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {259 let value = evaluate($ctx.clone(), &$name)?;260 $ty.check(&value)?;261 Ok(value)262 })?;263 $(264 let $name = if let $match(v) = $name {265 v266 } else {267 unreachable!();268 };269 )?270 )+271 ($handler as crate::Result<_>)272 }};273}