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.rsdiffbeforeafterboth1use std::fmt::Debug;23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};89use self::{10 builtin::{Builtin, StaticBuiltin},11 native::NativeDesc,12 parse::{parse_default_function_call, parse_function_call},13};14use crate::{evaluate, gc::TraceBox, typed::Any, Context, Result, State, Val};1516pub mod arglike;17pub mod builtin;18pub mod native;19pub mod parse;2021#[derive(Clone, Copy)]22pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);23impl<'l> CallLocation<'l> {24 pub const fn new(loc: &'l ExprLocation) -> Self {25 Self(Some(loc))26 }27}28impl CallLocation<'static> {29 pub const fn native() -> Self {30 Self(None)31 }32}3334/// Function implemented in jsonnet35#[derive(Debug, PartialEq, Trace)]36pub struct FuncDesc {37 /// In expressions like38 /// ```jsonnet39 /// local a = function() ...40 /// local a() ...41 /// { a: function() ... }42 /// { a() = ... }43 /// ```44 ///45 /// Deducted to `a`, unspecified otherwise46 pub name: IStr,47 /// Context, in which this function was evaluated48 ///49 /// I.e in50 /// ```jsonnet51 /// local a = 2;52 /// function() ...53 /// ```54 /// context will contain `a`55 pub ctx: Context,5657 pub params: ParamsDesc,58 pub body: LocExpr,59}60impl FuncDesc {61 /// Create body context, but fill arguments without defaults with lazy error62 pub fn default_body_context(&self) -> Context {63 parse_default_function_call(self.ctx.clone(), &self.params)64 }6566 /// Create context, with which body code will run67 pub fn call_body_context(68 &self,69 s: State,70 call_ctx: Context,71 args: &dyn ArgsLike,72 tailstrict: bool,73 ) -> Result<Context> {74 parse_function_call(75 s,76 call_ctx,77 self.ctx.clone(),78 &self.params,79 args,80 tailstrict,81 )82 }83}8485/// Any possible function value, including plain functions and user-provided builtins86#[allow(clippy::module_name_repetitions)]87#[derive(Trace, Clone)]88pub enum FuncVal {89 /// std.id90 Id,91 /// Plain function implemented in jsonnet92 Normal(Cc<FuncDesc>),93 /// Standard library function94 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),95 /// User-provided function96 Builtin(Cc<TraceBox<dyn Builtin>>),97}9899impl Debug for FuncVal {100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {101 match self {102 Self::Id => f.debug_tuple("Id").finish(),103 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),104 Self::StaticBuiltin(arg0) => {105 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()106 }107 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),108 }109 }110}111112impl FuncVal {113 pub fn into_native<D: NativeDesc>(self) -> D::Value {114 D::into_native(self)115 }116 pub fn params_len(&self) -> usize {117 match self {118 Self::Id => 1,119 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),120 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),121 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),122 }123 }124 pub fn name(&self) -> IStr {125 match self {126 Self::Id => "id".into(),127 Self::Normal(normal) => normal.name.clone(),128 Self::StaticBuiltin(builtin) => builtin.name().into(),129 Self::Builtin(builtin) => builtin.name().into(),130 }131 }132 pub fn evaluate(133 &self,134 s: State,135 call_ctx: Context,136 loc: CallLocation,137 args: &dyn ArgsLike,138 tailstrict: bool,139 ) -> Result<Val> {140 match self {141 Self::Id => {142 #[allow(clippy::unnecessary_wraps)]143 #[builtin]144 const fn builtin_id(v: Any) -> Result<Any> {145 Ok(v)146 }147 static ID: &builtin_id = &builtin_id {};148149 ID.call(s, call_ctx, loc, args)150 }151 Self::Normal(func) => {152 let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?;153 evaluate(s, body_ctx, &func.body)154 }155 Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args),156 Self::Builtin(b) => b.call(s, call_ctx, loc, args),157 }158 }159 pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {160 self.evaluate(s, Context::default(), CallLocation::native(), args, true)161 }162163 pub const fn is_identity(&self) -> bool {164 matches!(self, Self::Id)165 }166 pub const fn identity() -> Self {167 Self::Id168 }169}crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -7,6 +7,7 @@
builtin::{BuiltinParam, BuiltinParamName},
};
use crate::{
+ destructure::destruct,
error::{Error::*, Result},
evaluate_named,
gc::GcHashMap,
@@ -50,60 +51,77 @@
throw!(TooManyArgsFunctionHas(params.len()))
}
- let mut filled_args = 0;
+ let mut filled_named = 0;
+ let mut filled_positionals = 0;
args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
let name = params[id].0.clone();
- passed_args.insert(name, arg);
- filled_args += 1;
+ destruct(&name, arg, &mut passed_args)?;
+ filled_positionals += 1;
Ok(())
})?;
args.named_iter(s, ctx, tailstrict, &mut |name, value| {
// FIXME: O(n) for arg existence check
- if !params.iter().any(|p| &p.0 == name) {
+ if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {
throw!(UnknownFunctionParameter((name as &str).to_owned()));
}
if passed_args.insert(name.clone(), value).is_some() {
throw!(BindingParameterASecondTime(name.clone()));
}
- filled_args += 1;
+ filled_named += 1;
Ok(())
})?;
- if filled_args < params.len() {
+ if filled_named + filled_positionals < params.len() {
// Some args are unset, but maybe we have defaults for them
// Default values should be created in newly created context
let fctx = Context::new_future();
- let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
+ let mut defaults =
+ GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);
- for param in params.iter().filter(|p| p.1.is_some()) {
- if passed_args.contains_key(¶m.0.clone()) {
+ for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {
+ if let Some(name) = param.0.name() {
+ if passed_args.contains_key(&name) {
+ continue;
+ }
+ } else if idx < filled_positionals {
continue;
}
- defaults.insert(
- param.0.clone(),
+ destruct(
+ ¶m.0,
Thunk::new(tb!(EvaluateNamedThunk {
ctx: fctx.clone(),
- name: param.0.clone(),
+ name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: param.1.clone().expect("default exists"),
})),
- );
- filled_args += 1;
+ &mut defaults,
+ )?;
+ if param.0.name().is_some() {
+ filled_named += 1;
+ } else {
+ filled_positionals += 1;
+ }
}
- // Some args still wasn't filled
- if filled_args != params.len() {
+ // Some args still weren't filled
+ if filled_named + filled_positionals != params.len() {
for param in params.iter().skip(args.unnamed_len()) {
let mut found = false;
args.named_names(&mut |name| {
- if name == ¶m.0 {
+ if Some(name) == param.0.name().as_ref() {
found = true;
}
});
if !found {
- throw!(FunctionParameterNotBoundInCall(param.0.clone()));
+ throw!(FunctionParameterNotBoundInCall(
+ param
+ .0
+ .clone()
+ .name()
+ .unwrap_or_else(|| "<destruct>".into())
+ ));
}
}
unreachable!();
@@ -189,7 +207,7 @@
/// Creates Context, which has all argument default values applied
/// and with unbound values causing error to be returned
-pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
+pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
#[derive(Trace)]
struct DependsOnUnbound(IStr);
impl ThunkValue for DependsOnUnbound {
@@ -205,23 +223,27 @@
for param in params.iter() {
if let Some(v) = ¶m.1 {
- bindings.insert(
- param.0.clone(),
+ destruct(
+ ¶m.0.clone(),
Thunk::new(tb!(EvaluateNamedThunk {
ctx: fctx.clone(),
- name: param.0.clone(),
+ name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: v.clone(),
})),
- );
+ &mut bindings,
+ )?;
} else {
- bindings.insert(
- param.0.clone(),
- Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),
- );
+ destruct(
+ ¶m.0,
+ Thunk::new(tb!(DependsOnUnbound(
+ param.0.name().unwrap_or_else(|| "<destruct>".into())
+ ))),
+ &mut bindings,
+ )?;
}
}
- body_ctx
+ Ok(body_ctx
.extend(bindings, None, None, None)
- .into_future(fctx)
+ .into_future(fctx))
}
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())) }