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 destructure::destruct,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(51 params.len(),52 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()53 ))54 }5556 let mut filled_named = 0;57 let mut filled_positionals = 0;5859 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {60 let name = params[id].0.clone();61 destruct(62 &name,63 arg,64 Pending::new_filled(ctx.clone()),65 &mut passed_args,66 )?;67 filled_positionals += 1;68 Ok(())69 })?;7071 args.named_iter(s, ctx, tailstrict, &mut |name, value| {72 73 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {74 throw!(UnknownFunctionParameter((name as &str).to_owned()));75 }76 if passed_args.insert(name.clone(), value).is_some() {77 throw!(BindingParameterASecondTime(name.clone()));78 }79 filled_named += 1;80 Ok(())81 })?;8283 if filled_named + filled_positionals < params.len() {84 85 86 let fctx = Context::new_future();87 let mut defaults =88 GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);8990 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {91 if let Some(name) = param.0.name() {92 if passed_args.contains_key(&name) {93 continue;94 }95 } else if idx < filled_positionals {96 continue;97 }9899 destruct(100 ¶m.0,101 Thunk::new(tb!(EvaluateNamedThunk {102 ctx: fctx.clone(),103 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),104 value: param.1.clone().expect("default exists"),105 })),106 fctx.clone(),107 &mut defaults,108 )?;109 if param.0.name().is_some() {110 filled_named += 1;111 } else {112 filled_positionals += 1;113 }114 }115116 117 if filled_named + filled_positionals != params.len() {118 for param in params.iter().skip(args.unnamed_len()) {119 let mut found = false;120 args.named_names(&mut |name| {121 if Some(name) == param.0.name().as_ref() {122 found = true;123 }124 });125 if !found {126 throw!(FunctionParameterNotBoundInCall(127 param.0.clone().name(),128 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()129 ));130 }131 }132 unreachable!();133 }134135 Ok(body_ctx136 .extend(passed_args, None, None, None)137 .extend(defaults, None, None, None)138 .into_future(fctx))139 } else {140 let body_ctx = body_ctx.extend(passed_args, None, None, None);141 Ok(body_ctx)142 }143}144145146147148149150151152pub fn parse_builtin_call(153 s: State,154 ctx: Context,155 params: &[BuiltinParam],156 args: &dyn ArgsLike,157 tailstrict: bool,158) -> Result<Vec<Option<Thunk<Val>>>> {159 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];160 if args.unnamed_len() > params.len() {161 throw!(TooManyArgsFunctionHas(162 params.len(),163 params164 .iter()165 .map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default))166 .collect()167 ))168 }169170 let mut filled_args = 0;171172 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {173 passed_args[id] = Some(arg);174 filled_args += 1;175 Ok(())176 })?;177178 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {179 180 let id = params181 .iter()182 .position(|p| {183 p.name184 .as_ref()185 .map(|v| &v as &str == name as &str)186 .unwrap_or(false)187 })188 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;189 if replace(&mut passed_args[id], Some(arg)).is_some() {190 throw!(BindingParameterASecondTime(name.clone()));191 }192 filled_args += 1;193 Ok(())194 })?;195196 if filled_args < params.len() {197 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) {198 if passed_args[id].is_some() {199 continue;200 }201 filled_args += 1;202 }203204 205 if filled_args != params.len() {206 for param in params.iter().skip(args.unnamed_len()) {207 let mut found = false;208 args.named_names(&mut |name| {209 if param210 .name211 .as_ref()212 .map(|v| &v as &str == name as &str)213 .unwrap_or(false)214 {215 found = true;216 }217 });218 if !found {219 throw!(FunctionParameterNotBoundInCall(220 param.name.as_ref().map(|v| v.as_ref().into()),221 params222 .iter()223 .map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default))224 .collect()225 ));226 }227 }228 unreachable!();229 }230 }231 Ok(passed_args)232}233234235236pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {237 #[derive(Trace)]238 struct DependsOnUnbound(IStr, ParamsDesc);239 impl ThunkValue for DependsOnUnbound {240 type Output = Val;241 fn get(self: Box<Self>, _: State) -> Result<Val> {242 Err(FunctionParameterNotBoundInCall(243 Some(self.0.clone()),244 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),245 )246 .into())247 }248 }249250 let fctx = Context::new_future();251252 let mut bindings = GcHashMap::new();253254 for param in params.iter() {255 if let Some(v) = ¶m.1 {256 destruct(257 ¶m.0.clone(),258 Thunk::new(tb!(EvaluateNamedThunk {259 ctx: fctx.clone(),260 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),261 value: v.clone(),262 })),263 fctx.clone(),264 &mut bindings,265 )?;266 } else {267 destruct(268 ¶m.0,269 Thunk::new(tb!(DependsOnUnbound(270 param.0.name().unwrap_or_else(|| "<destruct>".into()),271 params.clone()272 ))),273 fctx.clone(),274 &mut bindings,275 )?;276 }277 }278279 Ok(body_ctx280 .extend(bindings, None, None, None)281 .into_future(fctx))282}