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
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -18,24 +18,26 @@
 explaining-traces = ["annotate-snippets", "hi-doc"]
 # Allows library authors to throw custom errors
 anyhow-error = ["anyhow"]
-# Use hand-written recursive descent parser instead of PEG parser
+# Use hand-written recursive descent parser
 ir-parser = ["dep:jrsonnet-ir-parser"]
+# Use PEG parser
+peg-parser = ["dep:jrsonnet-peg-parser"]
 
 # Allows to preserve field order in objects
 exp-preserve-order = []
 # Implements field destructuring
-exp-destruct = ["jrsonnet-peg-parser/exp-destruct"]
+exp-destruct = ["jrsonnet-peg-parser?/exp-destruct", "jrsonnet-ir-parser?/exp-destruct"]
 # Iteration over objects yields [key, value] elements
 exp-object-iteration = []
 # Bigint type
 exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]
 # obj?.field, obj?.['field']
-exp-null-coaelse = ["jrsonnet-peg-parser/exp-null-coaelse", "jrsonnet-ir-parser?/exp-null-coaelse"]
+exp-null-coaelse = ["jrsonnet-peg-parser?/exp-null-coaelse", "jrsonnet-ir-parser?/exp-null-coaelse"]
 
 [dependencies]
 jrsonnet-interner.workspace = true
 jrsonnet-ir.workspace = true
-jrsonnet-peg-parser.workspace = true
+jrsonnet-peg-parser = { workspace = true, optional = true }
 jrsonnet-ir-parser = { workspace = true, optional = true }
 jrsonnet-types.workspace = true
 jrsonnet-macros.workspace = true
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
14 ObjValue, ResolvePathOwned,14 ObjValue, ResolvePathOwned,
15};15};
16
17#[derive(Debug, Clone)]
18pub struct SyntaxErrorLocation {
19 pub offset: usize,
20}
21
22#[derive(Debug, Clone)]
23pub struct SyntaxError {
24 pub message: String,
25 pub location: SyntaxErrorLocation,
26}
27impl fmt::Display for SyntaxError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "{}", self.message)
30 }
31}
1632
17pub(crate) fn format_found(list: &[IStr], what: &str) -> String {33pub(crate) fn format_found(list: &[IStr], what: &str) -> String {
18 if list.is_empty() {34 if list.is_empty() {
154 ImportNotSupported(SourcePath, ResolvePathOwned),170 ImportNotSupported(SourcePath, ResolvePathOwned),
155 #[error("can't import from virtual file")]171 #[error("can't import from virtual file")]
156 CantImportFromVirtualFile,172 CantImportFromVirtualFile,
157 #[cfg(not(feature = "ir-parser"))]
158 #[error(
159 "syntax error: {}",
160 // Peg has no fancier way to handle critical parsing errors https://github.com/kevinmehall/rust-peg/issues/225
161 {.error.expected.tokens().find(|t| t.starts_with("!!!")).map_or_else(|| {
162 format!(
163 "expected {}, got {:?}",
164 .error.expected,
165 .path.code().chars().nth(error.location.offset)
166 .map_or_else(|| "EOF".into(), |c| c.to_string())
167 )
168 }, |v| v[3..].into())}
169 )]
170 ImportSyntaxError {
171 path: Source,
172 #[trace(skip)]
173 error: Box<jrsonnet_peg_parser::ParseError>,
174 },
175
176 #[cfg(feature = "ir-parser")]
177 #[error("syntax error: {error}")]173 #[error("syntax error: {error}")]
178 ImportSyntaxError {174 ImportSyntaxError {
179 path: Source,175 path: Source,
180 #[trace(skip)]176 #[trace(skip)]
181 error: Box<jrsonnet_ir_parser::ParseError>,177 error: Box<SyntaxError>,
182 },178 },
183179
184 #[error("runtime error: {}", format_empty_str(.0))]180 #[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),