1use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};2use closure::closure;3use jrsonnet_parser::{ArgsDesc, ParamsDesc};4use rustc_hash::FxHashMap;5use std::{collections::HashMap, hash::BuildHasherDefault, rc::Rc};67const NO_DEFAULT_CONTEXT: &str =8 "no default context set for call with defined default parameter value";91011121314151617pub fn parse_function_call(18 ctx: Context,19 body_ctx: Option<Context>,20 params: &ParamsDesc,21 args: &ArgsDesc,22 tailstrict: bool,23) -> Result<Context> {24 let mut out = HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());25 let mut positioned_args = vec![None; params.0.len()];26 for (id, arg) in args.iter().enumerate() {27 let idx = if let Some(name) = &arg.0 {28 params29 .iter()30 .position(|p| *p.0 == *name)31 .ok_or_else(|| UnknownFunctionParameter(name.clone()))?32 } else {33 id34 };3536 if idx >= params.len() {37 throw!(TooManyArgsFunctionHas(params.len()));38 }39 if positioned_args[idx].is_some() {40 throw!(BindingParameterASecondTime(params[idx].0.clone()));41 }42 positioned_args[idx] = Some(arg.1.clone());43 }44 45 for (id, p) in params.iter().enumerate() {46 let (ctx, expr) = if let Some(arg) = &positioned_args[id] {47 (ctx.clone(), arg)48 } else if let Some(default) = &p.1 {49 (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)50 } else {51 throw!(FunctionParameterNotBoundInCall(p.0.clone()));52 };53 let val = if tailstrict {54 resolved_lazy_val!(evaluate(ctx, expr)?)55 } else {56 lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr)))57 };58 out.insert(p.0.clone(), val);59 }6061 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))62}6364pub fn parse_function_call_map(65 ctx: Context,66 body_ctx: Option<Context>,67 params: &ParamsDesc,68 args: &HashMap<Rc<str>, Val>,69 tailstrict: bool,70) -> Result<Context> {71 let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());72 let mut positioned_args = vec![None; params.0.len()];73 for (name, val) in args.iter() {74 let idx = params75 .iter()76 .position(|p| *p.0 == **name)77 .ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?;7879 if idx >= params.len() {80 throw!(TooManyArgsFunctionHas(params.len()));81 }82 if positioned_args[idx].is_some() {83 throw!(BindingParameterASecondTime(params[idx].0.clone()));84 }85 positioned_args[idx] = Some(val.clone());86 }87 88 for (id, p) in params.iter().enumerate() {89 let val = if let Some(arg) = positioned_args[id].take() {90 resolved_lazy_val!(arg)91 } else if let Some(default) = &p.1 {92 if tailstrict {93 resolved_lazy_val!(evaluate(94 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),95 default96 )?)97 } else {98 let body_ctx = body_ctx.clone();99 let default = default.clone();100 lazy_val!(move || {101 evaluate(body_ctx.clone().expect(NO_DEFAULT_CONTEXT), &default)102 })103 }104 } else {105 throw!(FunctionParameterNotBoundInCall(p.0.clone()));106 };107 out.insert(p.0.clone(), val);108 }109110 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))111}112113pub(crate) fn place_args(114 ctx: Context,115 body_ctx: Option<Context>,116 params: &ParamsDesc,117 args: &[Val],118) -> Result<Context> {119 let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());120 let mut positioned_args = vec![None; params.0.len()];121 for (id, arg) in args.iter().enumerate() {122 if id >= params.len() {123 throw!(TooManyArgsFunctionHas(params.len()));124 }125 positioned_args[id] = Some(arg);126 }127 128 for (id, p) in params.iter().enumerate() {129 let val = if let Some(arg) = &positioned_args[id] {130 (*arg).clone()131 } else if let Some(default) = &p.1 {132 evaluate(ctx.clone(), default)?133 } else {134 throw!(FunctionParameterNotBoundInCall(p.0.clone()));135 };136 out.insert(p.0.clone(), resolved_lazy_val!(val));137 }138139 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))140}141142#[macro_export]143macro_rules! parse_args {144 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [145 $($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?146 ], $handler:block) => {{147 use crate::{throw, error::Error::*};148 let args = $args;149 if args.len() > $total_args {150 throw!(TooManyArgsFunctionHas($total_args));151 }152 $(153 if args.len() <= $id {154 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));155 }156 let $name = &args[$id];157 if $name.0.is_some() {158 if $name.0.as_ref().unwrap() != stringify!($name) {159 throw!(IntristicArgumentReorderingIsNotSupportedYet);160 }161 }162 let $name = evaluate($ctx.clone(), &$name.1)?;163 $(164 match $name {165 $($p(_))|+ => {},166 _ => throw!(TypeMismatch(167 concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),168 $nt, $name.value_type()?169 )),170 };171 $(172 let $name = match $name {173 $a(v) => v,174 _ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),175 };176 )*177 )*178 )+179 ($handler as crate::Result<_>)180 }};181}182183#[test]184fn test() -> Result<()> {185 use crate::val::ValType;186 use jrsonnet_parser::*;187 let state = crate::EvaluationState::default();188 let evaluator = state.with_stdlib();189 let ctx = evaluator.create_default_context()?;190 evaluator.run_in_state(|| {191 parse_args!(ctx, "test", ArgsDesc(vec![192 Arg(None, el!(Expr::Num(2.0))),193 Arg(Some("b".into()), el!(Expr::Num(1.0))),194 ]), 2, [195 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];196 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];197 ], {198 assert!((a - 2.0).abs() <= f64::EPSILON);199 assert!((b - 1.0).abs() <= f64::EPSILON);200 Ok(())201 })202 .unwrap();203 Ok(())204 })205}