1use jrsonnet_gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{LocExpr, ParamsDesc};45use super::{6 arglike::ArgsLike,7 builtin::{BuiltinParam, BuiltinParamName},8};9use crate::{10 destructure::destruct,11 error::{Error::*, Result},12 evaluate_named,13 gc::GcHashMap,14 tb, throw,15 val::ThunkValue,16 Context, Pending, State, 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>, s: State) -> Result<Val> {29 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)30 }31}32333435363738394041pub fn parse_function_call(42 s: State,43 ctx: Context,44 body_ctx: Context,45 params: &ParamsDesc,46 args: &dyn ArgsLike,47 tailstrict: bool,48) -> Result<Context> {49 let mut passed_args = GcHashMap::with_capacity(params.len());50 if args.unnamed_len() > params.len() {51 throw!(TooManyArgsFunctionHas(52 params.len(),53 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()54 ))55 }5657 let mut filled_named = 0;58 let mut filled_positionals = 0;5960 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {61 let name = params[id].0.clone();62 destruct(63 &name,64 arg,65 Pending::new_filled(ctx.clone()),66 &mut passed_args,67 )?;68 filled_positionals += 1;69 Ok(())70 })?;7172 args.named_iter(s, ctx, tailstrict, &mut |name, value| {73 74 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {75 throw!(UnknownFunctionParameter((name as &str).to_owned()));76 }77 if passed_args.insert(name.clone(), value).is_some() {78 throw!(BindingParameterASecondTime(name.clone()));79 }80 filled_named += 1;81 Ok(())82 })?;8384 if filled_named + filled_positionals < params.len() {85 86 87 let fctx = Context::new_future();88 let mut defaults =89 GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);9091 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {92 if let Some(name) = param.0.name() {93 if passed_args.contains_key(&name) {94 continue;95 }96 } else if idx < filled_positionals {97 continue;98 }99100 destruct(101 ¶m.0,102 Thunk::new(tb!(EvaluateNamedThunk {103 ctx: fctx.clone(),104 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),105 value: param.1.clone().expect("default exists"),106 })),107 fctx.clone(),108 &mut defaults,109 )?;110 if param.0.name().is_some() {111 filled_named += 1;112 } else {113 filled_positionals += 1;114 }115 }116117 118 if filled_named + filled_positionals != params.len() {119 for param in params.iter().skip(args.unnamed_len()) {120 let mut found = false;121 args.named_names(&mut |name| {122 if Some(name) == param.0.name().as_ref() {123 found = true;124 }125 });126 if !found {127 throw!(FunctionParameterNotBoundInCall(128 param129 .0130 .clone()131 .name()132 .unwrap_or_else(|| "<destruct>".into()),133 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()134 ));135 }136 }137 unreachable!();138 }139140 Ok(body_ctx141 .extend(passed_args, None, None, None)142 .extend(defaults, None, None, None)143 .into_future(fctx))144 } else {145 let body_ctx = body_ctx.extend(passed_args, None, None, None);146 Ok(body_ctx)147 }148}149150151152153154155156157pub fn parse_builtin_call(158 s: State,159 ctx: Context,160 params: &[BuiltinParam],161 args: &dyn ArgsLike,162 tailstrict: bool,163) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {164 let mut passed_args = GcHashMap::with_capacity(params.len());165 if args.unnamed_len() > params.len() {166 throw!(TooManyArgsFunctionHas(167 params.len(),168 params169 .iter()170 .map(|p| (Some(p.name.as_ref().into()), p.has_default))171 .collect()172 ))173 }174175 let mut filled_args = 0;176177 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {178 let name = params[id].name.clone();179 passed_args.insert(name, arg);180 filled_args += 1;181 Ok(())182 })?;183184 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {185 186 let p = params187 .iter()188 .find(|p| p.name == name as &str)189 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;190 if passed_args.insert(p.name.clone(), arg).is_some() {191 throw!(BindingParameterASecondTime(name.clone()));192 }193 filled_args += 1;194 Ok(())195 })?;196197 if filled_args < params.len() {198 for param in params.iter().filter(|p| p.has_default) {199 if passed_args.contains_key(¶m.name) {200 continue;201 }202 filled_args += 1;203 }204205 206 if filled_args != params.len() {207 for param in params.iter().skip(args.unnamed_len()) {208 let mut found = false;209 args.named_names(&mut |name| {210 if name as &str == ¶m.name as &str {211 found = true;212 }213 });214 if !found {215 throw!(FunctionParameterNotBoundInCall(216 param.name.clone().into(),217 params218 .iter()219 .map(|p| (Some(p.name.as_ref().into()), p.has_default))220 .collect()221 ));222 }223 }224 unreachable!();225 }226 }227 Ok(passed_args)228}229230231232pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {233 #[derive(Trace)]234 struct DependsOnUnbound(IStr, ParamsDesc);235 impl ThunkValue for DependsOnUnbound {236 type Output = Val;237 fn get(self: Box<Self>, _: State) -> Result<Val> {238 Err(FunctionParameterNotBoundInCall(239 self.0.clone(),240 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),241 )242 .into())243 }244 }245246 let fctx = Context::new_future();247248 let mut bindings = GcHashMap::new();249250 for param in params.iter() {251 if let Some(v) = ¶m.1 {252 destruct(253 ¶m.0.clone(),254 Thunk::new(tb!(EvaluateNamedThunk {255 ctx: fctx.clone(),256 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),257 value: v.clone(),258 })),259 fctx.clone(),260 &mut bindings,261 )?;262 } else {263 destruct(264 ¶m.0,265 Thunk::new(tb!(DependsOnUnbound(266 param.0.name().unwrap_or_else(|| "<destruct>".into()),267 params.clone()268 ))),269 fctx.clone(),270 &mut bindings,271 )?;272 }273 }274275 Ok(body_ctx276 .extend(bindings, None, None, None)277 .into_future(fctx))278}