git.delta.rocks / jrsonnet / refs/commits / 7eb771ff363d

difftreelog

feat allow both parsers at the same time

uwkkuzmuYaroslav Bolyukin2026-03-23parent: #b6f9e83.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
before · crates/jrsonnet-evaluator/Cargo.toml
1[package]2name = "jrsonnet-evaluator"3description = "jsonnet interpreter"4authors.workspace = true5edition.workspace = true6license.workspace = true7repository.workspace = true8version.workspace = true910build = "build.rs"1112[lints]13workspace = true1415[features]16default = ["explaining-traces", "ir-parser"]17# Rustc-like trace visualization18explaining-traces = ["annotate-snippets", "hi-doc"]19# Allows library authors to throw custom errors20anyhow-error = ["anyhow"]21# Use hand-written recursive descent parser instead of PEG parser22ir-parser = ["dep:jrsonnet-ir-parser"]2324# Allows to preserve field order in objects25exp-preserve-order = []26# Implements field destructuring27exp-destruct = ["jrsonnet-peg-parser/exp-destruct"]28# Iteration over objects yields [key, value] elements29exp-object-iteration = []30# Bigint type31exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]32# obj?.field, obj?.['field']33exp-null-coaelse = ["jrsonnet-peg-parser/exp-null-coaelse", "jrsonnet-ir-parser?/exp-null-coaelse"]3435[dependencies]36jrsonnet-interner.workspace = true37jrsonnet-ir.workspace = true38jrsonnet-peg-parser.workspace = true39jrsonnet-ir-parser = { workspace = true, optional = true }40jrsonnet-types.workspace = true41jrsonnet-macros.workspace = true42jrsonnet-gcmodule.workspace = true4344pathdiff.workspace = true45static_assertions.workspace = true4647rustc-hash.workspace = true4849thiserror.workspace = true50# Friendly errors51strsim.workspace = true5253serde.workspace = true5455anyhow = { workspace = true, optional = true }56# Explaining traces57annotate-snippets = { workspace = true, optional = true }58# Better explaining traces59hi-doc = { workspace = true, optional = true }60# Bigint61num-bigint = { workspace = true, features = ["serde"], optional = true }6263stacker = "0.1.23"64educe = { version = "0.6.0", default-features = false, features = ["Clone", "Debug", "Eq", "Hash", "PartialEq"] }6566[build-dependencies]67rustversion = "1.0.22"
modifiedcrates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/async_import.rs
+++ b/crates/jrsonnet-evaluator/src/async_import.rs
@@ -7,10 +7,6 @@
 	FieldMember, FieldName, ForSpecData, IfElse, IfSpecData, ImportKind, ObjBody, Slice, SliceDesc,
 	Source, SourcePath, Spanned,
 };
-#[cfg(feature = "ir-parser")]
-use jrsonnet_ir_parser::ParserSettings;
-#[cfg(not(feature = "ir-parser"))]
-use jrsonnet_peg_parser::ParserSettings;
 use rustc_hash::FxHashMap;
 
 use crate::{AsPathLike, FileData, ImportResolver, ResolvePathOwned, State};
@@ -326,7 +322,7 @@
 						};
 						let source = Source::new(path.clone(), code.clone());
 						// If failed - then skip import
-						file.parsed = crate::parse_jsonnet(&code, &ParserSettings { source })
+						file.parsed = crate::parse_jsonnet(&code, source)
 							.map(Rc::new)
 							.ok();
 						if let Some(parsed) = &file.parsed {
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -14,6 +14,22 @@
 	ObjValue, ResolvePathOwned,
 };
 
+#[derive(Debug, Clone)]
+pub struct SyntaxErrorLocation {
+	pub offset: usize,
+}
+
+#[derive(Debug, Clone)]
+pub struct SyntaxError {
+	pub message: String,
+	pub location: SyntaxErrorLocation,
+}
+impl fmt::Display for SyntaxError {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		write!(f, "{}", self.message)
+	}
+}
+
 pub(crate) fn format_found(list: &[IStr], what: &str) -> String {
 	if list.is_empty() {
 		return String::new();
@@ -154,31 +170,11 @@
 	ImportNotSupported(SourcePath, ResolvePathOwned),
 	#[error("can't import from virtual file")]
 	CantImportFromVirtualFile,
-	#[cfg(not(feature = "ir-parser"))]
-	#[error(
-		"syntax error: {}",
-		// Peg has no fancier way to handle critical parsing errors https://github.com/kevinmehall/rust-peg/issues/225
-		{.error.expected.tokens().find(|t| t.starts_with("!!!")).map_or_else(|| {
-			format!(
-				"expected {}, got {:?}",
-				.error.expected,
-				.path.code().chars().nth(error.location.offset)
-				.map_or_else(|| "EOF".into(), |c| c.to_string())
-			)
-		}, |v| v[3..].into())}
-	)]
-	ImportSyntaxError {
-		path: Source,
-		#[trace(skip)]
-		error: Box<jrsonnet_peg_parser::ParseError>,
-	},
-
-	#[cfg(feature = "ir-parser")]
 	#[error("syntax error: {error}")]
 	ImportSyntaxError {
 		path: Source,
 		#[trace(skip)]
-		error: Box<jrsonnet_ir_parser::ParseError>,
+		error: Box<SyntaxError>,
 	},
 
 	#[error("runtime error: {}", format_empty_str(.0))]
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -46,10 +46,11 @@
 use jrsonnet_ir::{Expr, Source, SourcePath};
 #[doc(hidden)]
 pub use jrsonnet_macros;
-#[cfg(feature = "ir-parser")]
-use jrsonnet_ir_parser::ParserSettings;
-#[cfg(not(feature = "ir-parser"))]
-use jrsonnet_peg_parser::ParserSettings;
+
+#[cfg(not(any(feature = "ir-parser", feature = "peg-parser")))]
+compile_error!("at least one of `ir-parser` or `peg-parser` features must be enabled");
+
+pub use error::{SyntaxError, SyntaxErrorLocation};
 pub use obj::*;
 pub use rustc_hash;
 use rustc_hash::FxHashMap;
@@ -59,20 +60,64 @@
 
 use crate::gc::WithCapacityExt as _;
 
+pub(crate) fn parse_jsonnet(code: &str, source: Source) -> Result<Expr, SyntaxError> {
+	#[cfg(all(feature = "ir-parser", feature = "peg-parser"))]
+	{
+		if std::env::var_os("JRSONNET_LEGACY_PARSER").is_some() {
+			return parse_peg(code, source);
+		}
+		return parse_ir(code, source);
+	}
+	#[cfg(all(feature = "ir-parser", not(feature = "peg-parser")))]
+	{
+		return parse_ir(code, source);
+	}
+	#[cfg(all(feature = "peg-parser", not(feature = "ir-parser")))]
+	{
+		return parse_peg(code, source);
+	}
+}
+
 #[cfg(feature = "ir-parser")]
-pub(crate) fn parse_jsonnet(
-	code: &str,
-	settings: &ParserSettings,
-) -> Result<Expr, jrsonnet_ir_parser::ParseError> {
-	jrsonnet_ir_parser::parse(code, settings)
+fn parse_ir(code: &str, source: Source) -> Result<Expr, SyntaxError> {
+	jrsonnet_ir_parser::parse(code, &jrsonnet_ir_parser::ParserSettings { source }).map_err(
+		|e| SyntaxError {
+			message: e.message,
+			location: SyntaxErrorLocation {
+				offset: e.location.offset,
+			},
+		},
+	)
 }
 
-#[cfg(not(feature = "ir-parser"))]
-pub(crate) fn parse_jsonnet(
-	code: &str,
-	settings: &ParserSettings,
-) -> Result<Expr, jrsonnet_peg_parser::ParseError> {
-	jrsonnet_peg_parser::parse(code, settings)
+#[cfg(feature = "peg-parser")]
+fn parse_peg(code: &str, source: Source) -> Result<Expr, SyntaxError> {
+	jrsonnet_peg_parser::parse(code, &jrsonnet_peg_parser::ParserSettings { source }).map_err(
+		|e| {
+			let message = e
+				.expected
+				.tokens()
+				.find(|t| t.starts_with("!!!"))
+				.map_or_else(
+					|| {
+						format!(
+							"expected {}, got {:?}",
+							e.expected,
+							code.chars()
+								.nth(e.location.offset)
+								.map_or_else(|| "EOF".into(), |c: char| c.to_string())
+						)
+					},
+					|v| v[3..].into(),
+				);
+			SyntaxError {
+				message,
+				location: SyntaxErrorLocation {
+					offset: e.location.offset,
+				},
+			}
+		},
+	)
 }
 
 cc_dyn!(
@@ -364,12 +409,7 @@
 		let file_name = Source::new(path.clone(), code.clone());
 		if file.parsed.is_none() {
 			file.parsed = Some(
-				parse_jsonnet(
-					&code,
-					&ParserSettings {
-						source: file_name.clone(),
-					},
-				)
+				parse_jsonnet(&code, file_name.clone())
 				.map(Rc::new)
 				.map_err(|e| ImportSyntaxError {
 					path: file_name.clone(),
@@ -480,12 +520,7 @@
 	pub fn evaluate_snippet(&self, name: impl Into<IStr>, code: impl Into<IStr>) -> Result<Val> {
 		let code = code.into();
 		let source = Source::new_virtual(name.into(), code.clone());
-		let parsed = parse_jsonnet(
-			&code,
-			&ParserSettings {
-				source: source.clone(),
-			},
-		)
+		let parsed = parse_jsonnet(&code, source.clone())
 		.map_err(|e| ImportSyntaxError {
 			path: source.clone(),
 			error: Box::new(e),
@@ -501,12 +536,7 @@
 	) -> Result<Val> {
 		let code = code.into();
 		let source = Source::new_virtual(name.into(), code.clone());
-		let parsed = parse_jsonnet(
-			&code,
-			&ParserSettings {
-				source: source.clone(),
-			},
-		)
+		let parsed = parse_jsonnet(&code, source.clone())
 		.map_err(|e| ImportSyntaxError {
 			path: source.clone(),
 			error: Box::new(e),