1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{LocExpr, ParamsDesc};45use super::{6 arglike::ArgsLike,7 builtin::{BuiltinParam, BuiltinParamName},8};9use crate::{10 error::{Error::*, Result},11 evaluate_named,12 gc::{GcHashMap, TraceBox},13 throw,14 val::LazyValValue,15 Context, FutureWrapper, LazyVal, State, Val,16};1718#[derive(Trace)]19struct EvaluateNamedLazyVal {20 ctx: FutureWrapper<Context>,21 name: IStr,22 value: LocExpr,23}2425impl LazyValValue for EvaluateNamedLazyVal {26 fn get(self: Box<Self>, s: State) -> Result<Val> {27 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)28 }29}30313233343536373839pub fn parse_function_call(40 s: State,41 ctx: Context,42 body_ctx: Context,43 params: &ParamsDesc,44 args: &dyn ArgsLike,45 tailstrict: bool,46) -> Result<Context> {47 let mut passed_args = GcHashMap::with_capacity(params.len());48 if args.unnamed_len() > params.len() {49 throw!(TooManyArgsFunctionHas(params.len()))50 }5152 let mut filled_args = 0;5354 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {55 let name = params[id].0.clone();56 passed_args.insert(name, arg);57 filled_args += 1;58 Ok(())59 })?;6061 args.named_iter(s, ctx, tailstrict, &mut |name, value| {62 63 if !params.iter().any(|p| &p.0 == name) {64 throw!(UnknownFunctionParameter((name as &str).to_owned()));65 }66 if passed_args.insert(name.clone(), value).is_some() {67 throw!(BindingParameterASecondTime(name.clone()));68 }69 filled_args += 1;70 Ok(())71 })?;7273 if filled_args < params.len() {74 75 76 let fctx = Context::new_future();77 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);7879 for param in params.iter().filter(|p| p.1.is_some()) {80 if passed_args.contains_key(¶m.0.clone()) {81 continue;82 }8384 defaults.insert(85 param.0.clone(),86 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {87 ctx: fctx.clone(),88 name: param.0.clone(),89 value: param.1.clone().expect("default exists"),90 }))),91 );92 filled_args += 1;93 }9495 96 if filled_args != params.len() {97 for param in params.iter().skip(args.unnamed_len()) {98 let mut found = false;99 args.named_names(&mut |name| {100 if name == ¶m.0 {101 found = true;102 }103 });104 if !found {105 throw!(FunctionParameterNotBoundInCall(param.0.clone()));106 }107 }108 unreachable!();109 }110111 Ok(body_ctx112 .extend(passed_args, None, None, None)113 .extend_bound(defaults)114 .into_future(fctx))115 } else {116 let body_ctx = body_ctx.extend(passed_args, None, None, None);117 Ok(body_ctx)118 }119}120121122123124125126127128pub fn parse_builtin_call(129 s: State,130 ctx: Context,131 params: &[BuiltinParam],132 args: &dyn ArgsLike,133 tailstrict: bool,134) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {135 let mut passed_args = GcHashMap::with_capacity(params.len());136 if args.unnamed_len() > params.len() {137 throw!(TooManyArgsFunctionHas(params.len()))138 }139140 let mut filled_args = 0;141142 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {143 let name = params[id].name.clone();144 passed_args.insert(name, arg);145 filled_args += 1;146 Ok(())147 })?;148149 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {150 151 let p = params152 .iter()153 .find(|p| p.name == name as &str)154 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;155 if passed_args.insert(p.name.clone(), arg).is_some() {156 throw!(BindingParameterASecondTime(name.clone()));157 }158 filled_args += 1;159 Ok(())160 })?;161162 if filled_args < params.len() {163 for param in params.iter().filter(|p| p.has_default) {164 if passed_args.contains_key(¶m.name) {165 continue;166 }167 filled_args += 1;168 }169170 171 if filled_args != params.len() {172 for param in params.iter().skip(args.unnamed_len()) {173 let mut found = false;174 args.named_names(&mut |name| {175 if name as &str == ¶m.name as &str {176 found = true;177 }178 });179 if !found {180 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));181 }182 }183 unreachable!();184 }185 }186 Ok(passed_args)187}188189190191pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {192 #[derive(Trace)]193 struct DependsOnUnbound(IStr);194 impl LazyValValue for DependsOnUnbound {195 fn get(self: Box<Self>, _: State) -> Result<Val> {196 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())197 }198 }199200 let fctx = Context::new_future();201202 let mut bindings = GcHashMap::new();203204 for param in params.iter() {205 if let Some(v) = ¶m.1 {206 bindings.insert(207 param.0.clone(),208 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {209 ctx: fctx.clone(),210 name: param.0.clone(),211 value: v.clone(),212 }))),213 );214 } else {215 bindings.insert(216 param.0.clone(),217 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),218 );219 }220 }221222 body_ctx223 .extend(bindings, None, None, None)224 .into_future(fctx)225}