From 0f5424fc1b4b8de33d59d507b461a1cd57213781 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 06 May 2026 04:55:17 +0000 Subject: [PATCH] feat(rowan): alternative object comp syntax --- --- a/crates/jrsonnet-formatter/src/lib.rs +++ b/crates/jrsonnet-formatter/src/lib.rs @@ -13,9 +13,9 @@ AstNode, AstToken as _, SyntaxToken, nodes::{ Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart, - DestructRest, Expr, ExprArray, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, - Member, Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, - Text, TextKind, UnaryOperator, Visibility, + DestructRest, Expr, ExprArray, ExprBase, FieldName, ForObjSpec, ForSpec, IfSpec, + ImportKind, Literal, Member, Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, + SourceFile, Stmt, Suffix, Text, TextKind, UnaryOperator, Visibility, }, }; @@ -645,6 +645,11 @@ p!(out, str("for ") {self.bind()} str(" in ") {self.expr()}); } } +impl Printable for ForObjSpec { + fn print(&self, out: &mut PrintItems) { + p!(out, str("for [") {self.key()} str("]") {self.visibility()} str(" ") {self.value()} str(" in ") {self.expr()}); + } +} impl Printable for IfSpec { fn print(&self, out: &mut PrintItems) { p!(out, str("if ") {self.expr()}); @@ -654,6 +659,7 @@ fn print(&self, out: &mut PrintItems) { match self { Self::ForSpec(f) => f.print(out), + Self::ForObjSpec(f) => f.print(out), Self::IfSpec(i) => i.print(out), } } --- a/crates/jrsonnet-rowan-parser/jsonnet.ungram +++ b/crates/jrsonnet-rowan-parser/jsonnet.ungram @@ -246,11 +246,21 @@ bind:Destruct 'in' Expr +ForObjSpec = + 'for' + '[' + key:Name + ']' + Visibility + value:Destruct + 'in' + Expr IfSpec = 'if' Expr CompSpec = ForSpec +| ForObjSpec | IfSpec BindDestruct = --- a/crates/jrsonnet-rowan-parser/src/generated/nodes.rs +++ b/crates/jrsonnet-rowan-parser/src/generated/nodes.rs @@ -650,6 +650,37 @@ } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ForObjSpec { + pub(crate) syntax: SyntaxNode, +} +impl ForObjSpec { + pub fn for_kw_token(&self) -> Option { + support::token(&self.syntax, T![for]) + } + pub fn l_brack_token(&self) -> Option { + support::token(&self.syntax, T!['[']) + } + pub fn key(&self) -> Option { + support::children(&self.syntax).next() + } + pub fn r_brack_token(&self) -> Option { + support::token(&self.syntax, T![']']) + } + pub fn visibility(&self) -> Option { + support::children(&self.syntax).next() + } + pub fn value(&self) -> Option { + support::children(&self.syntax).next() + } + pub fn in_kw_token(&self) -> Option { + support::token(&self.syntax, T![in]) + } + pub fn expr(&self) -> Option { + support::children(&self.syntax).next() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IfSpec { pub(crate) syntax: SyntaxNode, } @@ -845,6 +876,7 @@ #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CompSpec { ForSpec(ForSpec), + ForObjSpec(ForObjSpec), IfSpec(IfSpec), } @@ -1702,6 +1734,21 @@ &self.syntax } } +impl AstNode for ForObjSpec { + fn can_cast(kind: SyntaxKind) -> bool { + kind == FOR_OBJ_SPEC + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for IfSpec { fn can_cast(kind: SyntaxKind) -> bool { kind == IF_SPEC @@ -2014,6 +2061,11 @@ CompSpec::ForSpec(node) } } +impl From for CompSpec { + fn from(node: ForObjSpec) -> CompSpec { + CompSpec::ForObjSpec(node) + } +} impl From for CompSpec { fn from(node: IfSpec) -> CompSpec { CompSpec::IfSpec(node) @@ -2022,13 +2074,14 @@ impl AstNode for CompSpec { fn can_cast(kind: SyntaxKind) -> bool { match kind { - FOR_SPEC | IF_SPEC => true, + FOR_SPEC | FOR_OBJ_SPEC | IF_SPEC => true, _ => false, } } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { FOR_SPEC => CompSpec::ForSpec(ForSpec { syntax }), + FOR_OBJ_SPEC => CompSpec::ForObjSpec(ForObjSpec { syntax }), IF_SPEC => CompSpec::IfSpec(IfSpec { syntax }), _ => return None, }; @@ -2037,6 +2090,7 @@ fn syntax(&self) -> &SyntaxNode { match self { CompSpec::ForSpec(it) => &it.syntax, + CompSpec::ForObjSpec(it) => &it.syntax, CompSpec::IfSpec(it) => &it.syntax, } } @@ -3016,6 +3070,11 @@ std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ForObjSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for IfSpec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) --- a/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs +++ b/crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs @@ -145,6 +145,7 @@ FIELD_NAME_FIXED, FIELD_NAME_DYNAMIC, FOR_SPEC, + FOR_OBJ_SPEC, IF_SPEC, BIND_DESTRUCT, BIND_FUNCTION, --- a/crates/jrsonnet-rowan-parser/src/parser.rs +++ b/crates/jrsonnet-rowan-parser/src/parser.rs @@ -364,6 +364,19 @@ fn compspec(p: &mut Parser) -> CompletedMarker { assert!(p.at_ts(COMPSPEC)); if p.at(T![for]) { + if p.nth_at(1, T!['[']) && p.nth_at(2, IDENT) && p.nth_at(3, T![']']) && p.nth_at(4, T![:]) + { + let m = p.start(); + p.bump_assert(T![for]); + p.bump_assert(T!['[']); + name(p); + p.expect(T![']']); + visibility(p); + destruct(p); + p.expect(T![in]); + expr(p); + return m.complete(p, FOR_OBJ_SPEC); + } let m = p.start(); p.bump(); destruct(p); --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_force_visible.snap @@ -0,0 +1,51 @@ +--- +source: crates/jrsonnet-rowan-parser/src/tests.rs +expression: "{ [k]: v for [k]::: v in obj }\n" +--- +SOURCE_FILE@0..31 + EXPR@0..30 + EXPR_OBJECT@0..30 + OBJ_BODY_COMP@0..30 + L_BRACE@0..1 "{" + WHITESPACE@1..2 " " + MEMBER_FIELD_NORMAL@2..8 + FIELD_NAME_DYNAMIC@2..5 + L_BRACK@2..3 "[" + EXPR@3..4 + EXPR_VAR@3..4 + NAME@3..4 + IDENT@3..4 "k" + R_BRACK@4..5 "]" + VISIBILITY@5..6 + COLON@5..6 ":" + WHITESPACE@6..7 " " + EXPR@7..8 + EXPR_VAR@7..8 + NAME@7..8 + IDENT@7..8 "v" + WHITESPACE@8..9 " " + FOR_OBJ_SPEC@9..28 + FOR_KW@9..12 "for" + WHITESPACE@12..13 " " + L_BRACK@13..14 "[" + NAME@14..15 + IDENT@14..15 "k" + R_BRACK@15..16 "]" + VISIBILITY@16..19 + COLON@16..17 ":" + COLON@17..18 ":" + COLON@18..19 ":" + WHITESPACE@19..20 " " + DESTRUCT_FULL@20..21 + NAME@20..21 + IDENT@20..21 "v" + WHITESPACE@21..22 " " + IN_KW@22..24 "in" + WHITESPACE@24..25 " " + EXPR@25..28 + EXPR_VAR@25..28 + NAME@25..28 + IDENT@25..28 "obj" + WHITESPACE@28..29 " " + R_BRACE@29..30 "}" + WHITESPACE@30..31 "\n" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_hidden.snap @@ -0,0 +1,50 @@ +--- +source: crates/jrsonnet-rowan-parser/src/tests.rs +expression: "{ [k]: v for [k]:: v in obj }\n" +--- +SOURCE_FILE@0..30 + EXPR@0..29 + EXPR_OBJECT@0..29 + OBJ_BODY_COMP@0..29 + L_BRACE@0..1 "{" + WHITESPACE@1..2 " " + MEMBER_FIELD_NORMAL@2..8 + FIELD_NAME_DYNAMIC@2..5 + L_BRACK@2..3 "[" + EXPR@3..4 + EXPR_VAR@3..4 + NAME@3..4 + IDENT@3..4 "k" + R_BRACK@4..5 "]" + VISIBILITY@5..6 + COLON@5..6 ":" + WHITESPACE@6..7 " " + EXPR@7..8 + EXPR_VAR@7..8 + NAME@7..8 + IDENT@7..8 "v" + WHITESPACE@8..9 " " + FOR_OBJ_SPEC@9..27 + FOR_KW@9..12 "for" + WHITESPACE@12..13 " " + L_BRACK@13..14 "[" + NAME@14..15 + IDENT@14..15 "k" + R_BRACK@15..16 "]" + VISIBILITY@16..18 + COLON@16..17 ":" + COLON@17..18 ":" + WHITESPACE@18..19 " " + DESTRUCT_FULL@19..20 + NAME@19..20 + IDENT@19..20 "v" + WHITESPACE@20..21 " " + IN_KW@21..23 "in" + WHITESPACE@23..24 " " + EXPR@24..27 + EXPR_VAR@24..27 + NAME@24..27 + IDENT@24..27 "obj" + WHITESPACE@27..28 " " + R_BRACE@28..29 "}" + WHITESPACE@29..30 "\n" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_value_destruct.snap @@ -0,0 +1,66 @@ +--- +source: crates/jrsonnet-rowan-parser/src/tests.rs +expression: "{ [k]: a + b for [k]: [a, b] in obj }\n" +--- +SOURCE_FILE@0..38 + EXPR@0..37 + EXPR_OBJECT@0..37 + OBJ_BODY_COMP@0..37 + L_BRACE@0..1 "{" + WHITESPACE@1..2 " " + MEMBER_FIELD_NORMAL@2..12 + FIELD_NAME_DYNAMIC@2..5 + L_BRACK@2..3 "[" + EXPR@3..4 + EXPR_VAR@3..4 + NAME@3..4 + IDENT@3..4 "k" + R_BRACK@4..5 "]" + VISIBILITY@5..6 + COLON@5..6 ":" + WHITESPACE@6..7 " " + EXPR@7..12 + EXPR_BINARY@7..12 + EXPR@7..8 + EXPR_VAR@7..8 + NAME@7..8 + IDENT@7..8 "a" + WHITESPACE@8..9 " " + PLUS@9..10 "+" + WHITESPACE@10..11 " " + EXPR@11..12 + EXPR_VAR@11..12 + NAME@11..12 + IDENT@11..12 "b" + WHITESPACE@12..13 " " + FOR_OBJ_SPEC@13..35 + FOR_KW@13..16 "for" + WHITESPACE@16..17 " " + L_BRACK@17..18 "[" + NAME@18..19 + IDENT@18..19 "k" + R_BRACK@19..20 "]" + VISIBILITY@20..21 + COLON@20..21 ":" + WHITESPACE@21..22 " " + DESTRUCT_ARRAY@22..28 + L_BRACK@22..23 "[" + DESTRUCT_FULL@23..24 + NAME@23..24 + IDENT@23..24 "a" + COMMA@24..25 "," + WHITESPACE@25..26 " " + DESTRUCT_FULL@26..27 + NAME@26..27 + IDENT@26..27 "b" + R_BRACK@27..28 "]" + WHITESPACE@28..29 " " + IN_KW@29..31 "in" + WHITESPACE@31..32 " " + EXPR@32..35 + EXPR_VAR@32..35 + NAME@32..35 + IDENT@32..35 "obj" + WHITESPACE@35..36 " " + R_BRACE@36..37 "}" + WHITESPACE@37..38 "\n" --- /dev/null +++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_visible.snap @@ -0,0 +1,49 @@ +--- +source: crates/jrsonnet-rowan-parser/src/tests.rs +expression: "{ [k]: v for [k]: v in obj }\n" +--- +SOURCE_FILE@0..29 + EXPR@0..28 + EXPR_OBJECT@0..28 + OBJ_BODY_COMP@0..28 + L_BRACE@0..1 "{" + WHITESPACE@1..2 " " + MEMBER_FIELD_NORMAL@2..8 + FIELD_NAME_DYNAMIC@2..5 + L_BRACK@2..3 "[" + EXPR@3..4 + EXPR_VAR@3..4 + NAME@3..4 + IDENT@3..4 "k" + R_BRACK@4..5 "]" + VISIBILITY@5..6 + COLON@5..6 ":" + WHITESPACE@6..7 " " + EXPR@7..8 + EXPR_VAR@7..8 + NAME@7..8 + IDENT@7..8 "v" + WHITESPACE@8..9 " " + FOR_OBJ_SPEC@9..26 + FOR_KW@9..12 "for" + WHITESPACE@12..13 " " + L_BRACK@13..14 "[" + NAME@14..15 + IDENT@14..15 "k" + R_BRACK@15..16 "]" + VISIBILITY@16..17 + COLON@16..17 ":" + WHITESPACE@17..18 " " + DESTRUCT_FULL@18..19 + NAME@18..19 + IDENT@18..19 "v" + WHITESPACE@19..20 " " + IN_KW@20..22 "in" + WHITESPACE@22..23 " " + EXPR@23..26 + EXPR_VAR@23..26 + NAME@23..26 + IDENT@23..26 "obj" + WHITESPACE@26..27 " " + R_BRACE@27..28 "}" + WHITESPACE@28..29 "\n" --- a/crates/jrsonnet-rowan-parser/src/tests.rs +++ b/crates/jrsonnet-rowan-parser/src/tests.rs @@ -228,6 +228,19 @@ local_in_binop_rhs => r#" a + local x = 1; x "# + + for_obj_spec_visible => r#" + { [k]: v for [k]: v in obj } + "# + for_obj_spec_hidden => r#" + { [k]: v for [k]:: v in obj } + "# + for_obj_spec_force_visible => r#" + { [k]: v for [k]::: v in obj } + "# + for_obj_spec_value_destruct => r#" + { [k]: a + b for [k]: [a, b] in obj } + "# ); #[test] -- gitstuff