git.delta.rocks / jrsonnet / refs/commits / ecde57a8973c

difftreelog

feat parse new object iteration syntax

tvmprlwuYaroslav Bolyukin2026-05-06parent: #0f5424f.patch.diff
in: master

10 files changed

modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
31]31]
32# Destructuring of locals32# Destructuring of locals
33exp-destruct = ["jrsonnet-evaluator/exp-destruct"]33exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
34# Iteration over objects yields [key, value] elements34# Iteration over objects using [key]: value syntax
35exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]35exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]
36# Bigint type36# Bigint type
37exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]37exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -40,8 +40,12 @@
   "jrsonnet-peg-parser?/exp-destruct",
   "jrsonnet-ir-parser?/exp-destruct",
 ]
-# Iteration over objects yields [key, value] elements
-exp-object-iteration = []
+# Iteration over objects using [key]: value syntax
+exp-object-iteration = [
+  "jrsonnet-ir/exp-object-iteration",
+  "jrsonnet-peg-parser?/exp-object-iteration",
+  "jrsonnet-ir-parser?/exp-object-iteration",
+]
 # Bigint type
 exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]
 # obj?.field, obj?.['field']
modifiedcrates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/analyze.rs
+++ b/crates/jrsonnet-evaluator/src/analyze.rs
@@ -1862,6 +1862,8 @@
 				);
 				(r, rest)
 			}
+			#[cfg(feature = "exp-object-iteration")]
+			CompSpec::ForObjSpec(_) => todo!(),
 		}
 	}
 	let outer_depth = stack.depth;
modifiedcrates/jrsonnet-ir-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/Cargo.toml
+++ b/crates/jrsonnet-ir-parser/Cargo.toml
@@ -11,9 +11,10 @@
 
 [features]
 default = []
-experimental = ["exp-null-coaelse", "exp-destruct"]
+experimental = ["exp-null-coaelse", "exp-destruct", "exp-object-iteration"]
 exp-null-coaelse = ["jrsonnet-ir/exp-null-coaelse"]
 exp-destruct = ["jrsonnet-ir/exp-destruct"]
+exp-object-iteration = ["jrsonnet-ir/exp-object-iteration"]
 
 [dependencies]
 insta.workspace = true
modifiedcrates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -66,6 +66,13 @@
 		!self.at_eof() && self.peek() == kind
 	}
 
+	#[allow(dead_code)]
+	fn nth(&self, n: usize) -> SyntaxKind {
+		self.lexemes
+			.get(self.offset + n)
+			.map_or(SyntaxKind::EOF, |l| l.kind)
+	}
+
 	fn eat_any(&mut self) {
 		self.offset += 1;
 	}
@@ -561,20 +568,36 @@
 	}
 }
 
-fn for_spec(p: &mut Parser<'_>) -> Result<ForSpecData> {
+fn for_spec(p: &mut Parser<'_>) -> Result<CompSpec> {
 	p.eat(T![for])?;
+	#[cfg(feature = "exp-object-iteration")]
+	if p.at(T!['[']) && p.nth(1) == SyntaxKind::IDENT && p.nth(2) == T![']'] && p.nth(3) == T![:] {
+		p.eat(T!['['])?;
+		let key = ident(p)?;
+		p.eat(T![']'])?;
+		let visibility = visibility(p)?;
+		let value = destruct(p)?;
+		p.eat(T![in])?;
+		let over = expr(p)?;
+		return Ok(CompSpec::ForObjSpec(jrsonnet_ir::ForObjSpecData {
+			key,
+			visibility,
+			value,
+			over,
+		}));
+	}
 	let d = destruct(p)?;
 	p.eat(T![in])?;
 	let over = expr(p)?;
-	Ok(ForSpecData { destruct: d, over })
+	Ok(CompSpec::ForSpec(ForSpecData { destruct: d, over }))
 }
 
 fn compspecs(p: &mut Parser<'_>) -> Result<Vec<CompSpec>> {
 	let mut specs = Vec::new();
-	specs.push(CompSpec::ForSpec(for_spec(p)?));
+	specs.push(for_spec(p)?);
 	loop {
 		if p.at(T![for]) {
-			specs.push(CompSpec::ForSpec(for_spec(p)?));
+			specs.push(for_spec(p)?);
 		} else if p.at(T![if]) {
 			let isd = if_spec_data(p)?;
 			specs.push(CompSpec::IfSpec(isd));
modifiedcrates/jrsonnet-ir/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-ir/Cargo.toml
+++ b/crates/jrsonnet-ir/Cargo.toml
@@ -12,9 +12,10 @@
 
 [features]
 default = []
-experimental = ["exp-destruct", "exp-null-coaelse"]
+experimental = ["exp-destruct", "exp-null-coaelse", "exp-object-iteration"]
 exp-destruct = []
 exp-null-coaelse = []
+exp-object-iteration = []
 
 [dependencies]
 jrsonnet-interner.workspace = true
modifiedcrates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir/src/expr.rs
+++ b/crates/jrsonnet-ir/src/expr.rs
@@ -314,10 +314,21 @@
 	pub over: Expr,
 }
 
+#[cfg(feature = "exp-object-iteration")]
 #[derive(Debug, PartialEq, Acyclic)]
+pub struct ForObjSpecData {
+	pub key: IStr,
+	pub visibility: Visibility,
+	pub value: Destruct,
+	pub over: Expr,
+}
+
+#[derive(Debug, PartialEq, Acyclic)]
 pub enum CompSpec {
 	IfSpec(IfSpecData),
 	ForSpec(ForSpecData),
+	#[cfg(feature = "exp-object-iteration")]
+	ForObjSpec(ForObjSpecData),
 }
 
 #[derive(Debug, PartialEq, Acyclic)]
modifiedcrates/jrsonnet-ir/src/visit.rsdiffbeforeafterboth
--- a/crates/jrsonnet-ir/src/visit.rs
+++ b/crates/jrsonnet-ir/src/visit.rs
@@ -1,5 +1,7 @@
 use jrsonnet_interner::IStr;
 
+#[cfg(feature = "exp-object-iteration")]
+use crate::ForObjSpecData;
 use crate::{
 	ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, Expr, ExprParam,
 	ExprParams, FieldMember, FieldName, ForSpecData, IfElse, IfSpecData, ImportKind, IndexPart,
@@ -69,6 +71,17 @@
 			visit_destruct(v, destruct);
 			v.visit_expr(over);
 		}
+		#[cfg(feature = "exp-object-iteration")]
+		CompSpec::ForObjSpec(for_obj_spec_data) => {
+			let ForObjSpecData {
+				key: _,
+				visibility: _,
+				value,
+				over,
+			} = for_obj_spec_data;
+			visit_destruct(v, value);
+			v.visit_expr(over);
+		}
 	}
 }
 pub fn visit_params<V: Visitor>(v: &mut V, par: &ExprParams) {
modifiedcrates/jrsonnet-peg-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/Cargo.toml
+++ b/crates/jrsonnet-peg-parser/Cargo.toml
@@ -22,6 +22,7 @@
 
 [features]
 default = []
-experimental = ["exp-destruct", "exp-null-coaelse"]
+experimental = ["exp-destruct", "exp-null-coaelse", "exp-object-iteration"]
 exp-destruct = ["jrsonnet-ir/exp-destruct"]
 exp-null-coaelse = ["jrsonnet-ir/exp-null-coaelse"]
+exp-object-iteration = ["jrsonnet-ir/exp-object-iteration"]
modifiedcrates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -241,11 +241,21 @@
 			= i:spanned(<keyword("if")>, s) _ cond:expr(s) {IfSpecData { span: i.span, cond }}
 		pub rule forspec(s: &ParserSettings) -> ForSpecData
 			= keyword("for") _ destruct:destruct(s) _ keyword("in") _ over:expr(s) { ForSpecData { destruct, over } }
+		rule ensure_object_iteration()
+			= "" {?
+				#[cfg(not(feature = "exp-object-iteration"))] return Err("!!!experimental object iteration was not enabled");
+				#[cfg(feature = "exp-object-iteration")] Ok(())
+			}
+		pub rule forobjspec(s: &ParserSettings) -> CompSpec
+			= ensure_object_iteration() keyword("for") _ "[" _ key:id() _ "]" _ vis:visibility() _ value:destruct(s) _ keyword("in") _ over:expr(s) {
+				#[cfg(feature = "exp-object-iteration")] return CompSpec::ForObjSpec(jrsonnet_ir::ForObjSpecData { key, visibility: vis, value, over });
+				#[cfg(not(feature = "exp-object-iteration"))] unreachable!("ensure_object_iteration will fail if feature is not enabled")
+			}
 		rule compspec(s: &ParserSettings) -> CompSpec
-			= i:ifspec(s) { CompSpec::IfSpec(i) } / f:forspec(s) {CompSpec::ForSpec(f)}
+			= i:ifspec(s) { CompSpec::IfSpec(i) } / f:forobjspec(s) { f } / f:forspec(s) {CompSpec::ForSpec(f)}
 		pub rule compspecs(s: &ParserSettings) -> Vec<CompSpec>
 			= specs:compspec(s) ++ _ {?
-				if !matches!(specs[0], CompSpec::ForSpec(_)) {
+				if matches!(specs[0], CompSpec::IfSpec(_)) {
 					return Err("<first compspec should be for>")
 				}
 				Ok(specs)