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 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(params.len()))52 }5354 let mut filled_named = 0;55 let mut filled_positionals = 0;5657 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {58 let name = params[id].0.clone();59 destruct(&name, arg, &mut passed_args)?;60 filled_positionals += 1;61 Ok(())62 })?;6364 args.named_iter(s, ctx, tailstrict, &mut |name, value| {65 66 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {67 throw!(UnknownFunctionParameter((name as &str).to_owned()));68 }69 if passed_args.insert(name.clone(), value).is_some() {70 throw!(BindingParameterASecondTime(name.clone()));71 }72 filled_named += 1;73 Ok(())74 })?;7576 if filled_named + filled_positionals < params.len() {77 78 79 let fctx = Context::new_future();80 let mut defaults =81 GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);8283 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {84 if let Some(name) = param.0.name() {85 if passed_args.contains_key(&name) {86 continue;87 }88 } else if idx < filled_positionals {89 continue;90 }9192 destruct(93 ¶m.0,94 Thunk::new(tb!(EvaluateNamedThunk {95 ctx: fctx.clone(),96 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),97 value: param.1.clone().expect("default exists"),98 })),99 &mut defaults,100 )?;101 if param.0.name().is_some() {102 filled_named += 1;103 } else {104 filled_positionals += 1;105 }106 }107108 109 if filled_named + filled_positionals != params.len() {110 for param in params.iter().skip(args.unnamed_len()) {111 let mut found = false;112 args.named_names(&mut |name| {113 if Some(name) == param.0.name().as_ref() {114 found = true;115 }116 });117 if !found {118 throw!(FunctionParameterNotBoundInCall(119 param120 .0121 .clone()122 .name()123 .unwrap_or_else(|| "<destruct>".into())124 ));125 }126 }127 unreachable!();128 }129130 Ok(body_ctx131 .extend(passed_args, None, None, None)132 .extend(defaults, None, None, None)133 .into_future(fctx))134 } else {135 let body_ctx = body_ctx.extend(passed_args, None, None, None);136 Ok(body_ctx)137 }138}139140141142143144145146147pub fn parse_builtin_call(148 s: State,149 ctx: Context,150 params: &[BuiltinParam],151 args: &dyn ArgsLike,152 tailstrict: bool,153) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {154 let mut passed_args = GcHashMap::with_capacity(params.len());155 if args.unnamed_len() > params.len() {156 throw!(TooManyArgsFunctionHas(params.len()))157 }158159 let mut filled_args = 0;160161 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {162 let name = params[id].name.clone();163 passed_args.insert(name, arg);164 filled_args += 1;165 Ok(())166 })?;167168 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {169 170 let p = params171 .iter()172 .find(|p| p.name == name as &str)173 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;174 if passed_args.insert(p.name.clone(), arg).is_some() {175 throw!(BindingParameterASecondTime(name.clone()));176 }177 filled_args += 1;178 Ok(())179 })?;180181 if filled_args < params.len() {182 for param in params.iter().filter(|p| p.has_default) {183 if passed_args.contains_key(¶m.name) {184 continue;185 }186 filled_args += 1;187 }188189 190 if filled_args != params.len() {191 for param in params.iter().skip(args.unnamed_len()) {192 let mut found = false;193 args.named_names(&mut |name| {194 if name as &str == ¶m.name as &str {195 found = true;196 }197 });198 if !found {199 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));200 }201 }202 unreachable!();203 }204 }205 Ok(passed_args)206}207208209210pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {211 #[derive(Trace)]212 struct DependsOnUnbound(IStr);213 impl ThunkValue for DependsOnUnbound {214 type Output = Val;215 fn get(self: Box<Self>, _: State) -> Result<Val> {216 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())217 }218 }219220 let fctx = Context::new_future();221222 let mut bindings = GcHashMap::new();223224 for param in params.iter() {225 if let Some(v) = ¶m.1 {226 destruct(227 ¶m.0.clone(),228 Thunk::new(tb!(EvaluateNamedThunk {229 ctx: fctx.clone(),230 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),231 value: v.clone(),232 })),233 &mut bindings,234 )?;235 } else {236 destruct(237 ¶m.0,238 Thunk::new(tb!(DependsOnUnbound(239 param.0.name().unwrap_or_else(|| "<destruct>".into())240 ))),241 &mut bindings,242 )?;243 }244 }245246 Ok(body_ctx247 .extend(bindings, None, None, None)248 .into_future(fctx))249}