From 9411dba19aab06420fb74d475edaaca7a91f669e Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 19 Mar 2026 02:54:22 +0000 Subject: [PATCH] refactor(ir): explicit Rc wrapping --- --- 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", --- 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) -> Self { + pub fn expr(ctx: Context, exprs: Rc>>) -> Self { Self::new(ExprArray::new(ctx, exprs)) } --- 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 { +enum ArrayThunk { Computed(Val), Errored(Error), - Waiting(T), + Waiting, Pending, } #[derive(Debug, Trace, Clone)] pub struct ExprArray { ctx: Context, - cached: Cc>>>, + src: Rc>>, + cached: Cc>>, } impl ExprArray { - pub fn new(ctx: Context, items: impl IntoIterator) -> Self { + pub fn new(ctx: Context, src: Rc>>) -> 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 { inner: ArrValue, - cached: Cc>>>, + cached: Cc>>, mapper: FuncVal, } impl MappedArray { @@ -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)] --- 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, 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); --- 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; } -impl ErrorSource for &LocExpr { +impl ErrorSource for &Spanned { fn to_location(self) -> Option { Some(self.span()) } --- 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 { - fn is_trivial(expr: &LocExpr) -> bool { - match expr.expr() { +pub fn evaluate_trivial(expr: &Spanned) -> Option { + fn is_trivial(expr: &Spanned) -> 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>, +) -> Val { Val::Func(FuncVal::Normal(Cc::new(FuncDesc { name, ctx, @@ -215,7 +220,7 @@ #[derive(Trace)] struct UnboundValue { uctx: B, - value: LocExpr, + value: Rc>, name: IStr, } impl> Unbound for UnboundValue { @@ -245,7 +250,7 @@ #[derive(Trace)] struct UnboundMethod { uctx: B, - value: LocExpr, + value: Rc>, params: ParamsDesc, name: IStr, } @@ -301,7 +306,7 @@ #[derive(Trace)] struct ObjectAssert { uctx: B, - assert: AssertStmt, + assert: Rc, } impl> ObjectAssertion for ObjectAssert { fn run(&self, sup_this: SupThis) -> Result<()> { @@ -347,7 +352,7 @@ pub fn evaluate_apply( ctx: Context, - value: &LocExpr, + value: &Spanned, args: &ArgsDesc, loc: CallLocation<'_>, tailstrict: bool, @@ -389,23 +394,23 @@ Ok(()) } -pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result { +pub fn evaluate_named(ctx: Context, expr: &Spanned, name: IStr) -> Result { 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 { +pub fn evaluate(ctx: Context, expr: &Spanned) -> Result { 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( loc: CallLocation<'_>, ctx: Context, - expr: Option<&LocExpr>, + expr: Option<&Spanned>, desc: &'static str, ) -> Result> { 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 })? } --- 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, op: BinaryOpType, - b: &LocExpr, + b: &Spanned, ) -> Result { use BinaryOpType::*; use Val::*; --- 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>; } -impl ArgLike for &LocExpr { +impl ArgLike for &Rc> { fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result> { Ok(if tailstrict { Thunk::evaluated(evaluate(ctx, self)?) --- 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>, } 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, } --- 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, bytes: Option, - parsed: Option, + parsed: Option>>, evaluated: Option, 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), --- 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 --- 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), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)] @@ -34,8 +34,8 @@ } } -#[derive(Clone, Debug, PartialEq, Acyclic)] -pub struct AssertStmt(pub LocExpr, pub Option); +#[derive(Debug, PartialEq, Acyclic)] +pub struct AssertStmt(pub Spanned, pub Option>); #[derive(Debug, PartialEq, Acyclic)] pub struct FieldMember { @@ -43,14 +43,14 @@ pub plus: bool, pub params: Option, pub visibility: Visibility, - pub value: LocExpr, + pub value: Rc>, } #[derive(Debug, PartialEq, Acyclic)] pub enum Member { Field(FieldMember), BindStmt(BindSpec), - AssertStmt(AssertStmt), + AssertStmt(Rc), } #[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); +pub struct Param(pub Destruct, pub Option>>); /// Defined function parameters #[derive(Debug, Clone, PartialEq, Acyclic)] @@ -162,11 +162,11 @@ #[derive(Debug, PartialEq, Acyclic)] pub struct ArgsDesc { - pub unnamed: Vec, - pub named: Vec<(IStr, LocExpr)>, + pub unnamed: Vec>>, + pub named: Vec<(IStr, Rc>)>, } impl ArgsDesc { - pub fn new(unnamed: Vec, named: Vec<(IStr, LocExpr)>) -> Self { + pub fn new(unnamed: Vec>>, named: Vec<(IStr, Rc>)>) -> Self { Self { unnamed, named } } } @@ -192,7 +192,7 @@ }, #[cfg(feature = "exp-destruct")] Object { - fields: Vec<(IStr, Option, Option)>, + fields: Vec<(IStr, Option, Option>)>, rest: Option, }, } @@ -244,12 +244,12 @@ pub enum BindSpec { Field { into: Destruct, - value: LocExpr, + value: Rc>, }, Function { name: IStr, params: ParamsDesc, - value: LocExpr, + value: Rc>, }, } impl BindSpec { @@ -262,10 +262,10 @@ } #[derive(Debug, PartialEq, Acyclic)] -pub struct IfSpecData(pub LocExpr); +pub struct IfSpecData(pub Spanned); #[derive(Debug, PartialEq, Acyclic)] -pub struct ForSpecData(pub Destruct, pub LocExpr); +pub struct ForSpecData(pub Destruct, pub Spanned); #[derive(Debug, PartialEq, Acyclic)] pub enum CompSpec { @@ -276,7 +276,7 @@ #[derive(Debug, PartialEq, Acyclic)] pub struct ObjComp { pub pre_locals: Vec, - pub field: FieldMember, + pub field: Rc, pub post_locals: Vec, pub compspecs: Vec, } @@ -299,11 +299,44 @@ #[derive(Debug, PartialEq, Acyclic)] pub struct SliceDesc { - pub start: Option, - pub end: Option, - pub step: Option, + pub start: Option>, + pub end: Option>, + pub step: Option>, +} + +#[derive(Debug, PartialEq, Acyclic)] +pub struct AssertExpr { + pub assert: AssertStmt, + pub rest: Spanned, +} + +#[derive(Debug, PartialEq, Acyclic)] +pub struct BinaryOp { + pub lhs: Spanned, + pub op: BinaryOpType, + pub rhs: Spanned, +} + +#[derive(Debug, PartialEq, Acyclic)] +pub enum ImportKind { + Normal, + Str, + Bin, } +#[derive(Debug, PartialEq, Acyclic)] +pub struct IfElse { + pub cond: IfSpecData, + pub cond_then: Spanned, + pub cond_else: Option>, +} + +#[derive(Debug, PartialEq, Acyclic)] +pub struct Slice { + pub value: Spanned, + 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), + Arr(Rc>>), /// Array comprehension: /// ```jsonnet /// ingredients: [ @@ -329,51 +362,43 @@ /// ] /// ], /// ``` - ArrComp(LocExpr, Vec), + ArrComp(Rc>, Vec), /// Object: {a: 2} Obj(ObjBody), /// Object extension: var1 {b: 2} - ObjExtend(LocExpr, ObjBody), + ObjExtend(Rc>, ObjBody), /// -2 - UnaryOp(UnaryOpType, LocExpr), + UnaryOp(UnaryOpType, Box>), /// 2 - 2 - BinaryOp(LocExpr, BinaryOpType, LocExpr), + BinaryOp(Box), /// assert 2 == 2 : "Math is broken" - AssertExpr(AssertStmt, LocExpr), + AssertExpr(Rc), /// local a = 2; { b: a } - LocalExpr(Vec, LocExpr), + LocalExpr(Vec, Box>), - /// import "hello" - Import(LocExpr), - /// importStr "file.txt" - ImportStr(LocExpr), - /// importBin "file.txt" - ImportBin(LocExpr), + /// import* "hello" + Import(ImportKind, Box>), /// error "I'm broken" - ErrorStmt(LocExpr), + ErrorStmt(Box>), /// a(b, c) - Apply(LocExpr, ArgsDesc, bool), + Apply(Box>, ArgsDesc, bool), /// a[b], a.b, a?.b Index { - indexable: LocExpr, + indexable: Box>, parts: Vec, }, /// function(x) x - Function(ParamsDesc, LocExpr), + Function(ParamsDesc, Rc>), /// if true == false then 1 else 2 - IfElse { - cond: IfSpecData, - cond_then: LocExpr, - cond_else: Option, - }, - Slice(LocExpr, SliceDesc), + IfElse(Box), + Slice(Box), } #[derive(Debug, PartialEq, Acyclic)] pub struct IndexPart { - pub value: LocExpr, + pub value: Spanned, #[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, Span); +impl Deref for Spanned { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 } +} +impl Spanned { #[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 Debug for Spanned { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let expr = self.expr(); + let expr = &**self; if f.alternate() { write!(f, "{:#?}", expr)?; } else { --- a/crates/jrsonnet-parser/src/lib.rs +++ b/crates/jrsonnet-parser/src/lib.rs @@ -22,12 +22,16 @@ macro_rules! expr_bin { ($a:ident $op:ident $b:ident) => { - Expr::BinaryOp($a, $op, $b) + Expr::BinaryOp(Box::new(BinaryOp { + lhs: $a, + op: $op, + rhs: $b, + })) }; } macro_rules! expr_un { ($op:ident $a:ident) => { - Expr::UnaryOp($op, $a) + Expr::UnaryOp($op, Box::new($a)) }; } @@ -64,13 +68,13 @@ rule keyword(id: &'static str) -> () = ##parse_string_literal(id) end_of_ident() - pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) } + pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr.map(Rc::new)) } pub rule params(s: &ParserSettings) -> expr::ParamsDesc = params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) } / { expr::ParamsDesc(Rc::new(Vec::new())) } - pub rule arg(s: &ParserSettings) -> (Option, LocExpr) - = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("")) expr:expr(s) {(name, expr)} + pub rule arg(s: &ParserSettings) -> (Option, Rc>) + = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("")) expr:expr(s) {(name, Rc::new(expr))} pub rule args(s: &ParserSettings) -> expr::ArgsDesc = args:arg(s)**comma() comma()? {? @@ -135,8 +139,8 @@ / obj:destruct_object(s) {obj} pub rule bind(s: &ParserSettings) -> expr::BindSpec - = into:destruct(s) _ "=" _ expr:expr(s) {expr::BindSpec::Field{into, value: expr}} - / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec::Function{name, params, value: expr}} + = into:destruct(s) _ "=" _ expr:expr(s) {expr::BindSpec::Field{into, value: Rc::new(expr)}} + / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec::Function{name, params, value: Rc::new(expr)}} pub rule assertion(s: &ParserSettings) -> expr::AssertStmt = keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) } @@ -190,20 +194,20 @@ plus: plus.is_some(), params: None, visibility, - value, + value: Rc::new(value), }} / name:field_name(s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {expr::FieldMember{ name, plus: false, params: Some(params), visibility, - value, + value: Rc::new(value), }} pub rule obj_local(s: &ParserSettings) -> BindSpec = keyword("local") _ bind:bind(s) {bind} pub rule member(s: &ParserSettings) -> expr::Member = bind:obj_local(s) {expr::Member::BindStmt(bind)} - / assertion:assertion(s) {expr::Member::AssertStmt(assertion)} + / assertion:assertion(s) {expr::Member::AssertStmt(Rc::new(assertion))} / field:field(s) {expr::Member::Field(field)} pub rule objinside(s: &ParserSettings) -> expr::ObjBody = 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})? { @@ -211,7 +215,7 @@ compspecs.extend(others.unwrap_or_default()); expr::ObjBody::ObjComp(expr::ObjComp{ pre_locals, - field, + field: Rc::new(field), post_locals, compspecs, }) @@ -224,18 +228,18 @@ pub rule compspec(s: &ParserSettings) -> Vec = s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s} pub rule local_expr(s: &ParserSettings) -> Expr - = keyword("local") _ binds:bind(s) ** comma() (_ ",")? _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) } + = keyword("local") _ binds:bind(s) ** comma() (_ ",")? _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, Box::new(expr)) } pub rule string_expr(s: &ParserSettings) -> Expr = s:string() {Expr::Str(s.into())} pub rule obj_expr(s: &ParserSettings) -> Expr = "{" _ body:objinside(s) _ "}" {Expr::Obj(body)} pub rule array_expr(s: &ParserSettings) -> Expr - = "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)} + = "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(Rc::new(elems))} pub rule array_comp_expr(s: &ParserSettings) -> Expr = "[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" { let mut specs = vec![CompSpec::ForSpec(forspec)]; specs.extend(others.unwrap_or_default()); - Expr::ArrComp(expr, specs) + Expr::ArrComp(Rc::new(expr), specs) } pub rule number_expr(s: &ParserSettings) -> Expr = n:number() {? if n.is_finite() { @@ -245,14 +249,14 @@ }} pub rule var_expr(s: &ParserSettings) -> Expr = n:id() { expr::Expr::Var(n) } - pub rule id_loc(s: &ParserSettings) -> LocExpr - = a:position!() n:id() b:position!() { LocExpr::new(expr::Expr::Str(n), Span(s.source.clone(), a as u32,b as u32)) } + pub rule id_loc(s: &ParserSettings) -> Spanned + = a:position!() n:id() b:position!() { Spanned::new(expr::Expr::Str(n), Span(s.source.clone(), a as u32,b as u32)) } pub rule if_then_else_expr(s: &ParserSettings) -> Expr - = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{ + = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse(Box::new(IfElse{ cond, cond_then, cond_else, - }} + }))} pub rule literal(s: &ParserSettings) -> Expr = v:( @@ -264,6 +268,11 @@ / keyword("super") {LiteralType::Super} ) {Expr::Literal(v)} + rule import_kind() -> ImportKind + = keyword("importstr") { ImportKind::Str } + / keyword("importbin") { ImportKind::Bin } + / keyword("import") { ImportKind::Normal } + pub rule expr_basic(s: &ParserSettings) -> Expr = literal(s) @@ -273,20 +282,20 @@ / array_expr(s) / array_comp_expr(s) - / keyword("importstr") _ path:expr(s) {Expr::ImportStr(path)} - / keyword("importbin") _ path:expr(s) {Expr::ImportBin(path)} - / keyword("import") _ path:expr(s) {Expr::Import(path)} + / kind:import_kind() _ path:expr(s) {Expr::Import(kind, Box::new(path))} / var_expr(s) / local_expr(s) / if_then_else_expr(s) - / keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)} - / assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) } + / keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, Rc::new(expr))} + / assert:assertion(s) _ ";" _ rest:expr(s) { Expr::AssertExpr(Rc::new(AssertExpr{ + assert, rest + })) } - / keyword("error") _ expr:expr(s) { Expr::ErrorStmt(expr) } + / keyword("error") _ expr:expr(s) { Expr::ErrorStmt(Box::new(expr)) } - rule slice_part(s: &ParserSettings) -> Option + rule slice_part(s: &ParserSettings) -> Option> = _ e:(e:expr(s) _{e})? {e} pub rule slice_desc(s: &ParserSettings) -> SliceDesc = start:slice_part(s) ":" pair:(end:slice_part(s) step:(":" e:slice_part(s){e})? {(end, step.flatten())})? { @@ -311,10 +320,10 @@ } use BinaryOpType::*; use UnaryOpType::*; - rule expr(s: &ParserSettings) -> LocExpr + rule expr(s: &ParserSettings) -> Spanned = precedence! { "(" _ e:expr(s) _ ")" {e} - start:position!() v:@ end:position!() { LocExpr::new(v, Span(s.source.clone(), start as u32, end as u32)) } + start:position!() v:@ end:position!() { Spanned::new(v, Span(s.source.clone(), start as u32, end as u32)) } -- a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)} a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ { @@ -354,10 +363,10 @@ unaryop(<"!">) _ b:@ {expr_un!(Not b)} unaryop(<"~">) _ b:@ {expr_un!(BitNot b)} -- - a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)} - indexable:(@) _ parts:index_part(s)+ {Expr::Index{indexable, parts}} - a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())} - a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)} + value:(@) _ "[" _ slice:slice_desc(s) _ "]" {Expr::Slice(Box::new(Slice{value, slice}))} + indexable:(@) _ parts:index_part(s)+ {Expr::Index{indexable: Box::new(indexable), parts}} + a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(Box::new(a), args, ts.is_some())} + a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(Rc::new(a), body)} -- e:expr_basic(s) {e} } @@ -373,71 +382,51 @@ null_coaelse: n.is_some(), }} - pub rule jsonnet(s: &ParserSettings) -> LocExpr = _ e:expr(s) _ {e} + pub rule jsonnet(s: &ParserSettings) -> Spanned = _ e:expr(s) _ {e} } } pub type ParseError = peg::error::ParseError; -pub fn parse(str: &str, settings: &ParserSettings) -> Result { +pub fn parse(str: &str, settings: &ParserSettings) -> Result, ParseError> { jsonnet_parser::jsonnet(str, settings) } /// Used for importstr values -pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> LocExpr { +pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> Spanned { let len = str.len(); - LocExpr::new(Expr::Str(str), Span(settings.source.clone(), 0, len as u32)) + Spanned::new(Expr::Str(str), Span(settings.source.clone(), 0, len as u32)) } #[cfg(test)] pub mod tests { + use insta::assert_snapshot; use jrsonnet_interner::IStr; - use BinaryOpType::*; - use super::{expr::*, parse}; + use super::parse; use crate::{source::Source, ParserSettings}; + fn parsep(s: &str) -> String { + let v = parse( + s, + &ParserSettings { + source: Source::new_virtual("".into(), IStr::empty()), + }, + ) + .unwrap(); + format!("{v:#?}") + } + macro_rules! parse { ($s:expr) => { - parse( - $s, - &ParserSettings { - source: Source::new_virtual("".into(), IStr::empty()), - }, - ) - .unwrap() - }; - } - - macro_rules! el { - ($expr:expr, $from:expr, $to:expr$(,)?) => { - LocExpr::new( - $expr, - Span( - Source::new_virtual("".into(), IStr::empty()), - $from, - $to, - ), - ) + assert_snapshot!(parsep($s)); }; } #[test] fn multiline_string() { - assert_eq!( - parse!("|||\n Hello world!\n a\n|||"), - el!(Expr::Str("Hello world!\n a\n".into()), 0, 31), - ); - assert_eq!( - parse!("|||\n Hello world!\n a\n|||"), - el!(Expr::Str("Hello world!\n a\n".into()), 0, 27), - ); - assert_eq!( - parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"), - el!(Expr::Str("Hello world!\n\ta\n".into()), 0, 27), - ); - assert_eq!( - parse!("|||\n Hello world!\n a\n |||"), - el!(Expr::Str("Hello world!\n a\n".into()), 0, 30), - ); + parse!("|||\n Hello world!\n a\n|||"); + parse!("|||\n Hello world!\n a\n|||"); + parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"); + parse!("|||\n Hello world!\n a\n |||"); } #[test] @@ -451,217 +440,58 @@ #[test] fn string_escaping() { - assert_eq!( - parse!(r#""Hello, \"world\"!""#), - el!(Expr::Str(r#"Hello, "world"!"#.into()), 0, 19), - ); - assert_eq!( - parse!(r#"'Hello \'world\'!'"#), - el!(Expr::Str("Hello 'world'!".into()), 0, 18), - ); - assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into()), 0, 6)); + parse!(r#""Hello, \"world\"!""#); + parse!(r#"'Hello \'world\'!'"#); + parse!(r#"'\\\\'"#); } #[test] fn string_unescaping() { - assert_eq!( - parse!(r#""Hello\nWorld""#), - el!(Expr::Str("Hello\nWorld".into()), 0, 14), - ); + parse!(r#""Hello\nWorld""#); } #[test] fn string_verbantim() { - assert_eq!( - parse!(r#"@"Hello\n""World""""#), - el!(Expr::Str("Hello\\n\"World\"".into()), 0, 19), - ); + parse!(r#"@"Hello\n""World""""#); } #[test] fn imports() { - assert_eq!( - parse!("import \"hello\""), - el!(Expr::Import(el!(Expr::Str("hello".into()), 7, 14)), 0, 14), - ); - assert_eq!( - parse!("importstr \"garnish.txt\""), - el!( - Expr::ImportStr(el!(Expr::Str("garnish.txt".into()), 10, 23)), - 0, - 23 - ) - ); - assert_eq!( - parse!("importbin \"garnish.bin\""), - el!( - Expr::ImportBin(el!(Expr::Str("garnish.bin".into()), 10, 23)), - 0, - 23 - ) - ); + parse!("import \"hello\""); + parse!("importstr \"garnish.txt\""); + parse!("importbin \"garnish.bin\""); } #[test] fn empty_object() { - assert_eq!( - parse!("{}"), - el!(Expr::Obj(ObjBody::MemberList(vec![])), 0, 2) - ); + parse!("{}"); } #[test] fn basic_math() { - assert_eq!( - parse!("2+2*2"), - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 0, 1), - Add, - el!( - Expr::BinaryOp(el!(Expr::Num(2.0), 2, 3), Mul, el!(Expr::Num(2.0), 4, 5)), - 2, - 5 - ) - ), - 0, - 5 - ) - ); - } - - #[test] - fn basic_math_with_indents() { - assert_eq!( - parse!("2 + 2 * 2 "), - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 0, 1), - Add, - el!( - Expr::BinaryOp(el!(Expr::Num(2.0), 7, 8), Mul, el!(Expr::Num(2.0), 13, 14),), - 7, - 14 - ), - ), - 0, - 14 - ) - ); + parse!("2+2*2"); + parse!("2 + 2 * 2 "); + parse!("2+(2+2*2)"); + parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"); } #[test] - fn basic_math_parened() { - assert_eq!( - parse!("2+(2+2*2)"), - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 0, 1), - Add, - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 3, 4), - Add, - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 5, 6), - Mul, - el!(Expr::Num(2.0), 7, 8), - ), - 5, - 8 - ), - ), - 3, - 8 - ), - ), - 0, - 9 - ) - ); - } - - /// Comments should not affect parsing - #[test] - fn comments() { - assert_eq!( - parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"), - el!( - Expr::BinaryOp( - el!(Expr::Num(2.0), 0, 1), - Add, - el!( - Expr::BinaryOp( - el!(Expr::Num(3.0), 22, 23), - Mul, - el!(Expr::Num(4.0), 40, 41) - ), - 22, - 41 - ) - ), - 0, - 41 - ) - ); - } - - #[test] fn suffix() { - // assert_eq!(parse!("std.test"), el!(Expr::Num(2.2))); - // assert_eq!(parse!("std(2)"), el!(Expr::Num(2.2))); - // assert_eq!(parse!("std.test(2)"), el!(Expr::Num(2.2))); - // assert_eq!(parse!("a[b]"), el!(Expr::Num(2.2))) + parse!("std.test"); + parse!("std(2)"); + parse!("std.test(2)"); + parse!("a[b]"); } #[test] fn array_comp() { - use Expr::*; - /* - `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`, - `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` - */ - assert_eq!( - parse!("[std.deepJoin(x) for x in arr]"), - el!( - ArrComp( - el!( - Apply( - el!( - Index { - indexable: el!(Var("std".into()), 1, 4), - parts: vec![IndexPart { - value: el!(Str("deepJoin".into()), 5, 13), - #[cfg(feature = "exp-null-coaelse")] - null_coaelse: false, - }], - }, - 1, - 13 - ), - ArgsDesc::new(vec![el!(Var("x".into()), 14, 15)], vec![]), - false, - ), - 1, - 16 - ), - vec![CompSpec::ForSpec(ForSpecData( - Destruct::Full("x".into()), - el!(Var("arr".into()), 26, 29) - ))] - ), - 0, - 30 - ), - ) + parse!("[std.deepJoin(x) for x in arr]"); } #[test] fn reserved() { - use Expr::*; - assert_eq!(parse!("null"), el!(Literal(LiteralType::Null), 0, 4)); - assert_eq!(parse!("nulla"), el!(Var("nulla".into()), 0, 5)); + parse!("null"); + parse!("nulla"); } #[test] @@ -671,58 +501,18 @@ #[test] fn infix_precedence() { - use Expr::*; - assert_eq!( - parse!("!a && !b"), - el!( - BinaryOp( - el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2), - And, - el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 7, 8)), 6, 8) - ), - 0, - 8 - ) - ); - } - - #[test] - fn infix_precedence_division() { - use Expr::*; - assert_eq!( - parse!("!a / !b"), - el!( - BinaryOp( - el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2), - Div, - el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 6, 7)), 5, 7) - ), - 0, - 7 - ) - ); + parse!("!a && !b"); + parse!("!a / !b"); } #[test] fn double_negation() { - use Expr::*; - assert_eq!( - parse!("!!a"), - el!( - UnaryOp( - UnaryOpType::Not, - el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 2, 3)), 1, 3) - ), - 0, - 3 - ) - ) + parse!("!!a"); } #[test] fn array_test_error() { parse!("[a for a in b if c for e in f]"); - // ^^^^ failed code } #[test] @@ -741,44 +531,6 @@ #[test] fn add_location_info_to_all_sub_expressions() { - use Expr::*; - - let file_name = Source::new_virtual("".into(), IStr::empty()); - let expr = parse( - "{} { local x = 1, x: x } + {}", - &ParserSettings { source: file_name }, - ) - .unwrap(); - assert_eq!( - expr, - el!( - BinaryOp( - el!( - ObjExtend( - el!(Obj(ObjBody::MemberList(vec![])), 0, 2), - ObjBody::MemberList(vec![ - Member::BindStmt(BindSpec::Field { - into: Destruct::Full("x".into()), - value: el!(Num(1.0), 15, 16) - }), - Member::Field(FieldMember { - name: FieldName::Fixed("x".into()), - plus: false, - params: None, - visibility: Visibility::Normal, - value: el!(Var("x".into()), 21, 22), - }) - ]) - ), - 0, - 24 - ), - BinaryOpType::Add, - el!(Obj(ObjBody::MemberList(vec![])), 27, 29), - ), - 0, - 29 - ), - ); + parse!("{} { local x = 1, x: x } + {}"); } } --- /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::0-2, + MemberList( + [ + BindStmt( + Field { + into: Full( + "x", + ), + value: Num( + 1.0, + ) from virtual::15-16, + }, + ), + Field( + FieldMember { + name: Fixed( + "x", + ), + plus: false, + params: None, + visibility: Normal, + value: Var( + "x", + ) from virtual::21-22, + }, + ), + ], + ), + ) from virtual::0-24, + op: Add, + rhs: Obj( + MemberList( + [], + ), + ) from virtual::27-29, + }, +) from virtual::0-29 --- /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::1-4, + parts: [ + IndexPart { + value: Str( + "deepJoin", + ) from virtual::5-13, + }, + ], + } from virtual::1-13, + ArgsDesc { + unnamed: [ + Var( + "x", + ) from virtual::14-15, + ], + named: [], + }, + false, + ) from virtual::1-16, + [ + ForSpec( + ForSpecData( + Full( + "x", + ), + Var( + "arr", + ) from virtual::26-29, + ), + ), + ], +) from virtual::0-30 --- /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::1-2, + [ + ForSpec( + ForSpecData( + Full( + "a", + ), + Var( + "b", + ) from virtual::12-13, + ), + ), + IfSpec( + IfSpecData( + Var( + "c", + ) from virtual::17-18, + ), + ), + ForSpec( + ForSpecData( + Full( + "e", + ), + Var( + "f", + ) from virtual::28-29, + ), + ), + ], +) from virtual::0-30 --- /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::0-1, + op: Add, + rhs: BinaryOp( + BinaryOp { + lhs: Num( + 2.0, + ) from virtual::7-8, + op: Mul, + rhs: Num( + 2.0, + ) from virtual::13-14, + }, + ) from virtual::7-14, + }, +) from virtual::0-14 --- /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::0-1, + op: Add, + rhs: BinaryOp( + BinaryOp { + lhs: Num( + 2.0, + ) from virtual::3-4, + op: Add, + rhs: BinaryOp( + BinaryOp { + lhs: Num( + 2.0, + ) from virtual::5-6, + op: Mul, + rhs: Num( + 2.0, + ) from virtual::7-8, + }, + ) from virtual::5-8, + }, + ) from virtual::3-8, + }, +) from virtual::0-9 --- /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::0-1, + op: Add, + rhs: BinaryOp( + BinaryOp { + lhs: Num( + 3.0, + ) from virtual::22-23, + op: Mul, + rhs: Num( + 4.0, + ) from virtual::40-41, + }, + ) from virtual::22-41, + }, +) from virtual::0-41 --- /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::0-1, + op: Add, + rhs: BinaryOp( + BinaryOp { + lhs: Num( + 2.0, + ) from virtual::2-3, + op: Mul, + rhs: Num( + 2.0, + ) from virtual::4-5, + }, + ) from virtual::2-5, + }, +) from virtual::0-5 --- /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::14-19, + ), + ), + Param( + Full( + "bar", + ), + None, + ), + ], + ), + value: Literal( + Null, + ) from virtual::28-32, + }, + ], + Literal( + Null, + ) from virtual::34-38, +) from virtual::0-38 --- /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::2-3, + ) from virtual::1-3, +) from virtual::0-3 --- /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::0-2 --- /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::10-23, +) from virtual::0-23 --- /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::10-23, +) from virtual::0-23 --- /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::7-14, +) from virtual::0-14 --- /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::1-2, + ) from virtual::0-2, + op: Div, + rhs: UnaryOp( + Not, + Var( + "b", + ) from virtual::6-7, + ) from virtual::5-7, + }, +) from virtual::0-7 --- /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::1-2, + ) from virtual::0-2, + op: And, + rhs: UnaryOp( + Not, + Var( + "b", + ) from virtual::7-8, + ) from virtual::6-8, + }, +) from virtual::0-8 --- /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::3-4, + }, + ), + ], + ), +) from virtual::0-5 --- /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::0-27 --- /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::0-27 --- /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::0-30 --- /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::0-31 --- /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::0-1, + ArgsDesc { + unnamed: [ + Var( + "b", + ) from virtual::2-3, + Var( + "null_fields", + ) from virtual::5-16, + ], + named: [], + }, + false, +) from virtual::0-17 --- /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::0-5 --- /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::0-4 --- /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::0-1, + slice: SliceDesc { + start: Some( + Num( + 1.0, + ) from virtual::2-3, + ), + end: None, + step: None, + }, + }, +) from virtual::0-6 --- /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::0-1, + slice: SliceDesc { + start: None, + end: Some( + Num( + 1.0, + ) from virtual::3-4, + ), + step: None, + }, + }, +) from virtual::0-6 --- /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::0-1, + slice: SliceDesc { + start: None, + end: None, + step: Some( + Num( + 1.0, + ) from virtual::4-5, + ), + }, + }, +) from virtual::0-6 --- /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::0-3, + slice: SliceDesc { + start: None, + end: Some( + BinaryOp( + BinaryOp { + lhs: Var( + "len", + ) from virtual::5-8, + op: Sub, + rhs: Num( + 1.0, + ) from virtual::11-12, + }, + ) from virtual::5-12, + ), + step: None, + }, + }, +) from virtual::0-13 --- /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::0-1, + slice: SliceDesc { + start: Some( + Num( + 1.0, + ) from virtual::2-3, + ), + end: None, + step: None, + }, + }, +) from virtual::0-5 --- /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::0-18 --- /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::0-6 --- /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::0-19 --- /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::0-14 --- /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::0-19 --- /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::0-3, + ArgsDesc { + unnamed: [ + Num( + 2.0, + ) from virtual::4-5, + ], + named: [], + }, + false, +) from virtual::0-6 --- /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::0-3, + parts: [ + IndexPart { + value: Str( + "test", + ) from virtual::4-8, + }, + ], + } from virtual::0-8, + ArgsDesc { + unnamed: [ + Num( + 2.0, + ) from virtual::9-10, + ], + named: [], + }, + false, +) from virtual::0-11 --- /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::0-1, + parts: [ + IndexPart { + value: Var( + "b", + ) from virtual::2-3, + }, + ], +} from virtual::0-4 --- /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::0-3, + parts: [ + IndexPart { + value: Str( + "test", + ) from virtual::4-8, + }, + ], +} from virtual::0-8 --- 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); 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) + } +} -- gitstuff