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,13 tb, throw,14 val::ThunkValue,15 Context, Pending, State, Thunk, Val,16};1718#[derive(Trace)]19struct EvaluateNamedThunk {20 ctx: Pending<Context>,21 name: IStr,22 value: LocExpr,23}2425impl ThunkValue for EvaluateNamedThunk {26 type Output = Val;27 fn get(self: Box<Self>, s: State) -> Result<Val> {28 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)29 }30}31323334353637383940pub fn parse_function_call(41 s: State,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 = GcHashMap::with_capacity(params.len());49 if args.unnamed_len() > params.len() {50 throw!(TooManyArgsFunctionHas(params.len()))51 }5253 let mut filled_args = 0;5455 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {56 let name = params[id].0.clone();57 passed_args.insert(name, arg);58 filled_args += 1;59 Ok(())60 })?;6162 args.named_iter(s, ctx, tailstrict, &mut |name, value| {63 64 if !params.iter().any(|p| &p.0 == name) {65 throw!(UnknownFunctionParameter((name as &str).to_owned()));66 }67 if passed_args.insert(name.clone(), value).is_some() {68 throw!(BindingParameterASecondTime(name.clone()));69 }70 filled_args += 1;71 Ok(())72 })?;7374 if filled_args < params.len() {75 76 77 let fctx = Context::new_future();78 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);7980 for param in params.iter().filter(|p| p.1.is_some()) {81 if passed_args.contains_key(¶m.0.clone()) {82 continue;83 }8485 defaults.insert(86 param.0.clone(),87 Thunk::new(tb!(EvaluateNamedThunk {88 ctx: fctx.clone(),89 name: param.0.clone(),90 value: param.1.clone().expect("default exists"),91 })),92 );93 filled_args += 1;94 }9596 97 if filled_args != params.len() {98 for param in params.iter().skip(args.unnamed_len()) {99 let mut found = false;100 args.named_names(&mut |name| {101 if name == ¶m.0 {102 found = true;103 }104 });105 if !found {106 throw!(FunctionParameterNotBoundInCall(param.0.clone()));107 }108 }109 unreachable!();110 }111112 Ok(body_ctx113 .extend(passed_args, None, None, None)114 .extend(defaults, None, None, None)115 .into_future(fctx))116 } else {117 let body_ctx = body_ctx.extend(passed_args, None, None, None);118 Ok(body_ctx)119 }120}121122123124125126127128129pub fn parse_builtin_call(130 s: State,131 ctx: Context,132 params: &[BuiltinParam],133 args: &dyn ArgsLike,134 tailstrict: bool,135) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {136 let mut passed_args = GcHashMap::with_capacity(params.len());137 if args.unnamed_len() > params.len() {138 throw!(TooManyArgsFunctionHas(params.len()))139 }140141 let mut filled_args = 0;142143 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {144 let name = params[id].name.clone();145 passed_args.insert(name, arg);146 filled_args += 1;147 Ok(())148 })?;149150 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {151 152 let p = params153 .iter()154 .find(|p| p.name == name as &str)155 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;156 if passed_args.insert(p.name.clone(), arg).is_some() {157 throw!(BindingParameterASecondTime(name.clone()));158 }159 filled_args += 1;160 Ok(())161 })?;162163 if filled_args < params.len() {164 for param in params.iter().filter(|p| p.has_default) {165 if passed_args.contains_key(¶m.name) {166 continue;167 }168 filled_args += 1;169 }170171 172 if filled_args != params.len() {173 for param in params.iter().skip(args.unnamed_len()) {174 let mut found = false;175 args.named_names(&mut |name| {176 if name as &str == ¶m.name as &str {177 found = true;178 }179 });180 if !found {181 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));182 }183 }184 unreachable!();185 }186 }187 Ok(passed_args)188}189190191192pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {193 #[derive(Trace)]194 struct DependsOnUnbound(IStr);195 impl ThunkValue for DependsOnUnbound {196 type Output = Val;197 fn get(self: Box<Self>, _: State) -> Result<Val> {198 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())199 }200 }201202 let fctx = Context::new_future();203204 let mut bindings = GcHashMap::new();205206 for param in params.iter() {207 if let Some(v) = ¶m.1 {208 bindings.insert(209 param.0.clone(),210 Thunk::new(tb!(EvaluateNamedThunk {211 ctx: fctx.clone(),212 name: param.0.clone(),213 value: v.clone(),214 })),215 );216 } else {217 bindings.insert(218 param.0.clone(),219 Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),220 );221 }222 }223224 body_ctx225 .extend(bindings, None, None, None)226 .into_future(fctx)227}