1use crate::{error::Error::*, evaluate, throw, Context, LazyVal, LazyValValue, Result, Val};2use gc::{custom_trace, Finalize, Trace};3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};5use rustc_hash::FxHashMap;6use std::{collections::HashMap, hash::BuildHasherDefault};78const NO_DEFAULT_CONTEXT: &str =9 "no default context set for call with defined default parameter value";10111213141516171819pub fn parse_function_call(20 ctx: Context,21 body_ctx: Option<Context>,22 params: &ParamsDesc,23 args: &ArgsDesc,24 tailstrict: bool,25) -> Result<Context> {26 let mut out = HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());27 let mut positioned_args = vec![None; params.0.len()];28 for (id, arg) in args.iter().enumerate() {29 let idx = if let Some(name) = &arg.0 {30 params31 .iter()32 .position(|p| *p.0 == *name)33 .ok_or_else(|| UnknownFunctionParameter(name.clone()))?34 } else {35 id36 };3738 if idx >= params.len() {39 throw!(TooManyArgsFunctionHas(params.len()));40 }41 if positioned_args[idx].is_some() {42 throw!(BindingParameterASecondTime(params[idx].0.clone()));43 }44 positioned_args[idx] = Some(arg.1.clone());45 }46 47 for (id, p) in params.iter().enumerate() {48 let (ctx, expr) = if let Some(arg) = &positioned_args[id] {49 (ctx.clone(), arg)50 } else if let Some(default) = &p.1 {51 (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)52 } else {53 throw!(FunctionParameterNotBoundInCall(p.0.clone()));54 };55 let val = if tailstrict {56 LazyVal::new_resolved(evaluate(ctx, expr)?)57 } else {58 struct EvaluateLazyVal {59 context: Context,60 expr: LocExpr,61 }62 impl Finalize for EvaluateLazyVal {}63 unsafe impl Trace for EvaluateLazyVal {64 custom_trace!(this, {65 mark(&this.context);66 mark(&this.expr);67 });68 }69 impl LazyValValue for EvaluateLazyVal {70 fn get(self: Box<Self>) -> Result<Val> {71 evaluate(self.context, &self.expr)72 }73 }7475 LazyVal::new(Box::new(EvaluateLazyVal {76 context: ctx.clone(),77 expr: expr.clone(),78 }))79 };80 out.insert(p.0.clone(), val);81 }8283 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))84}8586pub fn parse_function_call_map(87 ctx: Context,88 body_ctx: Option<Context>,89 params: &ParamsDesc,90 args: &HashMap<IStr, Val>,91 tailstrict: bool,92) -> Result<Context> {93 let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());94 let mut positioned_args = vec![None; params.0.len()];95 for (name, val) in args.iter() {96 let idx = params97 .iter()98 .position(|p| *p.0 == **name)99 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;100101 if idx >= params.len() {102 throw!(TooManyArgsFunctionHas(params.len()));103 }104 if positioned_args[idx].is_some() {105 throw!(BindingParameterASecondTime(params[idx].0.clone()));106 }107 positioned_args[idx] = Some(val.clone());108 }109 110 for (id, p) in params.iter().enumerate() {111 let val = if let Some(arg) = positioned_args[id].take() {112 LazyVal::new_resolved(arg)113 } else if let Some(default) = &p.1 {114 if tailstrict {115 LazyVal::new_resolved(evaluate(116 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),117 default,118 )?)119 } else {120 let body_ctx = body_ctx.clone();121 let default = default.clone();122 #[derive(Trace, Finalize)]123 struct EvaluateLazyVal {124 body_ctx: Option<Context>,125 default: LocExpr,126 }127 impl LazyValValue for EvaluateLazyVal {128 fn get(self: Box<Self>) -> Result<Val> {129 evaluate(130 self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),131 &self.default,132 )133 }134 }135 LazyVal::new(Box::new(EvaluateLazyVal { body_ctx, default }))136 }137 } else {138 throw!(FunctionParameterNotBoundInCall(p.0.clone()));139 };140 out.insert(p.0.clone(), val);141 }142143 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))144}145146pub fn place_args(147 ctx: Context,148 body_ctx: Option<Context>,149 params: &ParamsDesc,150 args: &[Val],151) -> Result<Context> {152 let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());153 let mut positioned_args = vec![None; params.0.len()];154 for (id, arg) in args.iter().enumerate() {155 if id >= params.len() {156 throw!(TooManyArgsFunctionHas(params.len()));157 }158 positioned_args[id] = Some(arg);159 }160 161 for (id, p) in params.iter().enumerate() {162 let val = if let Some(arg) = &positioned_args[id] {163 (*arg).clone()164 } else if let Some(default) = &p.1 {165 evaluate(ctx.clone(), default)?166 } else {167 throw!(FunctionParameterNotBoundInCall(p.0.clone()));168 };169 out.insert(p.0.clone(), LazyVal::new_resolved(val));170 }171172 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))173}174175#[macro_export]176macro_rules! parse_args {177 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [178 $($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?179 ], $handler:block) => {{180 use $crate::{error::Error::*, throw, evaluate, push_stack_frame, typed::CheckType};181182 let args = $args;183 if args.len() > $total_args {184 throw!(TooManyArgsFunctionHas($total_args));185 }186 $(187 if args.len() <= $id {188 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));189 }190 let $name = &args[$id];191 if $name.0.is_some() {192 if $name.0.as_ref().unwrap() != stringify!($name) {193 throw!(IntrinsicArgumentReorderingIsNotSupportedYet);194 }195 }196 let $name = push_stack_frame(None, || format!("evaluating argument"), || {197 let value = evaluate($ctx.clone(), &$name.1)?;198 $ty.check(&value)?;199 Ok(value)200 })?;201 $(202 let $name = if let $match(v) = $name {203 v204 } else {205 unreachable!();206 };207 )?208 )+209 ($handler as crate::Result<_>)210 }};211}