difftreelog
feat lazy evaluation of function default params
in: master
Fixes #59
7 files changed
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -73,6 +73,9 @@
.cloned()
.ok_or(VariableIsNotDefined(name))?)
}
+ pub fn contains_binding(&self, name: IStr) -> bool {
+ self.0.bindings.contains_key(&name)
+ }
pub fn into_future(self, ctx: FutureWrapper<Self>) -> Self {
{
ctx.0.borrow_mut().replace(self);
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth1use crate::{2 builtin::{format::FormatError, sort::SortError},3 typed::TypeLocError,4};5use jrsonnet_gc::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};8use jrsonnet_types::ValType;9use std::{10 path::{Path, PathBuf},11 rc::Rc,12};13use thiserror::Error;1415#[derive(Error, Debug, Clone, Trace)]16#[trivially_drop]17pub enum Error {18 #[error("intrinsic not found: {0}")]19 IntrinsicNotFound(IStr),20 #[error("argument reordering in intrisics not supported yet")]21 IntrinsicArgumentReorderingIsNotSupportedYet,2223 #[error("operator {0} does not operate on type {1}")]24 UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),25 #[error("binary operation {1} {0} {2} is not implemented")]26 BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),2728 #[error("no top level object in this context")]29 NoTopLevelObjectFound,30 #[error("self is only usable inside objects")]31 CantUseSelfOutsideOfObject,32 #[error("no super found")]33 NoSuperFound,3435 #[error("for loop can only iterate over arrays")]36 InComprehensionCanOnlyIterateOverArray,3738 #[error("array out of bounds: {0} is not within [0,{1})")]39 ArrayBoundsError(usize, usize),4041 #[error("assert failed: {0}")]42 AssertionFailed(IStr),4344 #[error("variable is not defined: {0}")]45 VariableIsNotDefined(IStr),46 #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]47 TypeMismatch(&'static str, Vec<ValType>, ValType),48 #[error("no such field: {0}")]49 NoSuchField(IStr),5051 #[error("only functions can be called, got {0}")]52 OnlyFunctionsCanBeCalledGot(ValType),53 #[error("parameter {0} is not defined")]54 UnknownFunctionParameter(String),55 #[error("argument {0} is already bound")]56 BindingParameterASecondTime(IStr),57 #[error("too many args, function has {0}")]58 TooManyArgsFunctionHas(usize),59 #[error("founction argument is not passed: {0}")]60 FunctionParameterNotBoundInCall(IStr),6162 #[error("external variable is not defined: {0}")]63 UndefinedExternalVariable(IStr),64 #[error("native is not defined: {0}")]65 UndefinedExternalFunction(IStr),6667 #[error("field name should be string, got {0}")]68 FieldMustBeStringGot(ValType),6970 #[error("attempted to index array with string {0}")]71 AttemptedIndexAnArrayWithString(IStr),72 #[error("{0} index type should be {1}, got {2}")]73 ValueIndexMustBeTypeGot(ValType, ValType, ValType),74 #[error("cant index into {0}")]75 CantIndexInto(ValType),76 #[error("{0} is not indexable")]77 ValueIsNotIndexable(ValType),7879 #[error("super can't be used standalone")]80 StandaloneSuper,8182 #[error("can't resolve {1} from {0}")]83 ImportFileNotFound(PathBuf, PathBuf),84 #[error("resolved file not found: {0}")]85 ResolvedFileNotFound(PathBuf),86 #[error("imported file is not valid utf-8: {0:?}")]87 ImportBadFileUtf8(PathBuf),88 #[error("tried to import {1} from {0}, but imports is not supported")]89 ImportNotSupported(PathBuf, PathBuf),90 #[error(91 "syntax error, expected one of {}, got {:?}",92 .error.expected,93 .source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())94 )]95 ImportSyntaxError {96 path: Rc<Path>,97 source_code: IStr,98 #[unsafe_ignore_trace]99 error: Box<jrsonnet_parser::ParseError>,100 },101102 #[error("runtime error: {0}")]103 RuntimeError(IStr),104 #[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]105 StackOverflow,106 #[error("infinite recursion detected")]107 RecursiveLazyValueEvaluation,108 #[error("tried to index by fractional value")]109 FractionalIndex,110 #[error("attempted to divide by zero")]111 DivisionByZero,112113 #[error("string manifest output is not an string")]114 StringManifestOutputIsNotAString,115 #[error("stream manifest output is not an array")]116 StreamManifestOutputIsNotAArray,117 #[error("multi manifest output is not an object")]118 MultiManifestOutputIsNotAObject,119120 #[error("cant recurse stream manifest")]121 StreamManifestOutputCannotBeRecursed,122 #[error("stream manifest output cannot consist of raw strings")]123 StreamManifestCannotNestString,124125 #[error("{0}")]126 ImportCallbackError(String),127 #[error("invalid unicode codepoint: {0}")]128 InvalidUnicodeCodepointGot(u32),129130 #[error("format error: {0}")]131 Format(#[from] FormatError),132 #[error("type error: {0}")]133 TypeError(TypeLocError),134 #[error("sort error: {0}")]135 Sort(#[from] SortError),136137 #[cfg(feature = "anyhow-error")]138 #[error(transparent)]139 Other(Rc<anyhow::Error>),140}141142#[cfg(feature = "anyhow-error")]143impl From<anyhow::Error> for LocError {144 fn from(e: anyhow::Error) -> Self {145 Self::new(Error::Other(Rc::new(e)))146 }147}148149impl From<Error> for LocError {150 fn from(e: Error) -> Self {151 Self::new(e)152 }153}154155#[derive(Clone, Debug, Trace)]156#[trivially_drop]157pub struct StackTraceElement {158 pub location: Option<ExprLocation>,159 pub desc: String,160}161#[derive(Debug, Clone, Trace)]162#[trivially_drop]163pub struct StackTrace(pub Vec<StackTraceElement>);164165#[derive(Debug, Clone, Trace)]166#[trivially_drop]167pub struct LocError(Box<(Error, StackTrace)>);168impl LocError {169 pub fn new(e: Error) -> Self {170 Self(Box::new((e, StackTrace(vec![]))))171 }172173 pub const fn error(&self) -> &Error {174 &(self.0).0175 }176 pub fn error_mut(&mut self) -> &mut Error {177 &mut (self.0).0178 }179 pub const fn trace(&self) -> &StackTrace {180 &(self.0).1181 }182 pub fn trace_mut(&mut self) -> &mut StackTrace {183 &mut (self.0).1184 }185}186187pub type Result<V> = std::result::Result<V, LocError>;188189#[macro_export]190macro_rules! throw {191 ($e: expr) => {192 return Err($e.into())193 };194}1use crate::{2 builtin::{format::FormatError, sort::SortError},3 typed::TypeLocError,4};5use jrsonnet_gc::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};8use jrsonnet_types::ValType;9use std::{10 path::{Path, PathBuf},11 rc::Rc,12};13use thiserror::Error;1415#[derive(Error, Debug, Clone, Trace)]16#[trivially_drop]17pub enum Error {18 #[error("intrinsic not found: {0}")]19 IntrinsicNotFound(IStr),20 #[error("argument reordering in intrisics not supported yet")]21 IntrinsicArgumentReorderingIsNotSupportedYet,2223 #[error("operator {0} does not operate on type {1}")]24 UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),25 #[error("binary operation {1} {0} {2} is not implemented")]26 BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),2728 #[error("no top level object in this context")]29 NoTopLevelObjectFound,30 #[error("self is only usable inside objects")]31 CantUseSelfOutsideOfObject,32 #[error("no super found")]33 NoSuperFound,3435 #[error("for loop can only iterate over arrays")]36 InComprehensionCanOnlyIterateOverArray,3738 #[error("array out of bounds: {0} is not within [0,{1})")]39 ArrayBoundsError(usize, usize),4041 #[error("assert failed: {0}")]42 AssertionFailed(IStr),4344 #[error("variable is not defined: {0}")]45 VariableIsNotDefined(IStr),46 #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]47 TypeMismatch(&'static str, Vec<ValType>, ValType),48 #[error("no such field: {0}")]49 NoSuchField(IStr),5051 #[error("only functions can be called, got {0}")]52 OnlyFunctionsCanBeCalledGot(ValType),53 #[error("parameter {0} is not defined")]54 UnknownFunctionParameter(String),55 #[error("argument {0} is already bound")]56 BindingParameterASecondTime(IStr),57 #[error("too many args, function has {0}")]58 TooManyArgsFunctionHas(usize),59 #[error("function argument is not passed: {0}")]60 FunctionParameterNotBoundInCall(IStr),6162 #[error("external variable is not defined: {0}")]63 UndefinedExternalVariable(IStr),64 #[error("native is not defined: {0}")]65 UndefinedExternalFunction(IStr),6667 #[error("field name should be string, got {0}")]68 FieldMustBeStringGot(ValType),6970 #[error("attempted to index array with string {0}")]71 AttemptedIndexAnArrayWithString(IStr),72 #[error("{0} index type should be {1}, got {2}")]73 ValueIndexMustBeTypeGot(ValType, ValType, ValType),74 #[error("cant index into {0}")]75 CantIndexInto(ValType),76 #[error("{0} is not indexable")]77 ValueIsNotIndexable(ValType),7879 #[error("super can't be used standalone")]80 StandaloneSuper,8182 #[error("can't resolve {1} from {0}")]83 ImportFileNotFound(PathBuf, PathBuf),84 #[error("resolved file not found: {0}")]85 ResolvedFileNotFound(PathBuf),86 #[error("imported file is not valid utf-8: {0:?}")]87 ImportBadFileUtf8(PathBuf),88 #[error("tried to import {1} from {0}, but imports is not supported")]89 ImportNotSupported(PathBuf, PathBuf),90 #[error(91 "syntax error, expected one of {}, got {:?}",92 .error.expected,93 .source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())94 )]95 ImportSyntaxError {96 path: Rc<Path>,97 source_code: IStr,98 #[unsafe_ignore_trace]99 error: Box<jrsonnet_parser::ParseError>,100 },101102 #[error("runtime error: {0}")]103 RuntimeError(IStr),104 #[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]105 StackOverflow,106 #[error("infinite recursion detected")]107 RecursiveLazyValueEvaluation,108 #[error("tried to index by fractional value")]109 FractionalIndex,110 #[error("attempted to divide by zero")]111 DivisionByZero,112113 #[error("string manifest output is not an string")]114 StringManifestOutputIsNotAString,115 #[error("stream manifest output is not an array")]116 StreamManifestOutputIsNotAArray,117 #[error("multi manifest output is not an object")]118 MultiManifestOutputIsNotAObject,119120 #[error("cant recurse stream manifest")]121 StreamManifestOutputCannotBeRecursed,122 #[error("stream manifest output cannot consist of raw strings")]123 StreamManifestCannotNestString,124125 #[error("{0}")]126 ImportCallbackError(String),127 #[error("invalid unicode codepoint: {0}")]128 InvalidUnicodeCodepointGot(u32),129130 #[error("format error: {0}")]131 Format(#[from] FormatError),132 #[error("type error: {0}")]133 TypeError(TypeLocError),134 #[error("sort error: {0}")]135 Sort(#[from] SortError),136137 #[cfg(feature = "anyhow-error")]138 #[error(transparent)]139 Other(Rc<anyhow::Error>),140}141142#[cfg(feature = "anyhow-error")]143impl From<anyhow::Error> for LocError {144 fn from(e: anyhow::Error) -> Self {145 Self::new(Error::Other(Rc::new(e)))146 }147}148149impl From<Error> for LocError {150 fn from(e: Error) -> Self {151 Self::new(e)152 }153}154155#[derive(Clone, Debug, Trace)]156#[trivially_drop]157pub struct StackTraceElement {158 pub location: Option<ExprLocation>,159 pub desc: String,160}161#[derive(Debug, Clone, Trace)]162#[trivially_drop]163pub struct StackTrace(pub Vec<StackTraceElement>);164165#[derive(Debug, Clone, Trace)]166#[trivially_drop]167pub struct LocError(Box<(Error, StackTrace)>);168impl LocError {169 pub fn new(e: Error) -> Self {170 Self(Box::new((e, StackTrace(vec![]))))171 }172173 pub const fn error(&self) -> &Error {174 &(self.0).0175 }176 pub fn error_mut(&mut self) -> &mut Error {177 &mut (self.0).0178 }179 pub const fn trace(&self) -> &StackTrace {180 &(self.0).1181 }182 pub fn trace_mut(&mut self) -> &mut StackTrace {183 &mut (self.0).1184 }185}186187pub type Result<V> = std::result::Result<V, LocError>;188189#[macro_export]190macro_rules! throw {191 ($e: expr) => {192 return Err($e.into())193 };194}crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -1,4 +1,7 @@
-use crate::{error::Error::*, evaluate, throw, Context, LazyVal, LazyValValue, Result, Val};
+use crate::{
+ error::Error::*, evaluate, evaluate_named, throw, Context, FutureWrapper, LazyVal,
+ LazyValValue, Result, Val,
+};
use jrsonnet_gc::Trace;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};
@@ -8,6 +11,18 @@
const NO_DEFAULT_CONTEXT: &str =
"no default context set for call with defined default parameter value";
+#[derive(Trace)]
+#[trivially_drop]
+struct EvaluateLazyVal {
+ context: Context,
+ expr: LocExpr,
+}
+impl LazyValValue for EvaluateLazyVal {
+ fn get(self: Box<Self>) -> Result<Val> {
+ evaluate(self.context, &self.expr)
+ }
+}
+
/// Creates correct [context](Context) for function body evaluation returning error on invalid call.
///
/// ## Parameters
@@ -18,64 +33,119 @@
/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily
pub fn parse_function_call(
ctx: Context,
- body_ctx: Option<Context>,
+ body_ctx: Context,
params: &ParamsDesc,
args: &ArgsDesc,
tailstrict: bool,
) -> Result<Context> {
- let mut out = HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
- let mut positioned_args = vec![None; params.0.len()];
- for (id, arg) in args.iter().enumerate() {
- let idx = if let Some(name) = &arg.0 {
- params
- .iter()
- .position(|p| *p.0 == *name)
- .ok_or_else(|| UnknownFunctionParameter(name.clone()))?
- } else {
- id
- };
+ let mut passed_args =
+ HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+ if args.unnamed.len() > params.len() {
+ throw!(TooManyArgsFunctionHas(params.len()))
+ }
+
+ let mut filled_args = 0;
- if idx >= params.len() {
- throw!(TooManyArgsFunctionHas(params.len()));
+ for (id, arg) in args.unnamed.iter().enumerate() {
+ let name = params[id].0.clone();
+ passed_args.insert(
+ name,
+ if tailstrict {
+ LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)
+ } else {
+ LazyVal::new(Box::new(EvaluateLazyVal {
+ context: ctx.clone(),
+ expr: arg.clone(),
+ }))
+ },
+ );
+ filled_args += 1;
+ }
+
+ for (name, value) in args.named.iter() {
+ // FIXME: O(n) for arg existence check
+ if !params.iter().any(|p| &p.0 == name) {
+ throw!(UnknownFunctionParameter((name as &str).to_owned()));
}
- if positioned_args[idx].is_some() {
- throw!(BindingParameterASecondTime(params[idx].0.clone()));
+ if passed_args
+ .insert(
+ name.clone(),
+ if tailstrict {
+ LazyVal::new_resolved(evaluate(ctx.clone(), value)?)
+ } else {
+ LazyVal::new(Box::new(EvaluateLazyVal {
+ context: ctx.clone(),
+ expr: value.clone(),
+ }))
+ },
+ )
+ .is_some()
+ {
+ throw!(BindingParameterASecondTime(name.clone()));
}
- positioned_args[idx] = Some(arg.1.clone());
+ filled_args += 1;
}
- // Fill defaults
- for (id, p) in params.iter().enumerate() {
- let (ctx, expr) = if let Some(arg) = &positioned_args[id] {
- (ctx.clone(), arg)
- } else if let Some(default) = &p.1 {
- (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default)
- } else {
- throw!(FunctionParameterNotBoundInCall(p.0.clone()));
- };
- let val = if tailstrict {
- LazyVal::new_resolved(evaluate(ctx, expr)?)
- } else {
+
+ if filled_args < params.len() {
+ // Some args are unset, but maybe we have defaults for them
+ // Default values should be created in newly created context
+ let future_context = FutureWrapper::<Context>::new();
+ let mut defaults = HashMap::with_capacity_and_hasher(
+ params.len() - filled_args,
+ BuildHasherDefault::default(),
+ );
+
+ for param in params.iter().filter(|p| p.1.is_some()) {
+ if passed_args.contains_key(¶m.0.clone()) {
+ continue;
+ }
#[derive(Trace)]
#[trivially_drop]
- struct EvaluateLazyVal {
- context: Context,
- expr: LocExpr,
+ struct LazyNamedBinding {
+ future_context: FutureWrapper<Context>,
+ name: IStr,
+ value: LocExpr,
}
- impl LazyValValue for EvaluateLazyVal {
+ impl LazyValValue for LazyNamedBinding {
fn get(self: Box<Self>) -> Result<Val> {
- evaluate(self.context, &self.expr)
+ evaluate_named(self.future_context.unwrap(), &self.value, self.name)
}
}
+ LazyVal::new(Box::new(LazyNamedBinding {
+ future_context: future_context.clone(),
+ name: param.0.clone(),
+ value: param.1.clone().unwrap(),
+ }));
- LazyVal::new(Box::new(EvaluateLazyVal {
- context: ctx.clone(),
- expr: expr.clone(),
- }))
- };
- out.insert(p.0.clone(), val);
- }
+ defaults.insert(
+ param.0.clone(),
+ LazyVal::new(Box::new(LazyNamedBinding {
+ future_context: future_context.clone(),
+ name: param.0.clone(),
+ value: param.1.clone().unwrap(),
+ })),
+ );
+ filled_args += 1;
+ }
+
+ // Some args still wasn't filled
+ if filled_args != params.len() {
+ for param in params.iter().skip(args.unnamed.len()) {
+ if !args.named.iter().any(|a| a.0 == param.0) {
+ throw!(FunctionParameterNotBoundInCall(param.0.clone()));
+ }
+ }
+ unreachable!();
+ }
- Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))
+ Ok(body_ctx
+ .extend(passed_args, None, None, None)
+ .extend_bound(defaults)
+ .into_future(future_context))
+ } else {
+ let body_ctx = body_ctx.extend(passed_args, None, None, None);
+ Ok(body_ctx)
+ }
}
pub fn parse_function_call_map(
@@ -176,21 +246,25 @@
use $crate::{error::Error::*, throw, evaluate, push_stack_frame, typed::CheckType};
let args = $args;
- if args.len() > $total_args {
+ if args.unnamed.len() + args.named.len() > $total_args {
throw!(TooManyArgsFunctionHas($total_args));
}
$(
- if args.len() <= $id {
+ if args.unnamed.len() + args.named.len() <= $id {
throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));
}
- let $name = &args[$id];
- if $name.0.is_some() {
- if $name.0.as_ref().unwrap() != stringify!($name) {
+ // Is named
+ let $name = if $id >= $args.unnamed.len() {
+ let named = &args.named[$id - $args.unnamed.len()];
+ if &named.0 != stringify!($name) {
throw!(IntrinsicArgumentReorderingIsNotSupportedYet);
}
- }
+ &named.1
+ } else {
+ &$args.unnamed[$id]
+ };
let $name = push_stack_frame(None, || format!("evaluating argument"), || {
- let value = evaluate($ctx.clone(), &$name.1)?;
+ let value = evaluate($ctx.clone(), &$name)?;
$ty.check(&value)?;
Ok(value)
})?;
crates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -29,6 +29,16 @@
.get(key)
.or_else(|| self.0.parent.as_ref().and_then(|p| p.get(key)))
}
+
+ pub fn contains_key(&self, key: &IStr) -> bool {
+ (self.0).current.contains_key(key)
+ || self
+ .0
+ .parent
+ .as_ref()
+ .map(|p| p.contains_key(key))
+ .unwrap_or(false)
+ }
}
impl Clone for LayeredHashMap {
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -11,7 +11,7 @@
};
use jrsonnet_gc::{Gc, GcCell, Trace};
use jrsonnet_interner::IStr;
-use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
+use jrsonnet_parser::{el, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
use jrsonnet_types::ValType;
use std::{collections::HashMap, fmt::Debug, rc::Rc};
@@ -127,7 +127,7 @@
Self::Normal(func) => {
let ctx = parse_function_call(
call_ctx,
- Some(func.ctx.clone()),
+ func.ctx.clone(),
&func.params,
args,
tailstrict,
@@ -136,7 +136,8 @@
}
Self::Intrinsic(name) => call_builtin(call_ctx, loc, name, args),
Self::NativeExt(_name, handler) => {
- let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;
+ let args =
+ parse_function_call(call_ctx, Context::new(), &handler.params, args, true)?;
let mut out_args = Vec::with_capacity(handler.params.len());
for p in handler.params.0.iter() {
out_args.push(args.binding(p.0.clone())?.evaluate()?);
@@ -554,17 +555,17 @@
el!(Expr::Var("std".into())),
el!(Expr::Str("manifestYamlDoc".into()))
)),
- ArgsDesc(vec![
- Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),
- Arg(
- None,
+ ArgsDesc::new(
+ vec![
+ el!(Expr::Var("__tmp__to_json__".into())),
el!(Expr::Literal(if padding != 0 {
LiteralType::True
} else {
LiteralType::False
- }))
- )
- ]),
+ })),
+ ],
+ vec![]
+ ),
false
)),
)?
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -194,18 +194,13 @@
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
#[trivially_drop]
-pub struct Arg(pub Option<String>, pub LocExpr);
-
-#[cfg_attr(feature = "serialize", derive(Serialize))]
-#[cfg_attr(feature = "deserialize", derive(Deserialize))]
-#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
-pub struct ArgsDesc(pub Vec<Arg>);
-
-impl Deref for ArgsDesc {
- type Target = Vec<Arg>;
- fn deref(&self) -> &Self::Target {
- &self.0
+pub struct ArgsDesc {
+ pub unnamed: Vec<LocExpr>,
+ pub named: Vec<(IStr, LocExpr)>,
+}
+impl ArgsDesc {
+ pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {
+ Self { unnamed, named }
}
}
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -7,6 +7,7 @@
};
mod expr;
pub use expr::*;
+pub use jrsonnet_interner::IStr;
pub use peg;
pub struct ParserSettings {
@@ -70,19 +71,29 @@
}
/ { expr::ParamsDesc(Rc::new(Vec::new())) }
- pub rule arg(s: &ParserSettings) -> expr::Arg
- = name:$(id()) _ "=" _ expr:expr(s) {expr::Arg(Some(name.into()), expr)}
- / expr:expr(s) {expr::Arg(None, expr)}
+ pub rule arg(s: &ParserSettings) -> (Option<IStr>, LocExpr)
+ = quiet! { name:(s:$(id()) _ "=" _ {s})? expr:expr(s) {(name.map(Into::into), expr)} }
+ / expected!("<argument>")
+
pub rule args(s: &ParserSettings) -> expr::ArgsDesc
- = args:arg(s) ** comma() comma()? {
+ = args:arg(s)**comma() comma()? {?
+ let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();
+ let mut unnamed = Vec::with_capacity(unnamed_count);
+ let mut named = Vec::with_capacity(args.len() - unnamed_count);
let mut named_started = false;
- for arg in &args {
- named_started = named_started || arg.0.is_some();
- assert_eq!(named_started, arg.0.is_some(), "named args should be used after all positionals");
+ for (name, value) in args {
+ if let Some(name) = name {
+ named_started = true;
+ named.push((name, value));
+ } else {
+ if named_started {
+ return Err("<named argument>")
+ }
+ unnamed.push(value);
+ }
}
- expr::ArgsDesc(args)
+ Ok(expr::ArgsDesc::new(unnamed, named))
}
- / { expr::ArgsDesc(Vec::new()) }
pub rule bind(s: &ParserSettings) -> expr::BindSpec
= name:$(id()) _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: None, value: expr}}
@@ -493,7 +504,7 @@
el!(ArrComp(
el!(Apply(
el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),
- ArgsDesc(vec![Arg(None, el!(Var("x".into())))]),
+ ArgsDesc::new(vec![el!(Var("x".into()))], vec![]),
false,
)),
vec![CompSpec::ForSpec(ForSpecData(