difftreelog
feat(rowan) alternative object comp syntax
in: master
10 files changed
crates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth--- 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),
}
}
crates/jrsonnet-rowan-parser/jsonnet.ungramdiffbeforeafterboth--- 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 =
crates/jrsonnet-rowan-parser/src/generated/nodes.rsdiffbeforeafterboth--- 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<SyntaxToken> {
+ support::token(&self.syntax, T![for])
+ }
+ pub fn l_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['['])
+ }
+ pub fn key(&self) -> Option<Name> {
+ support::children(&self.syntax).next()
+ }
+ pub fn r_brack_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![']'])
+ }
+ pub fn visibility(&self) -> Option<Visibility> {
+ support::children(&self.syntax).next()
+ }
+ pub fn value(&self) -> Option<Destruct> {
+ support::children(&self.syntax).next()
+ }
+ pub fn in_kw_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![in])
+ }
+ pub fn expr(&self) -> Option<Expr> {
+ 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<Self> {
+ 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<ForObjSpec> for CompSpec {
+ fn from(node: ForObjSpec) -> CompSpec {
+ CompSpec::ForObjSpec(node)
+ }
+}
impl From<IfSpec> 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<Self> {
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)
crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rsdiffbeforeafterboth--- 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,
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth1use std::{cell::Cell, fmt, rc::Rc};23use rowan::{GreenNode, TextRange};45use crate::{6 AstToken, SyntaxKind,7 SyntaxKind::*,8 SyntaxNode, T, TS,9 event::Event,10 marker::{CompletedMarker, Marker},11 nodes::{BinaryOperatorKind, Literal, Number, Text, UnaryOperatorKind},12 token_set::SyntaxKindSet,13};1415pub struct Parse {16 pub green_node: GreenNode,17 pub errors: Vec<LocatedSyntaxError>,18}1920pub struct Parser {21 // TODO: remove all trivia before feeding to parser?22 kinds: Vec<SyntaxKind>,23 pub offset: usize,24 pub events: Vec<Event>,25 pub entered: u32,26 pub hints: Vec<(u32, TextRange, String)>,27 pub last_error_token: usize,28 expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,29 steps: Cell<u64>,30}3132#[derive(Clone, Debug)]33pub enum SyntaxError {34 Unexpected {35 expected: ExpectedSyntax,36 found: SyntaxKind,37 },38 Missing {39 expected: ExpectedSyntax,40 },41 Custom {42 error: String,43 },44 Hint {45 error: String,46 },47}48impl fmt::Display for SyntaxError {49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {50 match self {51 SyntaxError::Unexpected { expected, found } => {52 write!(f, "unexpected {found:?}, expecting {expected}")53 }54 SyntaxError::Missing { expected } => write!(f, "missing {expected}"),55 SyntaxError::Custom { error } | SyntaxError::Hint { error } => write!(f, "{error}"),56 }57 }58}5960#[derive(Debug)]61pub struct LocatedSyntaxError {62 pub error: SyntaxError,63 pub range: TextRange,64}6566impl Parser {67 pub fn new(kinds: Vec<SyntaxKind>) -> Self {68 Self {69 kinds,70 offset: 0,71 events: vec![],72 entered: 0,73 last_error_token: 0,74 hints: vec![],75 expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),76 steps: Cell::new(0),77 }78 }79 pub fn clear_outdated_hints(&mut self) {80 let amount = self81 .hints82 .iter()83 .rev()84 .take_while(|h| h.0 > self.entered)85 .count();86 self.hints.truncate(self.hints.len() - amount);87 }88 fn clear_expected_syntaxes(&self) {89 self.expected_syntax_tracking_state90 .set(ExpectedSyntax::Unnamed(TS![]));91 }92 pub fn start(&mut self) -> Marker {93 let start_event_idx = self.events.len();94 self.events.push(Event::Pending);95 self.entered += 1;96 Marker::new(start_event_idx)97 }98 // pub fn start_ranger(&mut self) -> Ranger {99 // let pos = self.offset;100 // Ranger { pos }101 // }102 pub fn parse(mut self) -> Vec<Event> {103 let m = self.start();104 expr(&mut self);105 if !self.at(EOF) {106 let m = self.start();107 while !self.at(EOF) {108 self.bump();109 }110 m.complete_error(&mut self, "unexpected tokens after end");111 }112 m.complete(&mut self, SOURCE_FILE);113114 self.events115 }116117 pub(crate) fn expect(&mut self, kind: SyntaxKind) {118 self.expect_with_recovery_set(kind, TS![]);119 }120121 pub(crate) fn expect_with_recovery_set(122 &mut self,123 kind: SyntaxKind,124 recovery_set: SyntaxKindSet,125 ) {126 if self.at(kind) {127 if kind != EOF {128 self.bump();129 }130 } else {131 self.error_with_recovery_set(recovery_set);132 }133 }134135 // pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {136 // if self.at(kind) {137 // self.bump();138 // } else {139 // self.error_with_no_skip();140 // }141 // }142 pub fn error_with_no_skip(&mut self) -> CompletedMarker {143 self.error_with_recovery_set(SyntaxKindSet::ALL)144 }145146 pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {147 let expected = self.expected_syntax_tracking_state.get();148 self.expected_syntax_tracking_state149 .set(ExpectedSyntax::Unnamed(TS![]));150151 if self.at_end() || self.at_ts(recovery_set) {152 let m = self.start();153 return m.complete_missing(self, expected);154 }155156 let current_token = self.current();157158 self.last_error_token = self.offset;159160 let m = self.start();161 self.bump();162 let m = m.complete_unexpected(self, expected, current_token);163 self.clear_expected_syntaxes();164 m165 }166 fn bump_assert(&mut self, kind: SyntaxKind) {167 assert!(self.at(kind), "expected {kind:?}");168 self.bump_remap(self.current());169 }170 fn bump(&mut self) {171 self.bump_remap(self.current());172 }173 fn bump_remap(&mut self, kind: SyntaxKind) {174 assert_ne!(self.offset, self.kinds.len(), "already at end");175 self.events.push(Event::Token { kind });176 self.offset += 1;177 self.clear_expected_syntaxes();178 }179 fn step(&self) {180 use std::fmt::Write;181 let steps = self.steps.get();182 if steps >= 15_000_000 {183 let mut out = "seems like parsing is stuck".to_owned();184 {185 let last = 20;186 write!(out, "\n\nLast {last} events:").unwrap();187 for (i, event) in self188 .events189 .iter()190 .skip(self.events.len().saturating_sub(last))191 .enumerate()192 {193 write!(out, "\n{i}. {event:?}").unwrap();194 }195 }196 {197 let next = 20;198 write!(out, "\n\nNext {next} tokens:").unwrap();199 for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {200 write!(out, "\n{i}. {tok:?}").unwrap();201 }202 }203 panic!("{out}")204 }205 self.steps.set(steps + 1);206 }207 fn nth(&self, i: usize) -> SyntaxKind {208 self.step();209 let mut offset = self.offset;210 for _ in 0..i {211 offset += 1;212 }213 self.kinds.get(offset).copied().unwrap_or(EOF)214 }215 fn current(&self) -> SyntaxKind {216 self.nth(0)217 }218 #[must_use]219 pub(crate) fn expected_syntax_name(&self, name: &'static str) -> ExpectedSyntaxGuard {220 self.expected_syntax_tracking_state221 .set(ExpectedSyntax::Named(name));222223 ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))224 }225 pub fn at(&self, kind: SyntaxKind) -> bool {226 self.nth_at(0, kind)227 }228 pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {229 if n == 0 {230 if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {231 let kinds = kinds.with(kind);232 self.expected_syntax_tracking_state233 .set(ExpectedSyntax::Unnamed(kinds));234 }235 }236 self.nth(n) == kind237 }238 pub fn at_ts(&self, set: SyntaxKindSet) -> bool {239 if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {240 let kinds = kinds.union(set);241 self.expected_syntax_tracking_state242 .set(ExpectedSyntax::Unnamed(kinds));243 }244 set.contains(self.current())245 }246 pub fn at_end(&self) -> bool {247 self.at(EOF)248 }249}250pub struct ExpectedSyntaxGuard {251 expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,252}253254impl ExpectedSyntaxGuard {255 fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {256 Self {257 expected_syntax_tracking_state,258 }259 }260}261262impl Drop for ExpectedSyntaxGuard {263 fn drop(&mut self) {264 self.expected_syntax_tracking_state265 .set(ExpectedSyntax::Unnamed(TS![]));266 }267}268269#[derive(Clone, Debug, Copy)]270pub enum ExpectedSyntax {271 Named(&'static str),272 Unnamed(SyntaxKindSet),273}274impl fmt::Display for ExpectedSyntax {275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {276 match self {277 Self::Named(name) => write!(f, "{name}"),278 Self::Unnamed(set) => write!(f, "{set}"),279 }280 }281}282283fn expr(p: &mut Parser) -> CompletedMarker {284 let m = p.start();285 while p.at(T![local]) || p.at(T![assert]) {286 let m = p.start();287288 if p.at(T![local]) {289 p.bump();290 loop {291 if p.at(T![;]) {292 p.bump();293 break;294 }295 bind(p);296297 if p.at(T![,]) {298 p.bump();299 continue;300 }301 p.expect(T![;]);302 break;303 }304 m.complete(p, STMT_LOCAL);305 } else {306 assertion(p);307 p.expect(T![;]);308 m.complete(p, STMT_ASSERT);309 }310 }311 match expr_binding_power(p, 0) {312 Ok(m) | Err(m) => m,313 };314 m.complete(p, EXPR)315}316317fn expr_binding_power(318 p: &mut Parser,319 minimum_binding_power: u8,320) -> Result<CompletedMarker, CompletedMarker> {321 let mut lhs = lhs(p)?;322323 while let Some(op) = BinaryOperatorKind::cast(p.current())324 .or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))325 {326 let (left_binding_power, right_binding_power) = op.binding_power();327 if left_binding_power < minimum_binding_power {328 break;329 }330331 let m = lhs.wrap(p, EXPR, false);332333 // Object apply is not a real operator, we dont have something to bump334 if op != BinaryOperatorKind::MetaObjectApply {335 p.bump();336 }337338 let m = m.precede(p);339 let parsed_rhs = if p.at(T![local]) || p.at(T![assert]) {340 expr(p);341 true342 } else {343 expr_binding_power(p, right_binding_power)344 .map(|v| v.precede(p).complete(p, EXPR))345 .is_ok()346 };347 lhs = m.complete(348 p,349 if op == BinaryOperatorKind::MetaObjectApply {350 EXPR_OBJ_EXTEND351 } else {352 EXPR_BINARY353 },354 );355356 if !parsed_rhs {357 break;358 }359 }360 Ok(lhs)361}362363const COMPSPEC: SyntaxKindSet = TS![for if];364fn compspec(p: &mut Parser) -> CompletedMarker {365 assert!(p.at_ts(COMPSPEC));366 if p.at(T![for]) {367 if p.nth_at(1, T!['[']) && p.nth_at(2, IDENT) && p.nth_at(3, T![']']) && p.nth_at(4, T![:])368 {369 let m = p.start();370 p.bump_assert(T![for]);371 p.bump_assert(T!['[']);372 name(p);373 p.expect(T![']']);374 visibility(p);375 destruct(p);376 p.expect(T![in]);377 expr(p);378 return m.complete(p, FOR_OBJ_SPEC);379 }380 let m = p.start();381 p.bump();382 destruct(p);383 p.expect(T![in]);384 expr(p);385 m.complete(p, FOR_SPEC)386 } else if p.at(T![if]) {387 let m = p.start();388 p.bump();389 expr(p);390 m.complete(p, IF_SPEC)391 } else {392 unreachable!()393 }394}395396fn comma(p: &mut Parser) -> bool {397 comma_with_alternatives(p, TS![])398}399fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {400 if p.at(T![,]) {401 p.bump();402 true403 } else if p.at_ts(set) {404 let _ex = p.expected_syntax_name("comma");405 p.expect_with_recovery_set(T![,], TS![]);406 true407 } else {408 false409 }410}411412fn field_name(p: &mut Parser) {413 let _e = p.expected_syntax_name("field name");414 let m = p.start();415 if p.at(T!['[']) {416 p.bump();417 expr(p);418 p.expect(T![']']);419 m.complete(p, FIELD_NAME_DYNAMIC);420 } else if p.at(IDENT) {421 name(p);422 m.complete(p, FIELD_NAME_FIXED);423 } else if Text::can_cast(p.current()) {424 text(p);425 m.complete(p, FIELD_NAME_FIXED);426 } else {427 m.forget(p);428 // Recover with ::, :::429 p.error_with_recovery_set(TS![; : '(']);430 }431}432fn visibility(p: &mut Parser) {433 let m = p.start();434 if !p.at_ts(TS![:]) {435 p.error_with_recovery_set(TS![=]);436 }437 p.bump();438 'colons: {439 if !p.at_ts(TS![:]) {440 break 'colons;441 }442 p.bump();443 if !p.at_ts(TS![:]) {444 break 'colons;445 }446 p.bump();447 }448 m.complete(p, VISIBILITY);449}450fn assertion(p: &mut Parser) {451 let m = p.start();452 p.bump_assert(T![assert]);453 expr(p);454 if p.at(T![:]) {455 p.bump();456 expr(p);457 }458 m.complete(p, ASSERTION);459}460fn object(p: &mut Parser) -> CompletedMarker {461 let m_t = p.start();462 let m = p.start();463 p.bump_assert(T!['{']);464465 let mut elems = 0;466 let mut compspecs = Vec::new();467 let mut asserts = Vec::new();468 loop {469 if p.at(T!['}']) {470 p.bump();471 break;472 }473 if p.at_ts(TS![for]) {474 if elems == 0 {475 let m = p.start();476 m.complete_missing(p, ExpectedSyntax::Named("field definition"));477 }478 while p.at_ts(COMPSPEC) {479 compspecs.push(compspec(p));480 }481 if comma_with_alternatives(p, TS![;]) {482 continue;483 }484 p.expect(R_BRACE);485 break;486 }487 let m = p.start();488 if p.at(T![local]) {489 obj_local(p);490 m.complete(p, MEMBER_BIND_STMT);491 } else if p.at(T![assert]) {492 assertion(p);493 asserts.push(m.complete(p, MEMBER_ASSERT_STMT));494 } else {495 field_name(p);496 if p.at(T![+]) {497 p.bump();498 }499 let params = if p.at(T!['(']) {500 params_desc(p);501 visibility(p);502 expr(p);503 true504 } else {505 visibility(p);506 if p.at(T![function]) {507 p.bump_assert(T![function]);508 params_desc(p);509 expr(p);510 true511 } else {512 expr(p);513 false514 }515 };516 elems += 1;517518 if params {519 m.complete(p, MEMBER_FIELD_METHOD)520 } else {521 m.complete(p, MEMBER_FIELD_NORMAL)522 };523 }524 while p.at_ts(COMPSPEC) {525 compspecs.push(compspec(p));526 }527 if comma_with_alternatives(p, TS![;]) {528 continue;529 }530 p.expect(R_BRACE);531 break;532 }533534 if elems > 1 && !compspecs.is_empty() {535 for errored in compspecs {536 errored.wrap_error(537 p,538 "compspec may only be used if there is only one object element",539 true,540 );541 }542 m.complete(p, OBJ_BODY_MEMBER_LIST);543 } else if !compspecs.is_empty() {544 for errored in asserts {545 errored.wrap_error(p, "asserts can't be used in object comprehensions", true);546 }547 m.complete(p, OBJ_BODY_COMP);548 } else {549 m.complete(p, OBJ_BODY_MEMBER_LIST);550 }551 m_t.complete(p, EXPR_OBJECT)552}553fn param(p: &mut Parser) {554 let m = p.start();555 destruct(p);556 if p.at(T![=]) {557 p.bump();558 expr(p);559 }560 m.complete(p, PARAM);561}562fn params_desc(p: &mut Parser) -> CompletedMarker {563 let m = p.start();564 p.bump_assert(T!['(']);565566 loop {567 if p.at(T![')']) {568 p.bump();569 break;570 }571 param(p);572 if comma(p) {573 continue;574 }575 p.expect(T![')']);576 break;577 }578579 m.complete(p, PARAMS_DESC)580}581fn args_desc(p: &mut Parser) {582 let m = p.start();583 p.bump_assert(T!['(']);584585 let started_named = Cell::new(false);586 let mut unnamed_after_named = Vec::new();587588 loop {589 if p.at(T![')']) {590 break;591 }592593 let m = p.start();594 if p.at(IDENT) && p.nth_at(1, T![=]) {595 name(p);596 p.bump();597 expr(p);598 m.complete(p, ARG);599 started_named.set(true);600 } else {601 expr(p);602 let arg = m.complete(p, ARG);603 if started_named.get() {604 unnamed_after_named.push(arg);605 }606 }607 if comma(p) {608 continue;609 }610 break;611 }612 p.expect(T![')']);613 if p.at(T![tailstrict]) {614 p.bump();615 }616617 for errored in unnamed_after_named {618 errored.wrap_error(p, "can't use positional arguments after named", true);619 }620621 m.complete(p, ARGS_DESC);622}623624fn array(p: &mut Parser) -> CompletedMarker {625 // Start the list node626 let m = p.start();627 p.bump_assert(T!['[']);628629 let mut compspecs = Vec::new();630 let mut elems = 0;631632 loop {633 if p.at(T![']']) {634 p.bump();635 break;636 }637 if elems != 0 && p.at_ts(TS![for]) {638 while p.at_ts(COMPSPEC) {639 compspecs.push(compspec(p));640 }641 if comma(p) {642 continue;643 }644 p.expect(T![']']);645 break;646 }647 expr(p);648 elems += 1;649 while p.at_ts(COMPSPEC) {650 compspecs.push(compspec(p));651 }652 if comma(p) {653 continue;654 }655 p.expect(T![']']);656 break;657 }658659 if elems > 1 && !compspecs.is_empty() {660 for spec in compspecs {661 spec.wrap_error(662 p,663 "compspec may only be used if there is only one array element",664 true,665 );666 }667668 m.complete(p, EXPR_ARRAY)669 } else if !compspecs.is_empty() {670 m.complete(p, EXPR_ARRAY_COMP)671 } else {672 m.complete(p, EXPR_ARRAY)673 }674}675/// Returns true if it was slice, false if just index676#[must_use]677fn slice_desc_or_index(p: &mut Parser) -> bool {678 let m = p.start();679 p.bump();680 // Start681 if !p.at(T![:]) {682 expr(p);683 }684 if p.at(T![:]) {685 p.bump();686 // End687 if !p.at_ts(TS![']' :]) {688 expr(p).wrap(p, SLICE_DESC_END, true);689 }690 if p.at(T![:]) {691 p.bump();692 // Step693 if !p.at(T![']']) {694 expr(p).wrap(p, SLICE_DESC_STEP, true);695 }696 }697 } else {698 // It was not a slice699 p.expect(T![']']);700 m.forget(p);701 return false;702 }703 p.expect(T![']']);704 m.complete(p, SLICE_DESC);705 true706}707708fn suffix(p: &mut Parser) {709 loop {710 let start = p.start();711 let _marker: CompletedMarker = if p.at(T![?]) {712 p.bump();713 p.expect(T![.]);714 if p.at(IDENT) {715 name(p);716 start.complete(p, SUFFIX_INDEX)717 } else if p.at(T!['[']) {718 p.bump();719 expr(p);720 p.expect(T![']']);721 start.complete(p, SUFFIX_INDEX_EXPR)722 } else {723 start.complete_missing(p, ExpectedSyntax::Named("index"))724 }725 } else if p.at(T![.]) {726 p.bump();727 name(p);728 start.complete(p, SUFFIX_INDEX)729 } else if p.at(T!['[']) {730 if slice_desc_or_index(p) {731 start.complete(p, SUFFIX_SLICE)732 } else {733 start.complete(p, SUFFIX_INDEX_EXPR)734 }735 } else if p.at(T!['(']) {736 args_desc(p);737 start.complete(p, SUFFIX_APPLY)738 } else {739 start.forget(p);740 break;741 };742 }743}744745fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {746 let lhs = lhs_basic(p)?;747748 suffix(p);749750 Ok(lhs)751}752fn name(p: &mut Parser) {753 let m = p.start();754 p.expect(IDENT);755 m.complete(p, NAME);756}757fn destruct_rest(p: &mut Parser) {758 let m = p.start();759 p.bump_assert(T![...]);760 if p.at(IDENT) {761 p.bump();762 }763 m.complete(p, DESTRUCT_REST);764}765fn destruct_object_field(p: &mut Parser) {766 let m = p.start();767 name(p);768 if p.at(T![:]) {769 p.bump();770 destruct(p);771 }772 if p.at(T![=]) {773 p.bump();774 expr(p);775 }776 m.complete(p, DESTRUCT_OBJECT_FIELD);777}778fn obj_local(p: &mut Parser) {779 let m = p.start();780 p.bump_assert(T![local]);781 bind(p);782 m.complete(p, OBJ_LOCAL);783}784fn destruct(p: &mut Parser) -> CompletedMarker {785 let m = p.start();786 let _ex = p.expected_syntax_name("destruction specifier");787 if p.at(T![?]) {788 p.bump();789 m.complete(p, DESTRUCT_SKIP)790 } else if p.at(T!['[']) {791 p.bump();792 // let mut had_rest = false;793 loop {794 if p.at(T![']']) {795 p.bump();796 break;797 } else if p.at(T![...]) {798 // let m_err = p.start_ranger();799 destruct_rest(p);800 // if had_rest {801 // p.custom_error(m_err.finish(p), "only one rest can be present in array");802 // }803 // had_rest = true;804 } else {805 destruct(p);806 }807 if p.at(T![,]) {808 p.bump();809 continue;810 }811 p.expect(T![']']);812 break;813 }814 m.complete(p, DESTRUCT_ARRAY)815 } else if p.at(T!['{']) {816 p.bump();817 let mut had_rest = false;818 loop {819 if p.at(T!['}']) {820 p.bump();821 break;822 } else if p.at(T![...]) {823 // let m_err = p.start_ranger();824 destruct_rest(p);825 // if had_rest {826 // p.custom_error(m_err.finish(p), "only one rest can be present in object");827 // }828 had_rest = true;829 } else {830 if had_rest {831 p.error_with_recovery_set(TS![]);832 }833 destruct_object_field(p);834 }835 if p.at(T![,]) {836 p.bump();837 continue;838 }839 p.expect(T!['}']);840 break;841 }842 m.complete(p, DESTRUCT_OBJECT)843 } else if p.at(IDENT) {844 name(p);845 m.complete(p, DESTRUCT_FULL)846 } else {847 m.forget(p);848 p.error_with_recovery_set(TS![; , '}', '(', :])849 }850}851fn bind(p: &mut Parser) {852 let m = p.start();853 if p.at(IDENT) && p.nth_at(1, T!['(']) {854 name(p);855 params_desc(p);856 p.expect(T![=]);857 expr(p);858 m.complete(p, BIND_FUNCTION)859 } else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {860 name(p);861 p.expect(T![=]);862 p.expect(T![function]);863 params_desc(p);864 expr(p);865 m.complete(p, BIND_FUNCTION)866 } else {867 destruct(p);868 p.expect(T![=]);869 expr(p);870 m.complete(p, BIND_DESTRUCT)871 };872}873fn text(p: &mut Parser) {874 assert!(Text::can_cast(p.current()));875 p.bump();876}877fn number(p: &mut Parser) {878 assert!(Number::can_cast(p.current()));879 p.bump();880}881fn literal(p: &mut Parser) {882 assert!(Literal::can_cast(p.current()));883 p.bump();884}885fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {886 let _e = p.expected_syntax_name("expression");887 Ok(if Literal::can_cast(p.current()) {888 let m = p.start();889 literal(p);890 m.complete(p, EXPR_LITERAL)891 } else if Text::can_cast(p.current()) {892 let m = p.start();893 text(p);894 m.complete(p, EXPR_STRING)895 } else if Number::can_cast(p.current()) {896 let m = p.start();897 number(p);898 m.complete(p, EXPR_NUMBER)899 } else if p.at(IDENT) {900 let m = p.start();901 name(p);902 m.complete(p, EXPR_VAR)903 } else if p.at(T![if]) {904 let m = p.start();905 p.bump();906 expr(p);907 p.expect(T![then]);908 expr(p).wrap(p, TRUE_EXPR, true);909 if p.at(T![else]) {910 p.bump();911 expr(p).wrap(p, FALSE_EXPR, true);912 }913 m.complete(p, EXPR_IF_THEN_ELSE)914 } else if p.at(T!['[']) {915 array(p)916 } else if p.at(T!['{']) {917 object(p)918 } else if p.at(T![function]) {919 let m = p.start();920 p.bump();921 params_desc(p);922 expr(p);923 m.complete(p, EXPR_FUNCTION)924 } else if p.at(T![error]) {925 let m = p.start();926 p.bump();927 expr(p);928 m.complete(p, EXPR_ERROR)929 } else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {930 let m = p.start();931 p.bump();932 text(p);933 m.complete(p, EXPR_IMPORT)934 } else if let Some(op) = UnaryOperatorKind::cast(p.current()) {935 let ((), right_binding_power) = op.binding_power();936937 let m = p.start();938 p.bump();939 let _ = expr_binding_power(p, right_binding_power).map(|v| v.precede(p).complete(p, EXPR));940 m.complete(p, EXPR_UNARY)941 } else if p.at(T!['(']) {942 let m = p.start();943 p.bump();944 expr(p);945 p.expect(T![')']);946 m.complete(p, EXPR_PARENED)947 } else {948 return Err(p.error_with_no_skip());949 })950}951952impl Parse {953 pub fn syntax(&self) -> SyntaxNode {954 SyntaxNode::new_root(self.green_node.clone())955 }956}crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_force_visible.snapdiffbeforeafterboth--- /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"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_hidden.snapdiffbeforeafterboth--- /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"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_value_destruct.snapdiffbeforeafterboth--- /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"
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__for_obj_spec_visible.snapdiffbeforeafterboth--- /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"
crates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth--- 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]