difftreelog
feat destruct function arguments
in: master
5 files changed
crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -12,7 +12,7 @@
};
#[allow(clippy::too_many_lines)]
-fn destruct(
+pub fn destruct(
d: &Destruct,
parent: Thunk<Val>,
new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -59,7 +59,7 @@
}
impl FuncDesc {
/// Create body context, but fill arguments without defaults with lazy error
- pub fn default_body_context(&self) -> Context {
+ pub fn default_body_context(&self) -> Result<Context> {
parse_default_function_call(self.ctx.clone(), &self.params)
}
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth1use 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}3132/// Creates correct [context](Context) for function body evaluation returning error on invalid call.33///34/// ## Parameters35/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)36/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)37/// * `params`: function parameters' definition38/// * `args`: passed function arguments39/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily40pub 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 // FIXME: O(n) for arg existence check64 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 // Some args are unset, but maybe we have defaults for them76 // Default values should be created in newly created context77 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 // Some args still wasn't filled97 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}121122/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead123///124/// ## Parameters125/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)126/// * `params`: function parameters' definition127/// * `args`: passed function arguments128/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily129pub 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 // FIXME: O(n) for arg existence check152 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 // Some args still wasn't filled172 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}189190/// Creates Context, which has all argument default values applied191/// and with unbound values causing error to be returned192pub 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}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}3233/// Creates correct [context](Context) for function body evaluation returning error on invalid call.34///35/// ## Parameters36/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)37/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)38/// * `params`: function parameters' definition39/// * `args`: passed function arguments40/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily41pub 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 // FIXME: O(n) for arg existence check66 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 // Some args are unset, but maybe we have defaults for them78 // Default values should be created in newly created context79 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 // Some args still weren't filled109 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}139140/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead141///142/// ## Parameters143/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)144/// * `params`: function parameters' definition145/// * `args`: passed function arguments146/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily147pub 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 // FIXME: O(n) for arg existence check170 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 // Some args still wasn't filled190 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}207208/// Creates Context, which has all argument default values applied209/// and with unbound values causing error to be returned210pub 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}crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -152,7 +152,7 @@
/// name, default value
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-pub struct Param(pub IStr, pub Option<LocExpr>);
+pub struct Param(pub Destruct, pub Option<LocExpr>);
/// Defined function parameters
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -206,6 +206,7 @@
},
}
impl Destruct {
+ /// Name of destructure, used for function parameter names
pub fn name(&self) -> Option<IStr> {
match self {
Self::Full(name) => Some(name.clone()),
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -59,7 +59,7 @@
rule keyword(id: &'static str) -> ()
= ##parse_string_literal(id) end_of_ident()
- pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
+ pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
pub rule params(s: &ParserSettings) -> expr::ParamsDesc
= params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }
/ { expr::ParamsDesc(Rc::new(Vec::new())) }