1use std::mem::replace;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};67use super::{arglike::ArgsLike, builtin::BuiltinParam};8use crate::{9 bail,10 destructure::destruct,11 error::{ErrorKind::*, Result},12 evaluate_named,13 function::builtin::ParamDefault,14 gc::GcHashMap,15 val::ThunkValue,16 Context, Pending, Thunk, Val,17};1819#[derive(Trace)]20struct EvaluateNamedThunk {21 ctx: Pending<Context>,22 name: IStr,23 value: LocExpr,24}2526impl ThunkValue for EvaluateNamedThunk {27 type Output = Val;28 fn get(self: Box<Self>) -> Result<Val> {29 evaluate_named(self.ctx.unwrap(), &self.value, self.name)30 }31}32333435363738394041pub fn parse_function_call(42 ctx: Context,43 body_ctx: Context,44 params: &ParamsDesc,45 args: &dyn ArgsLike,46 tailstrict: bool,47) -> Result<Context> {48 let mut passed_args =49 GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());50 if args.unnamed_len() > params.len() {51 bail!(TooManyArgsFunctionHas(52 params.len(),53 params54 .iter()55 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))56 .collect()57 ))58 }5960 let mut filled_named = 0;61 let mut filled_positionals = 0;6263 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {64 let name = params[id].0.clone();65 destruct(66 &name,67 arg,68 Pending::new_filled(ctx.clone()),69 &mut passed_args,70 )?;71 filled_positionals += 1;72 Ok(())73 })?;7475 args.named_iter(ctx, tailstrict, &mut |name, value| {76 77 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {78 bail!(UnknownFunctionParameter((name as &str).to_owned()));79 }80 if passed_args.insert(name.clone(), value).is_some() {81 bail!(BindingParameterASecondTime(name.clone()));82 }83 filled_named += 1;84 Ok(())85 })?;8687 if filled_named + filled_positionals < params.len() {88 89 90 let fctx = Context::new_future();91 let mut defaults = GcHashMap::with_capacity(92 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()93 - filled_named94 - filled_positionals,95 );9697 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {98 if let Some(name) = param.0.name() {99 if passed_args.contains_key(&name) {100 continue;101 }102 } else if idx < filled_positionals {103 continue;104 }105106 destruct(107 ¶m.0,108 Thunk::new(EvaluateNamedThunk {109 ctx: fctx.clone(),110 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),111 value: param.1.clone().expect("default exists"),112 }),113 fctx.clone(),114 &mut defaults,115 )?;116 if param.0.name().is_some() {117 filled_named += 1;118 } else {119 filled_positionals += 1;120 }121 }122123 124 if filled_named + filled_positionals != params.len() {125 for param in params.iter().skip(args.unnamed_len()) {126 let mut found = false;127 args.named_names(&mut |name| {128 if Some(name) == param.0.name().as_ref() {129 found = true;130 }131 });132 if !found {133 bail!(FunctionParameterNotBoundInCall(134 param.0.clone().name(),135 params136 .iter()137 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))138 .collect()139 ));140 }141 }142 unreachable!();143 }144145 Ok(body_ctx146 .extend(passed_args, None, None, None)147 .extend(defaults, None, None, None)148 .into_future(fctx))149 } else {150 let body_ctx = body_ctx.extend(passed_args, None, None, None);151 Ok(body_ctx)152 }153}154155156157158159160161162pub fn parse_builtin_call(163 ctx: Context,164 params: &[BuiltinParam],165 args: &dyn ArgsLike,166 tailstrict: bool,167) -> Result<Vec<Option<Thunk<Val>>>> {168 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];169 if args.unnamed_len() > params.len() {170 bail!(TooManyArgsFunctionHas(171 params.len(),172 params173 .iter()174 .map(|p| (p.name().as_str().map(IStr::from), p.default()))175 .collect()176 ))177 }178179 let mut filled_args = 0;180181 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {182 passed_args[id] = Some(arg);183 filled_args += 1;184 Ok(())185 })?;186187 args.named_iter(ctx, tailstrict, &mut |name, arg| {188 189 let id = params190 .iter()191 .position(|p| p.name() == name)192 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;193 if replace(&mut passed_args[id], Some(arg)).is_some() {194 bail!(BindingParameterASecondTime(name.clone()));195 }196 filled_args += 1;197 Ok(())198 })?;199200 if filled_args < params.len() {201 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {202 if passed_args[id].is_some() {203 continue;204 }205 filled_args += 1;206 }207208 209 if filled_args != params.len() {210 for param in params.iter().skip(args.unnamed_len()) {211 let mut found = false;212 args.named_names(&mut |name| {213 if param.name() == name {214 found = true;215 }216 });217 if !found {218 bail!(FunctionParameterNotBoundInCall(219 param.name().as_str().map(IStr::from),220 params221 .iter()222 .map(|p| (p.name().as_str().map(IStr::from), p.default()))223 .collect()224 ));225 }226 }227 unreachable!();228 }229 }230 Ok(passed_args)231}232233234235pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {236 let fctx = Context::new_future();237238 let mut bindings = GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());239240 for param in params.iter() {241 if let Some(v) = ¶m.1 {242 destruct(243 ¶m.0.clone(),244 Thunk::new(EvaluateNamedThunk {245 ctx: fctx.clone(),246 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),247 value: v.clone(),248 }),249 fctx.clone(),250 &mut bindings,251 )?;252 } else {253 destruct(254 ¶m.0,255 {256 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());257 let params = params.clone();258 Thunk!(move || Err(FunctionParameterNotBoundInCall(259 Some(param_name),260 params261 .iter()262 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))263 .collect(),264 )265 .into()))266 },267 fctx.clone(),268 &mut bindings,269 )?;270 }271 }272273 Ok(body_ctx274 .extend(bindings, None, None, None)275 .into_future(fctx))276}