difftreelog
refactor(ir) explicit Rc wrapping
in: master
51 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -717,6 +717,7 @@
name = "jrsonnet-parser"
version = "0.5.0-pre97"
dependencies = [
+ "insta",
"jrsonnet-gcmodule",
"jrsonnet-interner",
"peg",
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -1,12 +1,12 @@
use std::{
any::Any,
fmt::{self},
- num::NonZeroU32,
+ num::NonZeroU32, rc::Rc,
};
use jrsonnet_gcmodule::{cc_dyn, Cc};
use jrsonnet_interner::IBytes;
-use jrsonnet_parser::LocExpr;
+use jrsonnet_parser::{Expr, Spanned};
use crate::{function::FuncVal, Context, Result, Thunk, Val};
@@ -37,7 +37,7 @@
Self::new(RangeArray::empty())
}
- pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {
+ pub fn expr(ctx: Context, exprs: Rc<Vec<Spanned<Expr>>>) -> Self {
Self::new(ExprArray::new(ctx, exprs))
}
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -1,8 +1,9 @@
-use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};
+use std::rc::Rc;
+use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::{IBytes, IStr};
-use jrsonnet_parser::LocExpr;
+use jrsonnet_parser::{Expr, Spanned};
use super::ArrValue;
use crate::{
@@ -103,25 +104,25 @@
}
#[derive(Debug, Trace, Clone)]
-enum ArrayThunk<T: 'static + Trace> {
+enum ArrayThunk {
Computed(Val),
Errored(Error),
- Waiting(T),
+ Waiting,
Pending,
}
#[derive(Debug, Trace, Clone)]
pub struct ExprArray {
ctx: Context,
- cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,
+ src: Rc<Vec<Spanned<Expr>>>,
+ cached: Cc<RefCell<Vec<ArrayThunk>>>,
}
impl ExprArray {
- pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {
+ pub fn new(ctx: Context, src: Rc<Vec<Spanned<Expr>>>) -> Self {
Self {
ctx,
- cached: Cc::new(RefCell::new(
- items.into_iter().map(ArrayThunk::Waiting).collect(),
- )),
+ cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),
+ src,
}
}
}
@@ -137,16 +138,16 @@
ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
ArrayThunk::Errored(e) => return Err(e.clone()),
ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
- ArrayThunk::Waiting(..) => {}
+ ArrayThunk::Waiting => {}
};
- let ArrayThunk::Waiting(expr) =
+ let ArrayThunk::Waiting =
replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
else {
unreachable!()
};
- let new_value = match evaluate(self.ctx.clone(), &expr) {
+ let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {
Ok(v) => v,
Err(e) => {
self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
@@ -163,7 +164,7 @@
match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
- ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
+ ArrayThunk::Waiting | ArrayThunk::Pending => {}
};
#[derive(Trace)]
@@ -406,7 +407,7 @@
#[derive(Trace, Debug, Clone)]
pub struct MappedArray<const WITH_INDEX: bool> {
inner: ArrValue,
- cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
+ cached: Cc<RefCell<Vec<ArrayThunk>>>,
mapper: FuncVal,
}
impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {
@@ -414,7 +415,7 @@
let len = inner.len();
Self {
inner,
- cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),
+ cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),
mapper,
}
}
@@ -439,10 +440,10 @@
ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
ArrayThunk::Errored(e) => return Err(e.clone()),
ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
- ArrayThunk::Waiting(..) => {}
+ ArrayThunk::Waiting => {}
};
- let ArrayThunk::Waiting(()) =
+ let ArrayThunk::Waiting =
replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
else {
unreachable!()
@@ -472,7 +473,7 @@
match &self.cached.borrow()[index] {
ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
- ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}
+ ArrayThunk::Waiting | ArrayThunk::Pending => {}
};
#[derive(Trace)]
crates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/async_import.rs
+++ b/crates/jrsonnet-evaluator/src/async_import.rs
@@ -1,10 +1,11 @@
+use std::rc::Rc;
use std::{any::Any, cell::RefCell, future::Future};
use jrsonnet_gcmodule::Acyclic;
use jrsonnet_parser::{
- ArgsDesc, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName, ForSpecData,
- IfSpecData, LocExpr, Member, ObjBody, Param, ParamsDesc, ParserSettings, SliceDesc, Source,
- SourcePath,
+ ArgsDesc, AssertExpr, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName,
+ ForSpecData, IfElse, IfSpecData, ImportKind, Member, ObjBody, Param, ParamsDesc,
+ ParserSettings, Slice, SliceDesc, Source, SourcePath, Spanned,
};
use rustc_hash::FxHashMap;
@@ -19,7 +20,7 @@
// Visits all nodes, trying to find import statements
#[allow(clippy::too_many_lines)]
-pub fn find_imports(expr: &LocExpr, out: &mut FoundImports) {
+pub fn find_imports(expr: &Spanned<Expr>, out: &mut FoundImports) {
fn in_destruct(dest: &Destruct, #[allow(unused_variables)] out: &mut FoundImports) {
match dest {
#[cfg(feature = "exp-destruct")]
@@ -120,9 +121,9 @@
find_imports(value, out);
}
Member::BindStmt(_) => todo!(),
- Member::AssertStmt(AssertStmt(expr, expr2)) => {
- find_imports(expr, out);
- if let Some(expr) = expr2 {
+ Member::AssertStmt(assert) => {
+ find_imports(&assert.0, out);
+ if let Some(expr) = &assert.1 {
find_imports(expr, out);
}
}
@@ -132,12 +133,12 @@
ObjBody::ObjComp(_) => todo!(),
}
}
- match &*expr.expr() {
- Expr::Import(v) | Expr::ImportStr(v) | Expr::ImportBin(v) => {
- if let Expr::Str(s) = &*v.expr() {
+ match &**expr {
+ Expr::Import(_, v) => {
+ if let Expr::Str(s) = &***v {
out.0.push(Import {
path: ResolvePathOwned::Str(s.to_string()),
- expression: matches!(&*expr.expr(), Expr::Import(_)),
+ expression: matches!(&**expr, Expr::Import(ImportKind::Normal, _)),
});
}
// Non-string import will fail in runtime
@@ -146,7 +147,7 @@
Expr::Literal(_) | Expr::Str(_) | Expr::Num(_) | Expr::Var(_) => {}
Expr::Arr(arr) => {
- for expr in arr {
+ for expr in &**arr {
find_imports(expr, out);
}
}
@@ -159,16 +160,20 @@
find_imports(expr, out);
in_obj(obj, out);
}
- Expr::BinaryOp(a, _, b) => {
- find_imports(a, out);
- find_imports(b, out);
+ Expr::BinaryOp(binop) => {
+ find_imports(&binop.lhs, out);
+ find_imports(&binop.rhs, out);
}
- Expr::AssertExpr(AssertStmt(expr, expr2), then) => {
+ Expr::AssertExpr(assert) => {
+ let AssertExpr {
+ assert: AssertStmt(expr, expr2),
+ rest,
+ } = &**assert;
find_imports(expr, out);
if let Some(expr) = expr2 {
find_imports(expr, out);
}
- find_imports(then, out);
+ find_imports(rest, out);
}
Expr::LocalExpr(specs, expr) => {
in_bind(specs, out);
@@ -188,19 +193,24 @@
in_params(params, out);
find_imports(expr, out);
}
- Expr::IfElse {
- cond: IfSpecData(expr),
- cond_then,
- cond_else,
- } => {
+ Expr::IfElse(if_else) => {
+ let IfElse {
+ cond: IfSpecData(expr),
+ cond_then,
+ cond_else,
+ } = &**if_else;
find_imports(expr, out);
find_imports(cond_then, out);
if let Some(expr) = cond_else {
find_imports(expr, out);
}
}
- Expr::Slice(expr, SliceDesc { start, end, step }) => {
- find_imports(expr, out);
+ Expr::Slice(slice) => {
+ let Slice {
+ value,
+ slice: SliceDesc { start, end, step },
+ } = &**slice;
+ find_imports(value, out);
if let Some(expr) = start {
find_imports(expr, out);
}
@@ -314,8 +324,9 @@
};
let source = Source::new(path.clone(), code.clone());
// If failed - then skip import
- file.parsed =
- jrsonnet_parser::parse(&code, &ParserSettings { source }).ok();
+ file.parsed = jrsonnet_parser::parse(&code, &ParserSettings { source })
+ .map(Rc::new)
+ .ok();
if let Some(parsed) = &file.parsed {
let mut imports = FoundImports(vec![]);
find_imports(parsed, &mut imports);
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -4,9 +4,9 @@
fmt::{Debug, Display},
};
-use jrsonnet_gcmodule::Trace;
+use jrsonnet_gcmodule::{Acyclic, Trace};
use jrsonnet_interner::IStr;
-use jrsonnet_parser::{BinaryOpType, LocExpr, Source, SourcePath, Span, UnaryOpType};
+use jrsonnet_parser::{BinaryOpType, Source, SourcePath, Span, Spanned, UnaryOpType};
use jrsonnet_types::ValType;
use thiserror::Error;
@@ -324,7 +324,7 @@
pub trait ErrorSource {
fn to_location(self) -> Option<Span>;
}
-impl ErrorSource for &LocExpr {
+impl<T: Acyclic> ErrorSource for &Spanned<T> {
fn to_location(self) -> Option<Span> {
Some(self.span())
}
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -4,7 +4,7 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::{
ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, FieldName,
- ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
+ ForSpecData, IfSpecData, ImportKind, LiteralType, Member, ObjBody, ParamsDesc, Spanned,
};
use jrsonnet_types::ValType;
use rustc_hash::FxHashMap;
@@ -46,9 +46,9 @@
stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f)
}
-pub fn evaluate_trivial(expr: &LocExpr) -> Option<Val> {
- fn is_trivial(expr: &LocExpr) -> bool {
- match expr.expr() {
+pub fn evaluate_trivial(expr: &Spanned<Expr>) -> Option<Val> {
+ fn is_trivial(expr: &Spanned<Expr>) -> bool {
+ match &**expr {
Expr::Str(_)
| Expr::Num(_)
| Expr::Literal(LiteralType::False | LiteralType::True | LiteralType::Null) => true,
@@ -56,7 +56,7 @@
_ => false,
}
}
- Some(match expr.expr() {
+ Some(match &**expr {
Expr::Str(s) => Val::string(s.clone()),
Expr::Num(n) => {
Val::Num(NumValue::new(*n).expect("parser will not allow non-finite values"))
@@ -79,7 +79,12 @@
})
}
-pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {
+pub fn evaluate_method(
+ ctx: Context,
+ name: IStr,
+ params: ParamsDesc,
+ body: Rc<Spanned<Expr>>,
+) -> Val {
Val::Func(FuncVal::Normal(Cc::new(FuncDesc {
name,
ctx,
@@ -215,7 +220,7 @@
#[derive(Trace)]
struct UnboundValue<B: Trace> {
uctx: B,
- value: LocExpr,
+ value: Rc<Spanned<Expr>>,
name: IStr,
}
impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {
@@ -245,7 +250,7 @@
#[derive(Trace)]
struct UnboundMethod<B: Trace> {
uctx: B,
- value: LocExpr,
+ value: Rc<Spanned<Expr>>,
params: ParamsDesc,
name: IStr,
}
@@ -301,7 +306,7 @@
#[derive(Trace)]
struct ObjectAssert<B: Trace> {
uctx: B,
- assert: AssertStmt,
+ assert: Rc<AssertStmt>,
}
impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {
fn run(&self, sup_this: SupThis) -> Result<()> {
@@ -347,7 +352,7 @@
pub fn evaluate_apply(
ctx: Context,
- value: &LocExpr,
+ value: &Spanned<Expr>,
args: &ArgsDesc,
loc: CallLocation<'_>,
tailstrict: bool,
@@ -389,23 +394,23 @@
Ok(())
}
-pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result<Val> {
+pub fn evaluate_named(ctx: Context, expr: &Spanned<Expr>, name: IStr) -> Result<Val> {
use Expr::*;
- Ok(match expr.expr() {
+ Ok(match &**expr {
Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),
_ => evaluate(ctx, expr)?,
})
}
#[allow(clippy::too_many_lines)]
-pub fn evaluate(ctx: Context, expr: &LocExpr) -> Result<Val> {
+pub fn evaluate(ctx: Context, expr: &Spanned<Expr>) -> Result<Val> {
use Expr::*;
if let Some(trivial) = evaluate_trivial(expr) {
return Ok(trivial);
}
let loc = expr.span();
- Ok(match expr.expr() {
+ Ok(match &**expr {
Literal(LiteralType::This) => Val::Obj(ctx.try_this()?),
Literal(LiteralType::Super) => Val::Obj(ctx.try_sup_this()?.standalone_super()?),
Literal(LiteralType::Dollar) => Val::Obj(ctx.try_dollar()?),
@@ -420,8 +425,9 @@
// Note that other jsonnet implementations will fail on `if value in (super)` expression,
// because the standalone super literal is not supported, that is because in other
// implementations `in super` treated differently from `in smth_else`.
- BinaryOp(field, BinaryOpType::In, e)
- if matches!(e.expr(), Expr::Literal(LiteralType::Super)) =>
+ BinaryOp(bin)
+ if matches!(&*bin.rhs, Expr::Literal(LiteralType::Super))
+ && bin.op == BinaryOpType::In =>
{
let sup_this = ctx.try_sup_this()?;
// In jsonnet, "field" in e is eager, LHS expression is always executed regardless of super existence.
@@ -429,10 +435,10 @@
if !sup_this.has_super() {
return Ok(Val::Bool(false));
}
- let field = evaluate(ctx, field)?;
+ let field = evaluate(ctx, &bin.lhs)?;
Val::Bool(sup_this.field_in_super(field.to_string()?))
}
- BinaryOp(v1, o, v2) => evaluate_binary_op_special(ctx, v1, *o, v2)?,
+ BinaryOp(bin) => evaluate_binary_op_special(ctx, &bin.lhs, bin.op, &bin.rhs)?,
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(ctx, v)?)?,
Var(name) => in_frame(
CallLocation::new(&loc),
@@ -441,7 +447,7 @@
)?,
Index { indexable, parts } => ensure_sufficient_stack(|| {
let mut parts = parts.iter();
- let mut indexable = if matches!(indexable.expr(), Expr::Literal(LiteralType::Super)) {
+ let mut indexable = if matches!(&***indexable, Expr::Literal(LiteralType::Super)) {
let part = parts.next().expect("at least part should exist");
// sup_this existence check might also be skipped here for null-coalesce...
// But I believe this might cause errors.
@@ -574,11 +580,8 @@
Arr(items) => {
if items.is_empty() {
Val::Arr(ArrValue::empty())
- } else if items.len() == 1 {
- let item = items[0].clone();
- Val::Arr(ArrValue::lazy(vec![Thunk!(move || evaluate(ctx, &item))]))
} else {
- Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
+ Val::Arr(ArrValue::expr(ctx, items.clone()))
}
}
ArrComp(expr, comp_specs) => {
@@ -601,38 +604,40 @@
Function(params, body) => {
evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())
}
- AssertExpr(assert, returned) => {
- evaluate_assert(ctx.clone(), assert)?;
- evaluate(ctx, returned)?
+ AssertExpr(assert) => {
+ evaluate_assert(ctx.clone(), &assert.assert)?;
+ evaluate(ctx, &assert.rest)?
}
ErrorStmt(e) => in_frame(
CallLocation::new(&loc),
|| "error statement".to_owned(),
|| bail!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),
)?,
- IfElse {
- cond,
- cond_then,
- cond_else,
- } => {
+ IfElse (if_else)
+ // {
+ // cond,
+ // cond_then,
+ // cond_else,
+ // }
+ => {
if in_frame(
CallLocation::new(&loc),
|| "if condition".to_owned(),
- || bool::from_untyped(evaluate(ctx.clone(), &cond.0)?),
+ || bool::from_untyped(evaluate(ctx.clone(), &if_else.cond.0)?),
)? {
- evaluate(ctx, cond_then)?
+ evaluate(ctx, &if_else.cond_then)?
} else {
- match cond_else {
+ match &if_else.cond_else {
Some(v) => evaluate(ctx, v)?,
None => Val::Null,
}
}
}
- Slice(value, desc) => {
+ Slice(slice) => {
fn parse_idx<T: Typed>(
loc: CallLocation<'_>,
ctx: Context,
- expr: Option<&LocExpr>,
+ expr: Option<&Spanned<Expr>>,
desc: &'static str,
) -> Result<Option<T>> {
if let Some(value) = expr {
@@ -646,33 +651,32 @@
}
}
- let indexable = evaluate(ctx.clone(), value)?;
+ let indexable = evaluate(ctx.clone(), &slice.value)?;
let loc = CallLocation::new(&loc);
- let start = parse_idx(loc, ctx.clone(), desc.start.as_ref(), "start")?;
- let end = parse_idx(loc, ctx.clone(), desc.end.as_ref(), "end")?;
- let step = parse_idx(loc, ctx, desc.step.as_ref(), "step")?;
+ let start = parse_idx(loc, ctx.clone(), slice.slice.start.as_ref(), "start")?;
+ let end = parse_idx(loc, ctx.clone(), slice.slice.end.as_ref(), "end")?;
+ let step = parse_idx(loc, ctx, slice.slice.step.as_ref(), "step")?;
IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)?
}
- i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {
- let Expr::Str(path) = &path.expr() else {
+ Import(kind, path) => {
+ let Expr::Str(path) = &***path else {
bail!("computed imports are not supported")
};
let tmp = loc.clone().0;
with_state(|s| {
let resolved_path = s.resolve_from(tmp.source_path(), path)?;
- Ok(match i {
- Import(_) => in_frame(
+ Ok(match kind {
+ ImportKind::Normal => in_frame(
CallLocation::new(&loc),
|| format!("import {:?}", path.clone()),
|| s.import_resolved(resolved_path),
)?,
- ImportStr(_) => Val::string(s.import_resolved_str(resolved_path)?),
- ImportBin(_) => {
+ ImportKind::Str => Val::string(s.import_resolved_str(resolved_path)?),
+ ImportKind::Bin => {
Val::Arr(ArrValue::bytes(s.import_resolved_bin(resolved_path)?))
}
- _ => unreachable!(),
}) as Result<Val>
})?
}
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -1,6 +1,6 @@
use std::cmp::Ordering;
-use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType};
+use jrsonnet_parser::{BinaryOpType, Expr, Spanned, UnaryOpType};
use crate::{
arr::ArrValue,
@@ -147,9 +147,9 @@
pub fn evaluate_binary_op_special(
ctx: Context,
- a: &LocExpr,
+ a: &Spanned<Expr>,
op: BinaryOpType,
- b: &LocExpr,
+ b: &Spanned<Expr>,
) -> Result<Val> {
use BinaryOpType::*;
use Val::*;
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -1,8 +1,9 @@
use std::collections::HashMap;
+use std::rc::Rc;
use jrsonnet_gcmodule::Trace;
use jrsonnet_interner::IStr;
-use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};
+use jrsonnet_parser::{ArgsDesc, Expr, SourceFifo, SourcePath, Spanned};
use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};
@@ -13,7 +14,7 @@
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;
}
-impl ArgLike for &LocExpr {
+impl ArgLike for &Rc<Spanned<Expr>> {
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
Ok(if tailstrict {
Thunk::evaluated(evaluate(ctx, self)?)
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -1,10 +1,10 @@
-use std::fmt::Debug;
+use std::{fmt::Debug, rc::Rc};
pub use arglike::{ArgLike, ArgsLike, TlaArg};
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
pub use jrsonnet_macros::builtin;
-use jrsonnet_parser::{Destruct, Expr, LocExpr, ParamsDesc, Span};
+use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};
use self::{
arglike::OptionalContext,
@@ -66,7 +66,7 @@
/// Function parameter definition
pub params: ParamsDesc,
/// Function body
- pub body: LocExpr,
+ pub body: Rc<Spanned<Expr>>,
}
impl FuncDesc {
/// Create body context, but fill arguments without defaults with lazy error
@@ -240,7 +240,7 @@
#[cfg(feature = "exp-destruct")]
_ => return false,
};
- desc.body.expr() == &Expr::Var(id.clone())
+ **desc.body == Expr::Var(id.clone())
}
_ => false,
}
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -45,7 +45,7 @@
#[doc(hidden)]
pub use jrsonnet_macros;
pub use jrsonnet_parser as parser;
-use jrsonnet_parser::{LocExpr, ParserSettings, Source, SourcePath};
+use jrsonnet_parser::{Expr, ParserSettings, Source, SourcePath, Spanned};
pub use obj::*;
pub use rustc_hash;
use rustc_hash::FxHashMap;
@@ -186,7 +186,7 @@
struct FileData {
string: Option<IStr>,
bytes: Option<IBytes>,
- parsed: Option<LocExpr>,
+ parsed: Option<Rc<Spanned<Expr>>>,
evaluated: Option<Val>,
evaluating: bool,
@@ -350,6 +350,7 @@
source: file_name.clone(),
},
)
+ .map(Rc::new)
.map_err(|e| ImportSyntaxError {
path: file_name.clone(),
error: Box::new(e),
crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -19,3 +19,6 @@
static_assertions.workspace = true
peg.workspace = true
+
+[dev-dependencies]
+insta.workspace = true
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -14,7 +14,7 @@
/// {fixed: 2}
Fixed(IStr),
/// {["dyn"+"amic"]: 3}
- Dyn(LocExpr),
+ Dyn(Spanned<Expr>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]
@@ -34,8 +34,8 @@
}
}
-#[derive(Clone, Debug, PartialEq, Acyclic)]
-pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
+#[derive(Debug, PartialEq, Acyclic)]
+pub struct AssertStmt(pub Spanned<Expr>, pub Option<Spanned<Expr>>);
#[derive(Debug, PartialEq, Acyclic)]
pub struct FieldMember {
@@ -43,14 +43,14 @@
pub plus: bool,
pub params: Option<ParamsDesc>,
pub visibility: Visibility,
- pub value: LocExpr,
+ pub value: Rc<Spanned<Expr>>,
}
#[derive(Debug, PartialEq, Acyclic)]
pub enum Member {
Field(FieldMember),
BindStmt(BindSpec),
- AssertStmt(AssertStmt),
+ AssertStmt(Rc<AssertStmt>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]
@@ -147,7 +147,7 @@
/// name, default value
#[derive(Debug, PartialEq, Acyclic)]
-pub struct Param(pub Destruct, pub Option<LocExpr>);
+pub struct Param(pub Destruct, pub Option<Rc<Spanned<Expr>>>);
/// Defined function parameters
#[derive(Debug, Clone, PartialEq, Acyclic)]
@@ -162,11 +162,11 @@
#[derive(Debug, PartialEq, Acyclic)]
pub struct ArgsDesc {
- pub unnamed: Vec<LocExpr>,
- pub named: Vec<(IStr, LocExpr)>,
+ pub unnamed: Vec<Rc<Spanned<Expr>>>,
+ pub named: Vec<(IStr, Rc<Spanned<Expr>>)>,
}
impl ArgsDesc {
- pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {
+ pub fn new(unnamed: Vec<Rc<Spanned<Expr>>>, named: Vec<(IStr, Rc<Spanned<Expr>>)>) -> Self {
Self { unnamed, named }
}
}
@@ -192,7 +192,7 @@
},
#[cfg(feature = "exp-destruct")]
Object {
- fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,
+ fields: Vec<(IStr, Option<Destruct>, Option<Spanned<Expr>>)>,
rest: Option<DestructRest>,
},
}
@@ -244,12 +244,12 @@
pub enum BindSpec {
Field {
into: Destruct,
- value: LocExpr,
+ value: Rc<Spanned<Expr>>,
},
Function {
name: IStr,
params: ParamsDesc,
- value: LocExpr,
+ value: Rc<Spanned<Expr>>,
},
}
impl BindSpec {
@@ -262,10 +262,10 @@
}
#[derive(Debug, PartialEq, Acyclic)]
-pub struct IfSpecData(pub LocExpr);
+pub struct IfSpecData(pub Spanned<Expr>);
#[derive(Debug, PartialEq, Acyclic)]
-pub struct ForSpecData(pub Destruct, pub LocExpr);
+pub struct ForSpecData(pub Destruct, pub Spanned<Expr>);
#[derive(Debug, PartialEq, Acyclic)]
pub enum CompSpec {
@@ -276,7 +276,7 @@
#[derive(Debug, PartialEq, Acyclic)]
pub struct ObjComp {
pub pre_locals: Vec<BindSpec>,
- pub field: FieldMember,
+ pub field: Rc<FieldMember>,
pub post_locals: Vec<BindSpec>,
pub compspecs: Vec<CompSpec>,
}
@@ -299,11 +299,44 @@
#[derive(Debug, PartialEq, Acyclic)]
pub struct SliceDesc {
- pub start: Option<LocExpr>,
- pub end: Option<LocExpr>,
- pub step: Option<LocExpr>,
+ pub start: Option<Spanned<Expr>>,
+ pub end: Option<Spanned<Expr>>,
+ pub step: Option<Spanned<Expr>>,
+}
+
+#[derive(Debug, PartialEq, Acyclic)]
+pub struct AssertExpr {
+ pub assert: AssertStmt,
+ pub rest: Spanned<Expr>,
+}
+
+#[derive(Debug, PartialEq, Acyclic)]
+pub struct BinaryOp {
+ pub lhs: Spanned<Expr>,
+ pub op: BinaryOpType,
+ pub rhs: Spanned<Expr>,
+}
+
+#[derive(Debug, PartialEq, Acyclic)]
+pub enum ImportKind {
+ Normal,
+ Str,
+ Bin,
}
+#[derive(Debug, PartialEq, Acyclic)]
+pub struct IfElse {
+ pub cond: IfSpecData,
+ pub cond_then: Spanned<Expr>,
+ pub cond_else: Option<Spanned<Expr>>,
+}
+
+#[derive(Debug, PartialEq, Acyclic)]
+pub struct Slice {
+ pub value: Spanned<Expr>,
+ pub slice: SliceDesc,
+}
+
/// Syntax base
#[derive(Debug, PartialEq, Acyclic)]
pub enum Expr {
@@ -317,7 +350,7 @@
Var(IStr),
/// Array of expressions: [1, 2, "Hello"]
- Arr(Vec<LocExpr>),
+ Arr(Rc<Vec<Spanned<Expr>>>),
/// Array comprehension:
/// ```jsonnet
/// ingredients: [
@@ -329,51 +362,43 @@
/// ]
/// ],
/// ```
- ArrComp(LocExpr, Vec<CompSpec>),
+ ArrComp(Rc<Spanned<Expr>>, Vec<CompSpec>),
/// Object: {a: 2}
Obj(ObjBody),
/// Object extension: var1 {b: 2}
- ObjExtend(LocExpr, ObjBody),
+ ObjExtend(Rc<Spanned<Expr>>, ObjBody),
/// -2
- UnaryOp(UnaryOpType, LocExpr),
+ UnaryOp(UnaryOpType, Box<Spanned<Expr>>),
/// 2 - 2
- BinaryOp(LocExpr, BinaryOpType, LocExpr),
+ BinaryOp(Box<BinaryOp>),
/// assert 2 == 2 : "Math is broken"
- AssertExpr(AssertStmt, LocExpr),
+ AssertExpr(Rc<AssertExpr>),
/// local a = 2; { b: a }
- LocalExpr(Vec<BindSpec>, LocExpr),
+ LocalExpr(Vec<BindSpec>, Box<Spanned<Expr>>),
- /// import "hello"
- Import(LocExpr),
- /// importStr "file.txt"
- ImportStr(LocExpr),
- /// importBin "file.txt"
- ImportBin(LocExpr),
+ /// import* "hello"
+ Import(ImportKind, Box<Spanned<Expr>>),
/// error "I'm broken"
- ErrorStmt(LocExpr),
+ ErrorStmt(Box<Spanned<Expr>>),
/// a(b, c)
- Apply(LocExpr, ArgsDesc, bool),
+ Apply(Box<Spanned<Expr>>, ArgsDesc, bool),
/// a[b], a.b, a?.b
Index {
- indexable: LocExpr,
+ indexable: Box<Spanned<Expr>>,
parts: Vec<IndexPart>,
},
/// function(x) x
- Function(ParamsDesc, LocExpr),
+ Function(ParamsDesc, Rc<Spanned<Expr>>),
/// if true == false then 1 else 2
- IfElse {
- cond: IfSpecData,
- cond_then: LocExpr,
- cond_else: Option<LocExpr>,
- },
- Slice(LocExpr, SliceDesc),
+ IfElse(Box<IfElse>),
+ Slice(Box<Slice>),
}
#[derive(Debug, PartialEq, Acyclic)]
pub struct IndexPart {
- pub value: LocExpr,
+ pub value: Spanned<Expr>,
#[cfg(feature = "exp-null-coaelse")]
pub null_coaelse: bool,
}
@@ -396,28 +421,28 @@
}
}
-/// Holds AST expression and its location in source file
#[derive(Clone, PartialEq, Acyclic)]
-pub struct LocExpr(Rc<(Expr, Span)>);
-impl LocExpr {
- pub fn new(expr: Expr, span: Span) -> Self {
- Self(Rc::new((expr, span)))
+pub struct Spanned<T: Acyclic>(T, Span);
+impl<T: Acyclic> Deref for Spanned<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ &self.0
}
+}
+impl<T: Acyclic> Spanned<T> {
#[inline]
- pub fn span(&self) -> Span {
- self.0 .1.clone()
+ pub fn new(v: T, s: Span) -> Self {
+ Self(v, s)
}
#[inline]
- pub fn expr(&self) -> &Expr {
- &self.0 .0
+ pub fn span(&self) -> Span {
+ self.1.clone()
}
}
-static_assertions::assert_eq_size!(LocExpr, usize);
-
-impl Debug for LocExpr {
+impl<T: Debug + Acyclic> Debug for Spanned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let expr = self.expr();
+ let expr = &**self;
if f.alternate() {
write!(f, "{:#?}", expr)?;
} else {
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]23use std::rc::Rc;45use peg::parser;6mod expr;7pub use expr::*;8pub use jrsonnet_interner::IStr;9pub use peg;10mod location;11mod source;12mod unescape;13pub use location::CodeLocation;14pub use source::{15 Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,16 SourcePathT, SourceVirtual,17};1819pub struct ParserSettings {20 pub source: Source,21}2223macro_rules! expr_bin {24 ($a:ident $op:ident $b:ident) => {25 Expr::BinaryOp($a, $op, $b)26 };27}28macro_rules! expr_un {29 ($op:ident $a:ident) => {30 Expr::UnaryOp($op, $a)31 };32}3334parser! {35 grammar jsonnet_parser() for str {36 use peg::ParseLiteral;3738 rule eof() = quiet!{![_]} / expected!("<eof>")39 rule eol() = "\n" / eof()4041 /// Standard C-like comments42 rule comment()43 = "//" (!eol()[_])* eol()44 / "/*" (!("*/")[_])* "*/"45 / "#" (!eol()[_])* eol()4647 rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")48 rule _() = quiet!{([' ' | '\r' | '\n' | '\t']+) / comment()}* / expected!("<whitespace>")4950 /// For comma-delimited elements51 rule comma() = quiet!{_ "," _} / expected!("<comma>")52 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().next().unwrap()}53 rule digit() -> char = d:$(['0'..='9']) {d.chars().next().unwrap()}54 rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']55 /// Sequence of digits56 rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }57 /// Number in scientific notation format58 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace("_","").parse().map_err(|_| "<number>") }} / expected!("<number>")5960 /// Reserved word followed by any non-alphanumberic61 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()62 rule id() -> IStr = v:$(quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")) { v.into() }6364 rule keyword(id: &'static str) -> ()65 = ##parse_string_literal(id) end_of_ident()6667 pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }68 pub rule params(s: &ParserSettings) -> expr::ParamsDesc69 = params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }70 / { expr::ParamsDesc(Rc::new(Vec::new())) }7172 pub rule arg(s: &ParserSettings) -> (Option<IStr>, LocExpr)73 = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("<argument name>")) expr:expr(s) {(name, expr)}7475 pub rule args(s: &ParserSettings) -> expr::ArgsDesc76 = args:arg(s)**comma() comma()? {?77 let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();78 let mut unnamed = Vec::with_capacity(unnamed_count);79 let mut named = Vec::with_capacity(args.len() - unnamed_count);80 let mut named_started = false;81 for (name, value) in args {82 if let Some(name) = name {83 named_started = true;84 named.push((name, value));85 } else {86 if named_started {87 return Err("<named argument>")88 }89 unnamed.push(value);90 }91 }92 Ok(expr::ArgsDesc::new(unnamed, named))93 }9495 pub rule destruct_rest() -> expr::DestructRest96 = "..." into:(_ into:id() {into})? {if let Some(into) = into {97 expr::DestructRest::Keep(into)98 } else {expr::DestructRest::Drop}}99 pub rule destruct_array(s: &ParserSettings) -> expr::Destruct100 = "[" _ start:destruct(s)**comma() rest:(101 comma() _ rest:destruct_rest()? end:(102 comma() end:destruct(s)**comma() (_ comma())? {end}103 / comma()? {Vec::new()}104 ) {(rest, end)}105 / comma()? {(None, Vec::new())}106 ) _ "]" {?107 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Array {108 start,109 rest: rest.0,110 end: rest.1,111 });112 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")113 }114 pub rule destruct_object(s: &ParserSettings) -> expr::Destruct115 = "{" _116 fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma()117 rest:(118 comma() rest:destruct_rest()? {rest}119 / comma()? {None}120 )121 _ "}" {?122 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Object {123 fields,124 rest,125 });126 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")127 }128 pub rule destruct(s: &ParserSettings) -> expr::Destruct129 = v:id() {expr::Destruct::Full(v)}130 / "?" {?131 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Skip);132 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")133 }134 / arr:destruct_array(s) {arr}135 / obj:destruct_object(s) {obj}136137 pub rule bind(s: &ParserSettings) -> expr::BindSpec138 = into:destruct(s) _ "=" _ expr:expr(s) {expr::BindSpec::Field{into, value: expr}}139 / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec::Function{name, params, value: expr}}140141 pub rule assertion(s: &ParserSettings) -> expr::AssertStmt142 = keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }143144 pub rule whole_line() -> &'input str145 = str:$((!['\n'][_])* "\n") {str}146 pub rule string_block() -> String147 = "|||" chomped:"-"? (!['\n']single_whitespace())* "\n"148 empty_lines:$(['\n']*)149 prefix:[' ' | '\t']+ first_line:whole_line()150 lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*151 [' ' | '\t']*<, {prefix.len() - 1}> "|||"152 {153 let mut l = empty_lines.to_owned();154 l.push_str(first_line);155 l.extend(lines);156 if chomped.is_some() {157 debug_assert!(l.ends_with('\n'));158 l.truncate(l.len() - 1);159 }160 l161 }162163 rule hex_char()164 = quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")165166 rule string_char(c: rule<()>)167 = (!['\\']!c()[_])+168 / "\\\\"169 / "\\u" hex_char() hex_char() hex_char() hex_char()170 / "\\x" hex_char() hex_char()171 / ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't' | '"' | '\''] } / expected!("<escape character>"))172 pub rule string() -> String173 = ['"'] str:$(string_char(<"\"">)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}174 / ['\''] str:$(string_char(<"\'">)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}175 / quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}176 / "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}177 / string_block() } / expected!("<string>")178179 pub rule field_name(s: &ParserSettings) -> expr::FieldName180 = name:id() {expr::FieldName::Fixed(name)}181 / name:string() {expr::FieldName::Fixed(name.into())}182 / "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}183 pub rule visibility() -> expr::Visibility184 = ":::" {expr::Visibility::Unhide}185 / "::" {expr::Visibility::Hidden}186 / ":" {expr::Visibility::Normal}187 pub rule field(s: &ParserSettings) -> expr::FieldMember188 = name:field_name(s) _ plus:"+"? _ visibility:visibility() _ value:expr(s) {expr::FieldMember{189 name,190 plus: plus.is_some(),191 params: None,192 visibility,193 value,194 }}195 / name:field_name(s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {expr::FieldMember{196 name,197 plus: false,198 params: Some(params),199 visibility,200 value,201 }}202 pub rule obj_local(s: &ParserSettings) -> BindSpec203 = keyword("local") _ bind:bind(s) {bind}204 pub rule member(s: &ParserSettings) -> expr::Member205 = bind:obj_local(s) {expr::Member::BindStmt(bind)}206 / assertion:assertion(s) {expr::Member::AssertStmt(assertion)}207 / field:field(s) {expr::Member::Field(field)}208 pub rule objinside(s: &ParserSettings) -> expr::ObjBody209 = pre_locals:(b: obj_local(s) comma() {b})* &"[" field:field(s) post_locals:(comma() b:obj_local(s) {b})* _ ("," _)? forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {210 let mut compspecs = vec![CompSpec::ForSpec(forspec)];211 compspecs.extend(others.unwrap_or_default());212 expr::ObjBody::ObjComp(expr::ObjComp{213 pre_locals,214 field,215 post_locals,216 compspecs,217 })218 }219 / members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}220 pub rule ifspec(s: &ParserSettings) -> IfSpecData221 = keyword("if") _ expr:expr(s) {IfSpecData(expr)}222 pub rule forspec(s: &ParserSettings) -> ForSpecData223 = keyword("for") _ id:destruct(s) _ keyword("in") _ cond:expr(s) {ForSpecData(id, cond)}224 pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>225 = s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}226 pub rule local_expr(s: &ParserSettings) -> Expr227 = keyword("local") _ binds:bind(s) ** comma() (_ ",")? _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }228 pub rule string_expr(s: &ParserSettings) -> Expr229 = s:string() {Expr::Str(s.into())}230 pub rule obj_expr(s: &ParserSettings) -> Expr231 = "{" _ body:objinside(s) _ "}" {Expr::Obj(body)}232 pub rule array_expr(s: &ParserSettings) -> Expr233 = "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}234 pub rule array_comp_expr(s: &ParserSettings) -> Expr235 = "[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {236 let mut specs = vec![CompSpec::ForSpec(forspec)];237 specs.extend(others.unwrap_or_default());238 Expr::ArrComp(expr, specs)239 }240 pub rule number_expr(s: &ParserSettings) -> Expr241 = n:number() {? if n.is_finite() {242 Ok(expr::Expr::Num(n))243 } else {244 Err("!!!numbers are finite")245 }}246 pub rule var_expr(s: &ParserSettings) -> Expr247 = n:id() { expr::Expr::Var(n) }248 pub rule id_loc(s: &ParserSettings) -> LocExpr249 = a:position!() n:id() b:position!() { LocExpr::new(expr::Expr::Str(n), Span(s.source.clone(), a as u32,b as u32)) }250 pub rule if_then_else_expr(s: &ParserSettings) -> Expr251 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{252 cond,253 cond_then,254 cond_else,255 }}256257 pub rule literal(s: &ParserSettings) -> Expr258 = v:(259 keyword("null") {LiteralType::Null}260 / keyword("true") {LiteralType::True}261 / keyword("false") {LiteralType::False}262 / keyword("self") {LiteralType::This}263 / keyword("$") {LiteralType::Dollar}264 / keyword("super") {LiteralType::Super}265 ) {Expr::Literal(v)}266267 pub rule expr_basic(s: &ParserSettings) -> Expr268 = literal(s)269270 / string_expr(s) / number_expr(s)271 / array_expr(s)272 / obj_expr(s)273 / array_expr(s)274 / array_comp_expr(s)275276 / keyword("importstr") _ path:expr(s) {Expr::ImportStr(path)}277 / keyword("importbin") _ path:expr(s) {Expr::ImportBin(path)}278 / keyword("import") _ path:expr(s) {Expr::Import(path)}279280 / var_expr(s)281 / local_expr(s)282 / if_then_else_expr(s)283284 / keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)}285 / assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) }286287 / keyword("error") _ expr:expr(s) { Expr::ErrorStmt(expr) }288289 rule slice_part(s: &ParserSettings) -> Option<LocExpr>290 = _ e:(e:expr(s) _{e})? {e}291 pub rule slice_desc(s: &ParserSettings) -> SliceDesc292 = start:slice_part(s) ":" pair:(end:slice_part(s) step:(":" e:slice_part(s){e})? {(end, step.flatten())})? {293 let (end, step) = if let Some((end, step)) = pair {294 (end, step)295 }else{296 (None, None)297 };298299 SliceDesc { start, end, step }300 }301302 rule binop(x: rule<()>) -> ()303 = quiet!{ x() } / expected!("<binary op>")304 rule unaryop(x: rule<()>) -> ()305 = quiet!{ x() } / expected!("<unary op>")306307 rule ensure_null_coaelse()308 = "" {?309 #[cfg(not(feature = "exp-null-coaelse"))] return Err("!!!experimental null coaelscing was not enabled");310 #[cfg(feature = "exp-null-coaelse")] Ok(())311 }312 use BinaryOpType::*;313 use UnaryOpType::*;314 rule expr(s: &ParserSettings) -> LocExpr315 = precedence! {316 "(" _ e:expr(s) _ ")" {e}317 start:position!() v:@ end:position!() { LocExpr::new(v, Span(s.source.clone(), start as u32, end as u32)) }318 --319 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}320 a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ {321 #[cfg(feature = "exp-null-coaelse")] return expr_bin!(a NullCoaelse b);322 unreachable!("ensure_null_coaelse will fail if feature is not enabled")323 }324 --325 a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}326 --327 a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)}328 --329 a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)}330 --331 a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)}332 --333 a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)}334 a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)}335 --336 a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)}337 a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)}338 a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)}339 a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)}340 a:(@) _ binop(<keyword("in")>) _ b:@ {expr_bin!(a In b)}341 --342 a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)}343 a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)}344 --345 a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)}346 a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)}347 --348 a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)}349 a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)}350 a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)}351 --352 unaryop(<"+">) _ b:@ {expr_un!(Plus b)}353 unaryop(<"-">) _ b:@ {expr_un!(Minus b)}354 unaryop(<"!">) _ b:@ {expr_un!(Not b)}355 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}356 --357 a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}358 indexable:(@) _ parts:index_part(s)+ {Expr::Index{indexable, parts}}359 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}360 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}361 --362 e:expr_basic(s) {e}363 }364 pub rule index_part(s: &ParserSettings) -> IndexPart365 = n:("?" _ ensure_null_coaelse())? "." _ value:id_loc(s) {IndexPart {366 value,367 #[cfg(feature = "exp-null-coaelse")]368 null_coaelse: n.is_some(),369 }}370 / n:("?" _ "." _ ensure_null_coaelse())? "[" _ value:expr(s) _ "]" {IndexPart {371 value,372 #[cfg(feature = "exp-null-coaelse")]373 null_coaelse: n.is_some(),374 }}375376 pub rule jsonnet(s: &ParserSettings) -> LocExpr = _ e:expr(s) _ {e}377 }378}379380pub type ParseError = peg::error::ParseError<peg::str::LineCol>;381pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {382 jsonnet_parser::jsonnet(str, settings)383}384/// Used for importstr values385pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> LocExpr {386 let len = str.len();387 LocExpr::new(Expr::Str(str), Span(settings.source.clone(), 0, len as u32))388}389390#[cfg(test)]391pub mod tests {392 use jrsonnet_interner::IStr;393 use BinaryOpType::*;394395 use super::{expr::*, parse};396 use crate::{source::Source, ParserSettings};397398 macro_rules! parse {399 ($s:expr) => {400 parse(401 $s,402 &ParserSettings {403 source: Source::new_virtual("<test>".into(), IStr::empty()),404 },405 )406 .unwrap()407 };408 }409410 macro_rules! el {411 ($expr:expr, $from:expr, $to:expr$(,)?) => {412 LocExpr::new(413 $expr,414 Span(415 Source::new_virtual("<test>".into(), IStr::empty()),416 $from,417 $to,418 ),419 )420 };421 }422423 #[test]424 fn multiline_string() {425 assert_eq!(426 parse!("|||\n Hello world!\n a\n|||"),427 el!(Expr::Str("Hello world!\n a\n".into()), 0, 31),428 );429 assert_eq!(430 parse!("|||\n Hello world!\n a\n|||"),431 el!(Expr::Str("Hello world!\n a\n".into()), 0, 27),432 );433 assert_eq!(434 parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),435 el!(Expr::Str("Hello world!\n\ta\n".into()), 0, 27),436 );437 assert_eq!(438 parse!("|||\n Hello world!\n a\n |||"),439 el!(Expr::Str("Hello world!\n a\n".into()), 0, 30),440 );441 }442443 #[test]444 fn slice() {445 parse!("a[1:]");446 parse!("a[1::]");447 parse!("a[:1:]");448 parse!("a[::1]");449 parse!("str[:len - 1]");450 }451452 #[test]453 fn string_escaping() {454 assert_eq!(455 parse!(r#""Hello, \"world\"!""#),456 el!(Expr::Str(r#"Hello, "world"!"#.into()), 0, 19),457 );458 assert_eq!(459 parse!(r#"'Hello \'world\'!'"#),460 el!(Expr::Str("Hello 'world'!".into()), 0, 18),461 );462 assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into()), 0, 6));463 }464465 #[test]466 fn string_unescaping() {467 assert_eq!(468 parse!(r#""Hello\nWorld""#),469 el!(Expr::Str("Hello\nWorld".into()), 0, 14),470 );471 }472473 #[test]474 fn string_verbantim() {475 assert_eq!(476 parse!(r#"@"Hello\n""World""""#),477 el!(Expr::Str("Hello\\n\"World\"".into()), 0, 19),478 );479 }480481 #[test]482 fn imports() {483 assert_eq!(484 parse!("import \"hello\""),485 el!(Expr::Import(el!(Expr::Str("hello".into()), 7, 14)), 0, 14),486 );487 assert_eq!(488 parse!("importstr \"garnish.txt\""),489 el!(490 Expr::ImportStr(el!(Expr::Str("garnish.txt".into()), 10, 23)),491 0,492 23493 )494 );495 assert_eq!(496 parse!("importbin \"garnish.bin\""),497 el!(498 Expr::ImportBin(el!(Expr::Str("garnish.bin".into()), 10, 23)),499 0,500 23501 )502 );503 }504505 #[test]506 fn empty_object() {507 assert_eq!(508 parse!("{}"),509 el!(Expr::Obj(ObjBody::MemberList(vec![])), 0, 2)510 );511 }512513 #[test]514 fn basic_math() {515 assert_eq!(516 parse!("2+2*2"),517 el!(518 Expr::BinaryOp(519 el!(Expr::Num(2.0), 0, 1),520 Add,521 el!(522 Expr::BinaryOp(el!(Expr::Num(2.0), 2, 3), Mul, el!(Expr::Num(2.0), 4, 5)),523 2,524 5525 )526 ),527 0,528 5529 )530 );531 }532533 #[test]534 fn basic_math_with_indents() {535 assert_eq!(536 parse!("2 + 2 * 2 "),537 el!(538 Expr::BinaryOp(539 el!(Expr::Num(2.0), 0, 1),540 Add,541 el!(542 Expr::BinaryOp(el!(Expr::Num(2.0), 7, 8), Mul, el!(Expr::Num(2.0), 13, 14),),543 7,544 14545 ),546 ),547 0,548 14549 )550 );551 }552553 #[test]554 fn basic_math_parened() {555 assert_eq!(556 parse!("2+(2+2*2)"),557 el!(558 Expr::BinaryOp(559 el!(Expr::Num(2.0), 0, 1),560 Add,561 el!(562 Expr::BinaryOp(563 el!(Expr::Num(2.0), 3, 4),564 Add,565 el!(566 Expr::BinaryOp(567 el!(Expr::Num(2.0), 5, 6),568 Mul,569 el!(Expr::Num(2.0), 7, 8),570 ),571 5,572 8573 ),574 ),575 3,576 8577 ),578 ),579 0,580 9581 )582 );583 }584585 /// Comments should not affect parsing586 #[test]587 fn comments() {588 assert_eq!(589 parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),590 el!(591 Expr::BinaryOp(592 el!(Expr::Num(2.0), 0, 1),593 Add,594 el!(595 Expr::BinaryOp(596 el!(Expr::Num(3.0), 22, 23),597 Mul,598 el!(Expr::Num(4.0), 40, 41)599 ),600 22,601 41602 )603 ),604 0,605 41606 )607 );608 }609610 #[test]611 fn suffix() {612 // assert_eq!(parse!("std.test"), el!(Expr::Num(2.2)));613 // assert_eq!(parse!("std(2)"), el!(Expr::Num(2.2)));614 // assert_eq!(parse!("std.test(2)"), el!(Expr::Num(2.2)));615 // assert_eq!(parse!("a[b]"), el!(Expr::Num(2.2)))616 }617618 #[test]619 fn array_comp() {620 use Expr::*;621 /*622 `ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Var("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`,623 `ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Str("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`624 */625 assert_eq!(626 parse!("[std.deepJoin(x) for x in arr]"),627 el!(628 ArrComp(629 el!(630 Apply(631 el!(632 Index {633 indexable: el!(Var("std".into()), 1, 4),634 parts: vec![IndexPart {635 value: el!(Str("deepJoin".into()), 5, 13),636 #[cfg(feature = "exp-null-coaelse")]637 null_coaelse: false,638 }],639 },640 1,641 13642 ),643 ArgsDesc::new(vec![el!(Var("x".into()), 14, 15)], vec![]),644 false,645 ),646 1,647 16648 ),649 vec![CompSpec::ForSpec(ForSpecData(650 Destruct::Full("x".into()),651 el!(Var("arr".into()), 26, 29)652 ))]653 ),654 0,655 30656 ),657 )658 }659660 #[test]661 fn reserved() {662 use Expr::*;663 assert_eq!(parse!("null"), el!(Literal(LiteralType::Null), 0, 4));664 assert_eq!(parse!("nulla"), el!(Var("nulla".into()), 0, 5));665 }666667 #[test]668 fn multiple_args_buf() {669 parse!("a(b, null_fields)");670 }671672 #[test]673 fn infix_precedence() {674 use Expr::*;675 assert_eq!(676 parse!("!a && !b"),677 el!(678 BinaryOp(679 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),680 And,681 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 7, 8)), 6, 8)682 ),683 0,684 8685 )686 );687 }688689 #[test]690 fn infix_precedence_division() {691 use Expr::*;692 assert_eq!(693 parse!("!a / !b"),694 el!(695 BinaryOp(696 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),697 Div,698 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 6, 7)), 5, 7)699 ),700 0,701 7702 )703 );704 }705706 #[test]707 fn double_negation() {708 use Expr::*;709 assert_eq!(710 parse!("!!a"),711 el!(712 UnaryOp(713 UnaryOpType::Not,714 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 2, 3)), 1, 3)715 ),716 0,717 3718 )719 )720 }721722 #[test]723 fn array_test_error() {724 parse!("[a for a in b if c for e in f]");725 // ^^^^ failed code726 }727728 #[test]729 fn missing_newline_between_comment_and_eof() {730 parse!(731 "{a:1}732733 //+213"734 );735 }736737 #[test]738 fn default_param_before_nondefault() {739 parse!("local x(foo = 'foo', bar) = null; null");740 }741742 #[test]743 fn add_location_info_to_all_sub_expressions() {744 use Expr::*;745746 let file_name = Source::new_virtual("<test>".into(), IStr::empty());747 let expr = parse(748 "{} { local x = 1, x: x } + {}",749 &ParserSettings { source: file_name },750 )751 .unwrap();752 assert_eq!(753 expr,754 el!(755 BinaryOp(756 el!(757 ObjExtend(758 el!(Obj(ObjBody::MemberList(vec![])), 0, 2),759 ObjBody::MemberList(vec![760 Member::BindStmt(BindSpec::Field {761 into: Destruct::Full("x".into()),762 value: el!(Num(1.0), 15, 16)763 }),764 Member::Field(FieldMember {765 name: FieldName::Fixed("x".into()),766 plus: false,767 params: None,768 visibility: Visibility::Normal,769 value: el!(Var("x".into()), 21, 22),770 })771 ])772 ),773 0,774 24775 ),776 BinaryOpType::Add,777 el!(Obj(ObjBody::MemberList(vec![])), 27, 29),778 ),779 0,780 29781 ),782 );783 }784}1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]23use std::rc::Rc;45use peg::parser;6mod expr;7pub use expr::*;8pub use jrsonnet_interner::IStr;9pub use peg;10mod location;11mod source;12mod unescape;13pub use location::CodeLocation;14pub use source::{15 Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,16 SourcePathT, SourceVirtual,17};1819pub struct ParserSettings {20 pub source: Source,21}2223macro_rules! expr_bin {24 ($a:ident $op:ident $b:ident) => {25 Expr::BinaryOp(Box::new(BinaryOp {26 lhs: $a,27 op: $op,28 rhs: $b,29 }))30 };31}32macro_rules! expr_un {33 ($op:ident $a:ident) => {34 Expr::UnaryOp($op, Box::new($a))35 };36}3738parser! {39 grammar jsonnet_parser() for str {40 use peg::ParseLiteral;4142 rule eof() = quiet!{![_]} / expected!("<eof>")43 rule eol() = "\n" / eof()4445 /// Standard C-like comments46 rule comment()47 = "//" (!eol()[_])* eol()48 / "/*" (!("*/")[_])* "*/"49 / "#" (!eol()[_])* eol()5051 rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")52 rule _() = quiet!{([' ' | '\r' | '\n' | '\t']+) / comment()}* / expected!("<whitespace>")5354 /// For comma-delimited elements55 rule comma() = quiet!{_ "," _} / expected!("<comma>")56 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().next().unwrap()}57 rule digit() -> char = d:$(['0'..='9']) {d.chars().next().unwrap()}58 rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']59 /// Sequence of digits60 rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }61 /// Number in scientific notation format62 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace("_","").parse().map_err(|_| "<number>") }} / expected!("<number>")6364 /// Reserved word followed by any non-alphanumberic65 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()66 rule id() -> IStr = v:$(quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")) { v.into() }6768 rule keyword(id: &'static str) -> ()69 = ##parse_string_literal(id) end_of_ident()7071 pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr.map(Rc::new)) }72 pub rule params(s: &ParserSettings) -> expr::ParamsDesc73 = params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }74 / { expr::ParamsDesc(Rc::new(Vec::new())) }7576 pub rule arg(s: &ParserSettings) -> (Option<IStr>, Rc<Spanned<Expr>>)77 = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("<argument name>")) expr:expr(s) {(name, Rc::new(expr))}7879 pub rule args(s: &ParserSettings) -> expr::ArgsDesc80 = args:arg(s)**comma() comma()? {?81 let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();82 let mut unnamed = Vec::with_capacity(unnamed_count);83 let mut named = Vec::with_capacity(args.len() - unnamed_count);84 let mut named_started = false;85 for (name, value) in args {86 if let Some(name) = name {87 named_started = true;88 named.push((name, value));89 } else {90 if named_started {91 return Err("<named argument>")92 }93 unnamed.push(value);94 }95 }96 Ok(expr::ArgsDesc::new(unnamed, named))97 }9899 pub rule destruct_rest() -> expr::DestructRest100 = "..." into:(_ into:id() {into})? {if let Some(into) = into {101 expr::DestructRest::Keep(into)102 } else {expr::DestructRest::Drop}}103 pub rule destruct_array(s: &ParserSettings) -> expr::Destruct104 = "[" _ start:destruct(s)**comma() rest:(105 comma() _ rest:destruct_rest()? end:(106 comma() end:destruct(s)**comma() (_ comma())? {end}107 / comma()? {Vec::new()}108 ) {(rest, end)}109 / comma()? {(None, Vec::new())}110 ) _ "]" {?111 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Array {112 start,113 rest: rest.0,114 end: rest.1,115 });116 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")117 }118 pub rule destruct_object(s: &ParserSettings) -> expr::Destruct119 = "{" _120 fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma()121 rest:(122 comma() rest:destruct_rest()? {rest}123 / comma()? {None}124 )125 _ "}" {?126 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Object {127 fields,128 rest,129 });130 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")131 }132 pub rule destruct(s: &ParserSettings) -> expr::Destruct133 = v:id() {expr::Destruct::Full(v)}134 / "?" {?135 #[cfg(feature = "exp-destruct")] return Ok(expr::Destruct::Skip);136 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")137 }138 / arr:destruct_array(s) {arr}139 / obj:destruct_object(s) {obj}140141 pub rule bind(s: &ParserSettings) -> expr::BindSpec142 = into:destruct(s) _ "=" _ expr:expr(s) {expr::BindSpec::Field{into, value: Rc::new(expr)}}143 / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec::Function{name, params, value: Rc::new(expr)}}144145 pub rule assertion(s: &ParserSettings) -> expr::AssertStmt146 = keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }147148 pub rule whole_line() -> &'input str149 = str:$((!['\n'][_])* "\n") {str}150 pub rule string_block() -> String151 = "|||" chomped:"-"? (!['\n']single_whitespace())* "\n"152 empty_lines:$(['\n']*)153 prefix:[' ' | '\t']+ first_line:whole_line()154 lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*155 [' ' | '\t']*<, {prefix.len() - 1}> "|||"156 {157 let mut l = empty_lines.to_owned();158 l.push_str(first_line);159 l.extend(lines);160 if chomped.is_some() {161 debug_assert!(l.ends_with('\n'));162 l.truncate(l.len() - 1);163 }164 l165 }166167 rule hex_char()168 = quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")169170 rule string_char(c: rule<()>)171 = (!['\\']!c()[_])+172 / "\\\\"173 / "\\u" hex_char() hex_char() hex_char() hex_char()174 / "\\x" hex_char() hex_char()175 / ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't' | '"' | '\''] } / expected!("<escape character>"))176 pub rule string() -> String177 = ['"'] str:$(string_char(<"\"">)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}178 / ['\''] str:$(string_char(<"\'">)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}179 / quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}180 / "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}181 / string_block() } / expected!("<string>")182183 pub rule field_name(s: &ParserSettings) -> expr::FieldName184 = name:id() {expr::FieldName::Fixed(name)}185 / name:string() {expr::FieldName::Fixed(name.into())}186 / "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}187 pub rule visibility() -> expr::Visibility188 = ":::" {expr::Visibility::Unhide}189 / "::" {expr::Visibility::Hidden}190 / ":" {expr::Visibility::Normal}191 pub rule field(s: &ParserSettings) -> expr::FieldMember192 = name:field_name(s) _ plus:"+"? _ visibility:visibility() _ value:expr(s) {expr::FieldMember{193 name,194 plus: plus.is_some(),195 params: None,196 visibility,197 value: Rc::new(value),198 }}199 / name:field_name(s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {expr::FieldMember{200 name,201 plus: false,202 params: Some(params),203 visibility,204 value: Rc::new(value),205 }}206 pub rule obj_local(s: &ParserSettings) -> BindSpec207 = keyword("local") _ bind:bind(s) {bind}208 pub rule member(s: &ParserSettings) -> expr::Member209 = bind:obj_local(s) {expr::Member::BindStmt(bind)}210 / assertion:assertion(s) {expr::Member::AssertStmt(Rc::new(assertion))}211 / field:field(s) {expr::Member::Field(field)}212 pub rule objinside(s: &ParserSettings) -> expr::ObjBody213 = pre_locals:(b: obj_local(s) comma() {b})* &"[" field:field(s) post_locals:(comma() b:obj_local(s) {b})* _ ("," _)? forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {214 let mut compspecs = vec![CompSpec::ForSpec(forspec)];215 compspecs.extend(others.unwrap_or_default());216 expr::ObjBody::ObjComp(expr::ObjComp{217 pre_locals,218 field: Rc::new(field),219 post_locals,220 compspecs,221 })222 }223 / members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}224 pub rule ifspec(s: &ParserSettings) -> IfSpecData225 = keyword("if") _ expr:expr(s) {IfSpecData(expr)}226 pub rule forspec(s: &ParserSettings) -> ForSpecData227 = keyword("for") _ id:destruct(s) _ keyword("in") _ cond:expr(s) {ForSpecData(id, cond)}228 pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>229 = s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}230 pub rule local_expr(s: &ParserSettings) -> Expr231 = keyword("local") _ binds:bind(s) ** comma() (_ ",")? _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, Box::new(expr)) }232 pub rule string_expr(s: &ParserSettings) -> Expr233 = s:string() {Expr::Str(s.into())}234 pub rule obj_expr(s: &ParserSettings) -> Expr235 = "{" _ body:objinside(s) _ "}" {Expr::Obj(body)}236 pub rule array_expr(s: &ParserSettings) -> Expr237 = "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(Rc::new(elems))}238 pub rule array_comp_expr(s: &ParserSettings) -> Expr239 = "[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {240 let mut specs = vec![CompSpec::ForSpec(forspec)];241 specs.extend(others.unwrap_or_default());242 Expr::ArrComp(Rc::new(expr), specs)243 }244 pub rule number_expr(s: &ParserSettings) -> Expr245 = n:number() {? if n.is_finite() {246 Ok(expr::Expr::Num(n))247 } else {248 Err("!!!numbers are finite")249 }}250 pub rule var_expr(s: &ParserSettings) -> Expr251 = n:id() { expr::Expr::Var(n) }252 pub rule id_loc(s: &ParserSettings) -> Spanned<Expr>253 = a:position!() n:id() b:position!() { Spanned::new(expr::Expr::Str(n), Span(s.source.clone(), a as u32,b as u32)) }254 pub rule if_then_else_expr(s: &ParserSettings) -> Expr255 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse(Box::new(IfElse{256 cond,257 cond_then,258 cond_else,259 }))}260261 pub rule literal(s: &ParserSettings) -> Expr262 = v:(263 keyword("null") {LiteralType::Null}264 / keyword("true") {LiteralType::True}265 / keyword("false") {LiteralType::False}266 / keyword("self") {LiteralType::This}267 / keyword("$") {LiteralType::Dollar}268 / keyword("super") {LiteralType::Super}269 ) {Expr::Literal(v)}270271 rule import_kind() -> ImportKind272 = keyword("importstr") { ImportKind::Str }273 / keyword("importbin") { ImportKind::Bin }274 / keyword("import") { ImportKind::Normal }275276 pub rule expr_basic(s: &ParserSettings) -> Expr277 = literal(s)278279 / string_expr(s) / number_expr(s)280 / array_expr(s)281 / obj_expr(s)282 / array_expr(s)283 / array_comp_expr(s)284285 / kind:import_kind() _ path:expr(s) {Expr::Import(kind, Box::new(path))}286287 / var_expr(s)288 / local_expr(s)289 / if_then_else_expr(s)290291 / keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, Rc::new(expr))}292 / assert:assertion(s) _ ";" _ rest:expr(s) { Expr::AssertExpr(Rc::new(AssertExpr{293 assert, rest294 })) }295296 / keyword("error") _ expr:expr(s) { Expr::ErrorStmt(Box::new(expr)) }297298 rule slice_part(s: &ParserSettings) -> Option<Spanned<Expr>>299 = _ e:(e:expr(s) _{e})? {e}300 pub rule slice_desc(s: &ParserSettings) -> SliceDesc301 = start:slice_part(s) ":" pair:(end:slice_part(s) step:(":" e:slice_part(s){e})? {(end, step.flatten())})? {302 let (end, step) = if let Some((end, step)) = pair {303 (end, step)304 }else{305 (None, None)306 };307308 SliceDesc { start, end, step }309 }310311 rule binop(x: rule<()>) -> ()312 = quiet!{ x() } / expected!("<binary op>")313 rule unaryop(x: rule<()>) -> ()314 = quiet!{ x() } / expected!("<unary op>")315316 rule ensure_null_coaelse()317 = "" {?318 #[cfg(not(feature = "exp-null-coaelse"))] return Err("!!!experimental null coaelscing was not enabled");319 #[cfg(feature = "exp-null-coaelse")] Ok(())320 }321 use BinaryOpType::*;322 use UnaryOpType::*;323 rule expr(s: &ParserSettings) -> Spanned<Expr>324 = precedence! {325 "(" _ e:expr(s) _ ")" {e}326 start:position!() v:@ end:position!() { Spanned::new(v, Span(s.source.clone(), start as u32, end as u32)) }327 --328 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}329 a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ {330 #[cfg(feature = "exp-null-coaelse")] return expr_bin!(a NullCoaelse b);331 unreachable!("ensure_null_coaelse will fail if feature is not enabled")332 }333 --334 a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}335 --336 a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)}337 --338 a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)}339 --340 a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)}341 --342 a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)}343 a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)}344 --345 a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)}346 a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)}347 a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)}348 a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)}349 a:(@) _ binop(<keyword("in")>) _ b:@ {expr_bin!(a In b)}350 --351 a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)}352 a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)}353 --354 a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)}355 a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)}356 --357 a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)}358 a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)}359 a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)}360 --361 unaryop(<"+">) _ b:@ {expr_un!(Plus b)}362 unaryop(<"-">) _ b:@ {expr_un!(Minus b)}363 unaryop(<"!">) _ b:@ {expr_un!(Not b)}364 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}365 --366 value:(@) _ "[" _ slice:slice_desc(s) _ "]" {Expr::Slice(Box::new(Slice{value, slice}))}367 indexable:(@) _ parts:index_part(s)+ {Expr::Index{indexable: Box::new(indexable), parts}}368 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(Box::new(a), args, ts.is_some())}369 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(Rc::new(a), body)}370 --371 e:expr_basic(s) {e}372 }373 pub rule index_part(s: &ParserSettings) -> IndexPart374 = n:("?" _ ensure_null_coaelse())? "." _ value:id_loc(s) {IndexPart {375 value,376 #[cfg(feature = "exp-null-coaelse")]377 null_coaelse: n.is_some(),378 }}379 / n:("?" _ "." _ ensure_null_coaelse())? "[" _ value:expr(s) _ "]" {IndexPart {380 value,381 #[cfg(feature = "exp-null-coaelse")]382 null_coaelse: n.is_some(),383 }}384385 pub rule jsonnet(s: &ParserSettings) -> Spanned<Expr> = _ e:expr(s) _ {e}386 }387}388389pub type ParseError = peg::error::ParseError<peg::str::LineCol>;390pub fn parse(str: &str, settings: &ParserSettings) -> Result<Spanned<Expr>, ParseError> {391 jsonnet_parser::jsonnet(str, settings)392}393/// Used for importstr values394pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> Spanned<Expr> {395 let len = str.len();396 Spanned::new(Expr::Str(str), Span(settings.source.clone(), 0, len as u32))397}398399#[cfg(test)]400pub mod tests {401 use insta::assert_snapshot;402 use jrsonnet_interner::IStr;403404 use super::parse;405 use crate::{source::Source, ParserSettings};406407 fn parsep(s: &str) -> String {408 let v = parse(409 s,410 &ParserSettings {411 source: Source::new_virtual("<test>".into(), IStr::empty()),412 },413 )414 .unwrap();415 format!("{v:#?}")416 }417418 macro_rules! parse {419 ($s:expr) => {420 assert_snapshot!(parsep($s));421 };422 }423424 #[test]425 fn multiline_string() {426 parse!("|||\n Hello world!\n a\n|||");427 parse!("|||\n Hello world!\n a\n|||");428 parse!("|||\n\t\tHello world!\n\t\t\ta\n|||");429 parse!("|||\n Hello world!\n a\n |||");430 }431432 #[test]433 fn slice() {434 parse!("a[1:]");435 parse!("a[1::]");436 parse!("a[:1:]");437 parse!("a[::1]");438 parse!("str[:len - 1]");439 }440441 #[test]442 fn string_escaping() {443 parse!(r#""Hello, \"world\"!""#);444 parse!(r#"'Hello \'world\'!'"#);445 parse!(r#"'\\\\'"#);446 }447448 #[test]449 fn string_unescaping() {450 parse!(r#""Hello\nWorld""#);451 }452453 #[test]454 fn string_verbantim() {455 parse!(r#"@"Hello\n""World""""#);456 }457458 #[test]459 fn imports() {460 parse!("import \"hello\"");461 parse!("importstr \"garnish.txt\"");462 parse!("importbin \"garnish.bin\"");463 }464465 #[test]466 fn empty_object() {467 parse!("{}");468 }469470 #[test]471 fn basic_math() {472 parse!("2+2*2");473 parse!("2 + 2 * 2 ");474 parse!("2+(2+2*2)");475 parse!("2//comment\n+//comment\n3/*test*/*/*test*/4");476 }477478 #[test]479 fn suffix() {480 parse!("std.test");481 parse!("std(2)");482 parse!("std.test(2)");483 parse!("a[b]");484 }485486 #[test]487 fn array_comp() {488 parse!("[std.deepJoin(x) for x in arr]");489 }490491 #[test]492 fn reserved() {493 parse!("null");494 parse!("nulla");495 }496497 #[test]498 fn multiple_args_buf() {499 parse!("a(b, null_fields)");500 }501502 #[test]503 fn infix_precedence() {504 parse!("!a && !b");505 parse!("!a / !b");506 }507508 #[test]509 fn double_negation() {510 parse!("!!a");511 }512513 #[test]514 fn array_test_error() {515 parse!("[a for a in b if c for e in f]");516 }517518 #[test]519 fn missing_newline_between_comment_and_eof() {520 parse!(521 "{a:1}522523 //+213"524 );525 }526527 #[test]528 fn default_param_before_nondefault() {529 parse!("local x(foo = 'foo', bar) = null; null");530 }531532 #[test]533 fn add_location_info_to_all_sub_expressions() {534 parse!("{} { local x = 1, x: x } + {}");535 }536}crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__add_location_info_to_all_sub_expressions.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__add_location_info_to_all_sub_expressions.snap
@@ -0,0 +1,48 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"{} { local x = 1, x: x } + {}\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: ObjExtend(
+ Obj(
+ MemberList(
+ [],
+ ),
+ ) from virtual:<test>:0-2,
+ MemberList(
+ [
+ BindStmt(
+ Field {
+ into: Full(
+ "x",
+ ),
+ value: Num(
+ 1.0,
+ ) from virtual:<test>:15-16,
+ },
+ ),
+ Field(
+ FieldMember {
+ name: Fixed(
+ "x",
+ ),
+ plus: false,
+ params: None,
+ visibility: Normal,
+ value: Var(
+ "x",
+ ) from virtual:<test>:21-22,
+ },
+ ),
+ ],
+ ),
+ ) from virtual:<test>:0-24,
+ op: Add,
+ rhs: Obj(
+ MemberList(
+ [],
+ ),
+ ) from virtual:<test>:27-29,
+ },
+) from virtual:<test>:0-29
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__array_comp.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__array_comp.snap
@@ -0,0 +1,41 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"[std.deepJoin(x) for x in arr]\")"
+---
+ArrComp(
+ Apply(
+ Index {
+ indexable: Var(
+ "std",
+ ) from virtual:<test>:1-4,
+ parts: [
+ IndexPart {
+ value: Str(
+ "deepJoin",
+ ) from virtual:<test>:5-13,
+ },
+ ],
+ } from virtual:<test>:1-13,
+ ArgsDesc {
+ unnamed: [
+ Var(
+ "x",
+ ) from virtual:<test>:14-15,
+ ],
+ named: [],
+ },
+ false,
+ ) from virtual:<test>:1-16,
+ [
+ ForSpec(
+ ForSpecData(
+ Full(
+ "x",
+ ),
+ Var(
+ "arr",
+ ) from virtual:<test>:26-29,
+ ),
+ ),
+ ],
+) from virtual:<test>:0-30
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__array_test_error.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__array_test_error.snap
@@ -0,0 +1,38 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"[a for a in b if c for e in f]\")"
+---
+ArrComp(
+ Var(
+ "a",
+ ) from virtual:<test>:1-2,
+ [
+ ForSpec(
+ ForSpecData(
+ Full(
+ "a",
+ ),
+ Var(
+ "b",
+ ) from virtual:<test>:12-13,
+ ),
+ ),
+ IfSpec(
+ IfSpecData(
+ Var(
+ "c",
+ ) from virtual:<test>:17-18,
+ ),
+ ),
+ ForSpec(
+ ForSpecData(
+ Full(
+ "e",
+ ),
+ Var(
+ "f",
+ ) from virtual:<test>:28-29,
+ ),
+ ),
+ ],
+) from virtual:<test>:0-30
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-2.snap
@@ -0,0 +1,23 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"2\t+ \t 2\t *\t2 \t\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:0-1,
+ op: Add,
+ rhs: BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:7-8,
+ op: Mul,
+ rhs: Num(
+ 2.0,
+ ) from virtual:<test>:13-14,
+ },
+ ) from virtual:<test>:7-14,
+ },
+) from virtual:<test>:0-14
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-3.snap
@@ -0,0 +1,31 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"2+(2+2*2)\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:0-1,
+ op: Add,
+ rhs: BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:3-4,
+ op: Add,
+ rhs: BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:5-6,
+ op: Mul,
+ rhs: Num(
+ 2.0,
+ ) from virtual:<test>:7-8,
+ },
+ ) from virtual:<test>:5-8,
+ },
+ ) from virtual:<test>:3-8,
+ },
+) from virtual:<test>:0-9
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-4.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math-4.snap
@@ -0,0 +1,23 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"2//comment\\n+//comment\\n3/*test*/*/*test*/4\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:0-1,
+ op: Add,
+ rhs: BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 3.0,
+ ) from virtual:<test>:22-23,
+ op: Mul,
+ rhs: Num(
+ 4.0,
+ ) from virtual:<test>:40-41,
+ },
+ ) from virtual:<test>:22-41,
+ },
+) from virtual:<test>:0-41
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__basic_math.snap
@@ -0,0 +1,23 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"2+2*2\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:0-1,
+ op: Add,
+ rhs: BinaryOp(
+ BinaryOp {
+ lhs: Num(
+ 2.0,
+ ) from virtual:<test>:2-3,
+ op: Mul,
+ rhs: Num(
+ 2.0,
+ ) from virtual:<test>:4-5,
+ },
+ ) from virtual:<test>:2-5,
+ },
+) from virtual:<test>:0-5
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__default_param_before_nondefault.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__default_param_before_nondefault.snap
@@ -0,0 +1,37 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"local x(foo = 'foo', bar) = null; null\")"
+---
+LocalExpr(
+ [
+ Function {
+ name: "x",
+ params: ParamsDesc(
+ [
+ Param(
+ Full(
+ "foo",
+ ),
+ Some(
+ Str(
+ "foo",
+ ) from virtual:<test>:14-19,
+ ),
+ ),
+ Param(
+ Full(
+ "bar",
+ ),
+ None,
+ ),
+ ],
+ ),
+ value: Literal(
+ Null,
+ ) from virtual:<test>:28-32,
+ },
+ ],
+ Literal(
+ Null,
+ ) from virtual:<test>:34-38,
+) from virtual:<test>:0-38
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__double_negation.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__double_negation.snap
@@ -0,0 +1,13 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"!!a\")"
+---
+UnaryOp(
+ Not,
+ UnaryOp(
+ Not,
+ Var(
+ "a",
+ ) from virtual:<test>:2-3,
+ ) from virtual:<test>:1-3,
+) from virtual:<test>:0-3
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__empty_object.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__empty_object.snap
@@ -0,0 +1,9 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"{}\")"
+---
+Obj(
+ MemberList(
+ [],
+ ),
+) from virtual:<test>:0-2
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports-2.snap
@@ -0,0 +1,10 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"importstr \\\"garnish.txt\\\"\")"
+---
+Import(
+ Str,
+ Str(
+ "garnish.txt",
+ ) from virtual:<test>:10-23,
+) from virtual:<test>:0-23
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports-3.snap
@@ -0,0 +1,10 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"importbin \\\"garnish.bin\\\"\")"
+---
+Import(
+ Bin,
+ Str(
+ "garnish.bin",
+ ) from virtual:<test>:10-23,
+) from virtual:<test>:0-23
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__imports.snap
@@ -0,0 +1,10 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"import \\\"hello\\\"\")"
+---
+Import(
+ Normal,
+ Str(
+ "hello",
+ ) from virtual:<test>:7-14,
+) from virtual:<test>:0-14
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__infix_precedence-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__infix_precedence-2.snap
@@ -0,0 +1,21 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"!a / !b\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: UnaryOp(
+ Not,
+ Var(
+ "a",
+ ) from virtual:<test>:1-2,
+ ) from virtual:<test>:0-2,
+ op: Div,
+ rhs: UnaryOp(
+ Not,
+ Var(
+ "b",
+ ) from virtual:<test>:6-7,
+ ) from virtual:<test>:5-7,
+ },
+) from virtual:<test>:0-7
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__infix_precedence.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__infix_precedence.snap
@@ -0,0 +1,21 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"!a && !b\")"
+---
+BinaryOp(
+ BinaryOp {
+ lhs: UnaryOp(
+ Not,
+ Var(
+ "a",
+ ) from virtual:<test>:1-2,
+ ) from virtual:<test>:0-2,
+ op: And,
+ rhs: UnaryOp(
+ Not,
+ Var(
+ "b",
+ ) from virtual:<test>:7-8,
+ ) from virtual:<test>:6-8,
+ },
+) from virtual:<test>:0-8
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__missing_newline_between_comment_and_eof.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__missing_newline_between_comment_and_eof.snap
@@ -0,0 +1,23 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"{a:1}\n\n\t\t\t//+213\")"
+---
+Obj(
+ MemberList(
+ [
+ Field(
+ FieldMember {
+ name: Fixed(
+ "a",
+ ),
+ plus: false,
+ params: None,
+ visibility: Normal,
+ value: Num(
+ 1.0,
+ ) from virtual:<test>:3-4,
+ },
+ ),
+ ],
+ ),
+) from virtual:<test>:0-5
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-2.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"|||\\n Hello world!\\n a\\n|||\")"
+---
+Str(
+ "Hello world!\n a\n",
+) from virtual:<test>:0-27
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-3.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"|||\\n\\t\\tHello world!\\n\\t\\t\\ta\\n|||\")"
+---
+Str(
+ "Hello world!\n\ta\n",
+) from virtual:<test>:0-27
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-4.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string-4.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"|||\\n Hello world!\\n a\\n |||\")"
+---
+Str(
+ "Hello world!\n a\n",
+) from virtual:<test>:0-30
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiline_string.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"|||\\n Hello world!\\n a\\n|||\")"
+---
+Str(
+ "Hello world!\n a\n",
+) from virtual:<test>:0-31
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiple_args_buf.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__multiple_args_buf.snap
@@ -0,0 +1,21 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a(b, null_fields)\")"
+---
+Apply(
+ Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ ArgsDesc {
+ unnamed: [
+ Var(
+ "b",
+ ) from virtual:<test>:2-3,
+ Var(
+ "null_fields",
+ ) from virtual:<test>:5-16,
+ ],
+ named: [],
+ },
+ false,
+) from virtual:<test>:0-17
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__reserved-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__reserved-2.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"nulla\")"
+---
+Var(
+ "nulla",
+) from virtual:<test>:0-5
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__reserved.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__reserved.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"null\")"
+---
+Literal(
+ Null,
+) from virtual:<test>:0-4
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-2.snap
@@ -0,0 +1,20 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a[1::]\")"
+---
+Slice(
+ Slice {
+ value: Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ slice: SliceDesc {
+ start: Some(
+ Num(
+ 1.0,
+ ) from virtual:<test>:2-3,
+ ),
+ end: None,
+ step: None,
+ },
+ },
+) from virtual:<test>:0-6
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-3.snap
@@ -0,0 +1,20 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a[:1:]\")"
+---
+Slice(
+ Slice {
+ value: Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ slice: SliceDesc {
+ start: None,
+ end: Some(
+ Num(
+ 1.0,
+ ) from virtual:<test>:3-4,
+ ),
+ step: None,
+ },
+ },
+) from virtual:<test>:0-6
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-4.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-4.snap
@@ -0,0 +1,20 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a[::1]\")"
+---
+Slice(
+ Slice {
+ value: Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ slice: SliceDesc {
+ start: None,
+ end: None,
+ step: Some(
+ Num(
+ 1.0,
+ ) from virtual:<test>:4-5,
+ ),
+ },
+ },
+) from virtual:<test>:0-6
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-5.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice-5.snap
@@ -0,0 +1,28 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"str[:len - 1]\")"
+---
+Slice(
+ Slice {
+ value: Var(
+ "str",
+ ) from virtual:<test>:0-3,
+ slice: SliceDesc {
+ start: None,
+ end: Some(
+ BinaryOp(
+ BinaryOp {
+ lhs: Var(
+ "len",
+ ) from virtual:<test>:5-8,
+ op: Sub,
+ rhs: Num(
+ 1.0,
+ ) from virtual:<test>:11-12,
+ },
+ ) from virtual:<test>:5-12,
+ ),
+ step: None,
+ },
+ },
+) from virtual:<test>:0-13
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__slice.snap
@@ -0,0 +1,20 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a[1:]\")"
+---
+Slice(
+ Slice {
+ value: Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ slice: SliceDesc {
+ start: Some(
+ Num(
+ 1.0,
+ ) from virtual:<test>:2-3,
+ ),
+ end: None,
+ step: None,
+ },
+ },
+) from virtual:<test>:0-5
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping-2.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(r#\"'Hello \\'world\\'!'\"#)"
+---
+Str(
+ "Hello 'world'!",
+) from virtual:<test>:0-18
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping-3.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(r#\"'\\\\\\\\'\"#)"
+---
+Str(
+ "\\\\",
+) from virtual:<test>:0-6
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_escaping.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(r#\"\"Hello, \\\"world\\\"!\"\"#)"
+---
+Str(
+ "Hello, \"world\"!",
+) from virtual:<test>:0-19
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_unescaping.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_unescaping.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(r#\"\"Hello\\nWorld\"\"#)"
+---
+Str(
+ "Hello\nWorld",
+) from virtual:<test>:0-14
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_verbantim.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__string_verbantim.snap
@@ -0,0 +1,7 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(r#\"@\"Hello\\n\"\"World\"\"\"\"#)"
+---
+Str(
+ "Hello\\n\"World\"",
+) from virtual:<test>:0-19
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-2.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-2.snap
@@ -0,0 +1,18 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"std(2)\")"
+---
+Apply(
+ Var(
+ "std",
+ ) from virtual:<test>:0-3,
+ ArgsDesc {
+ unnamed: [
+ Num(
+ 2.0,
+ ) from virtual:<test>:4-5,
+ ],
+ named: [],
+ },
+ false,
+) from virtual:<test>:0-6
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-3.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-3.snap
@@ -0,0 +1,27 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"std.test(2)\")"
+---
+Apply(
+ Index {
+ indexable: Var(
+ "std",
+ ) from virtual:<test>:0-3,
+ parts: [
+ IndexPart {
+ value: Str(
+ "test",
+ ) from virtual:<test>:4-8,
+ },
+ ],
+ } from virtual:<test>:0-8,
+ ArgsDesc {
+ unnamed: [
+ Num(
+ 2.0,
+ ) from virtual:<test>:9-10,
+ ],
+ named: [],
+ },
+ false,
+) from virtual:<test>:0-11
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-4.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix-4.snap
@@ -0,0 +1,16 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"a[b]\")"
+---
+Index {
+ indexable: Var(
+ "a",
+ ) from virtual:<test>:0-1,
+ parts: [
+ IndexPart {
+ value: Var(
+ "b",
+ ) from virtual:<test>:2-3,
+ },
+ ],
+} from virtual:<test>:0-4
crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__suffix.snap
@@ -0,0 +1,16 @@
+---
+source: crates/jrsonnet-parser/src/lib.rs
+expression: "parsep(\"std.test\")"
+---
+Index {
+ indexable: Var(
+ "std",
+ ) from virtual:<test>:0-3,
+ parts: [
+ IndexPart {
+ value: Str(
+ "test",
+ ) from virtual:<test>:4-8,
+ },
+ ],
+} from virtual:<test>:0-8
crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -79,7 +79,7 @@
/// search location is applicable
///
/// Resolver may also return custom implementations of this trait, for example it may return http url in case of remotely loaded files
-#[derive(Eq, Debug, Clone, Acyclic)]
+#[derive(Eq, Clone, Acyclic)]
pub struct SourcePath(Rc<dyn SourcePathT>);
impl SourcePath {
pub fn new(inner: impl SourcePathT) -> Self {
@@ -111,6 +111,11 @@
write!(f, "{}", self.0)
}
}
+impl Debug for SourcePath {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?}", self.0)
+ }
+}
impl Default for SourcePath {
fn default() -> Self {
Self(Rc::new(SourceDefault))
@@ -213,13 +218,18 @@
///
/// It is used for --ext-code=.../--tla-code=.../standard library source code by default,
/// and user can construct arbitrary values by hand, without asking import resolver
-#[derive(Acyclic, Hash, PartialEq, Eq, Debug, Clone)]
+#[derive(Acyclic, Hash, PartialEq, Eq, Clone)]
pub struct SourceVirtual(pub IStr);
impl Display for SourceVirtual {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
+ write!(f, "virtual:{}", self.0)
}
}
+impl fmt::Debug for SourceVirtual {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "virtual:{}", self.0)
+ }
+}
impl SourcePathT for SourceVirtual {
fn is_default(&self) -> bool {
true
@@ -263,7 +273,7 @@
/// Either real file, or virtual
/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut
-#[derive(Clone, PartialEq, Eq, Debug, Acyclic)]
+#[derive(Clone, PartialEq, Eq, Acyclic)]
pub struct Source(pub Rc<(SourcePath, IStr)>);
impl Source {
@@ -290,3 +300,8 @@
location_to_offset(&self.0 .1, line, column)
}
}
+impl fmt::Debug for Source {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?}", self.0 .0)
+ }
+}