difftreelog
fix(rowan) statements bind the tighest
in: master
3 files changed
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth1use std::{cell::Cell, fmt, rc::Rc};23use rowan::{GreenNode, TextRange};45use crate::{6 event::Event,7 marker::{CompletedMarker, Marker},8 nodes::{BinaryOperatorKind, Literal, Number, Text, UnaryOperatorKind},9 token_set::SyntaxKindSet,10 AstToken, SyntaxKind,11 SyntaxKind::*,12 SyntaxNode, T, TS,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}316fn expr_binding_power(317 p: &mut Parser,318 minimum_binding_power: u8,319) -> Result<CompletedMarker, CompletedMarker> {320 let mut lhs = lhs(p)?;321322 while let Some(op) = BinaryOperatorKind::cast(p.current())323 .or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))324 {325 let (left_binding_power, right_binding_power) = op.binding_power();326 if left_binding_power < minimum_binding_power {327 break;328 }329330 let m = lhs.wrap(p, EXPR, false);331332 // Object apply is not a real operator, we dont have something to bump333 if op != BinaryOperatorKind::MetaObjectApply {334 p.bump();335 }336337 let m = m.precede(p);338 let parsed_rhs = expr_binding_power(p, right_binding_power)339 .map(|v| v.precede(p).complete(p, EXPR))340 .is_ok();341 lhs = m.complete(342 p,343 if op == BinaryOperatorKind::MetaObjectApply {344 EXPR_OBJ_EXTEND345 } else {346 EXPR_BINARY347 },348 );349350 if !parsed_rhs {351 break;352 }353 }354 Ok(lhs)355}356357const COMPSPEC: SyntaxKindSet = TS![for if];358fn compspec(p: &mut Parser) -> CompletedMarker {359 assert!(p.at_ts(COMPSPEC));360 if p.at(T![for]) {361 let m = p.start();362 p.bump();363 destruct(p);364 p.expect(T![in]);365 expr(p);366 m.complete(p, FOR_SPEC)367 } else if p.at(T![if]) {368 let m = p.start();369 p.bump();370 expr(p);371 m.complete(p, IF_SPEC)372 } else {373 unreachable!()374 }375}376377fn comma(p: &mut Parser) -> bool {378 comma_with_alternatives(p, TS![])379}380fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {381 if p.at(T![,]) {382 p.bump();383 true384 } else if p.at_ts(set) {385 let _ex = p.expected_syntax_name("comma");386 p.expect_with_recovery_set(T![,], TS![]);387 true388 } else {389 false390 }391}392393fn field_name(p: &mut Parser) {394 let _e = p.expected_syntax_name("field name");395 let m = p.start();396 if p.at(T!['[']) {397 p.bump();398 expr(p);399 p.expect(T![']']);400 m.complete(p, FIELD_NAME_DYNAMIC);401 } else if p.at(IDENT) {402 name(p);403 m.complete(p, FIELD_NAME_FIXED);404 } else if Text::can_cast(p.current()) {405 text(p);406 m.complete(p, FIELD_NAME_FIXED);407 } else {408 m.forget(p);409 // Recover with ::, :::410 p.error_with_recovery_set(TS![; : '(']);411 }412}413fn visibility(p: &mut Parser) {414 let m = p.start();415 if !p.at_ts(TS![:]) {416 p.error_with_recovery_set(TS![=]);417 }418 p.bump();419 'colons: {420 if !p.at_ts(TS![:]) {421 break 'colons;422 }423 p.bump();424 if !p.at_ts(TS![:]) {425 break 'colons;426 }427 p.bump();428 }429 m.complete(p, VISIBILITY);430}431fn assertion(p: &mut Parser) {432 let m = p.start();433 p.bump_assert(T![assert]);434 expr(p);435 if p.at(T![:]) {436 p.bump();437 expr(p);438 }439 m.complete(p, ASSERTION);440}441fn object(p: &mut Parser) -> CompletedMarker {442 let m_t = p.start();443 let m = p.start();444 p.bump_assert(T!['{']);445446 let mut elems = 0;447 let mut compspecs = Vec::new();448 let mut asserts = Vec::new();449 loop {450 if p.at(T!['}']) {451 p.bump();452 break;453 }454 if p.at_ts(TS![for]) {455 if elems == 0 {456 let m = p.start();457 m.complete_missing(p, ExpectedSyntax::Named("field definition"));458 }459 while p.at_ts(COMPSPEC) {460 compspecs.push(compspec(p));461 }462 if comma_with_alternatives(p, TS![;]) {463 continue;464 }465 p.expect(R_BRACE);466 break;467 }468 let m = p.start();469 if p.at(T![local]) {470 obj_local(p);471 m.complete(p, MEMBER_BIND_STMT);472 } else if p.at(T![assert]) {473 assertion(p);474 asserts.push(m.complete(p, MEMBER_ASSERT_STMT));475 } else {476 field_name(p);477 if p.at(T![+]) {478 p.bump();479 }480 let params = if p.at(T!['(']) {481 params_desc(p);482 visibility(p);483 expr(p);484 true485 } else {486 visibility(p);487 if p.at(T![function]) {488 p.bump_assert(T![function]);489 params_desc(p);490 expr(p);491 true492 } else {493 expr(p);494 false495 }496 };497 elems += 1;498499 if params {500 m.complete(p, MEMBER_FIELD_METHOD)501 } else {502 m.complete(p, MEMBER_FIELD_NORMAL)503 };504 }505 while p.at_ts(COMPSPEC) {506 compspecs.push(compspec(p));507 }508 if comma_with_alternatives(p, TS![;]) {509 continue;510 }511 p.expect(R_BRACE);512 break;513 }514515 if elems > 1 && !compspecs.is_empty() {516 for errored in compspecs {517 errored.wrap_error(518 p,519 "compspec may only be used if there is only one object element",520 true,521 );522 }523 m.complete(p, OBJ_BODY_MEMBER_LIST);524 } else if !compspecs.is_empty() {525 for errored in asserts {526 errored.wrap_error(p, "asserts can't be used in object comprehensions", true);527 }528 m.complete(p, OBJ_BODY_COMP);529 } else {530 m.complete(p, OBJ_BODY_MEMBER_LIST);531 }532 m_t.complete(p, EXPR_OBJECT)533}534fn param(p: &mut Parser) {535 let m = p.start();536 destruct(p);537 if p.at(T![=]) {538 p.bump();539 expr(p);540 }541 m.complete(p, PARAM);542}543fn params_desc(p: &mut Parser) -> CompletedMarker {544 let m = p.start();545 p.bump_assert(T!['(']);546547 loop {548 if p.at(T![')']) {549 p.bump();550 break;551 }552 param(p);553 if comma(p) {554 continue;555 }556 p.expect(T![')']);557 break;558 }559560 m.complete(p, PARAMS_DESC)561}562fn args_desc(p: &mut Parser) {563 let m = p.start();564 p.bump_assert(T!['(']);565566 let started_named = Cell::new(false);567 let mut unnamed_after_named = Vec::new();568569 loop {570 if p.at(T![')']) {571 break;572 }573574 let m = p.start();575 if p.at(IDENT) && p.nth_at(1, T![=]) {576 name(p);577 p.bump();578 expr(p);579 m.complete(p, ARG);580 started_named.set(true);581 } else {582 expr(p);583 let arg = m.complete(p, ARG);584 if started_named.get() {585 unnamed_after_named.push(arg);586 }587 }588 if comma(p) {589 continue;590 }591 break;592 }593 p.expect(T![')']);594 if p.at(T![tailstrict]) {595 p.bump();596 }597598 for errored in unnamed_after_named {599 errored.wrap_error(p, "can't use positional arguments after named", true);600 }601602 m.complete(p, ARGS_DESC);603}604605fn array(p: &mut Parser) -> CompletedMarker {606 // Start the list node607 let m = p.start();608 p.bump_assert(T!['[']);609610 let mut compspecs = Vec::new();611 let mut elems = 0;612613 loop {614 if p.at(T![']']) {615 p.bump();616 break;617 }618 if elems != 0 && p.at_ts(TS![for]) {619 while p.at_ts(COMPSPEC) {620 compspecs.push(compspec(p));621 }622 if comma(p) {623 continue;624 }625 p.expect(T![']']);626 break;627 }628 expr(p);629 elems += 1;630 while p.at_ts(COMPSPEC) {631 compspecs.push(compspec(p));632 }633 if comma(p) {634 continue;635 }636 p.expect(T![']']);637 break;638 }639640 if elems > 1 && !compspecs.is_empty() {641 for spec in compspecs {642 spec.wrap_error(643 p,644 "compspec may only be used if there is only one array element",645 true,646 );647 }648649 m.complete(p, EXPR_ARRAY)650 } else if !compspecs.is_empty() {651 m.complete(p, EXPR_ARRAY_COMP)652 } else {653 m.complete(p, EXPR_ARRAY)654 }655}656/// Returns true if it was slice, false if just index657#[must_use]658fn slice_desc_or_index(p: &mut Parser) -> bool {659 let m = p.start();660 p.bump();661 // Start662 if !p.at(T![:]) {663 expr(p);664 }665 if p.at(T![:]) {666 p.bump();667 // End668 if !p.at_ts(TS![']' :]) {669 expr(p).wrap(p, SLICE_DESC_END, true);670 }671 if p.at(T![:]) {672 p.bump();673 // Step674 if !p.at(T![']']) {675 expr(p).wrap(p, SLICE_DESC_STEP, true);676 }677 }678 } else {679 // It was not a slice680 p.expect(T![']']);681 m.forget(p);682 return false;683 }684 p.expect(T![']']);685 m.complete(p, SLICE_DESC);686 true687}688689fn suffix(p: &mut Parser) {690 loop {691 let start = p.start();692 let _marker: CompletedMarker = if p.at(T![?]) {693 p.bump();694 p.expect(T![.]);695 if p.at(IDENT) {696 name(p);697 start.complete(p, SUFFIX_INDEX)698 } else if p.at(T!['[']) {699 p.bump();700 expr(p);701 p.expect(T![']']);702 start.complete(p, SUFFIX_INDEX_EXPR)703 } else {704 start.complete_missing(p, ExpectedSyntax::Named("index"))705 }706 } else if p.at(T![.]) {707 p.bump();708 name(p);709 start.complete(p, SUFFIX_INDEX)710 } else if p.at(T!['[']) {711 if slice_desc_or_index(p) {712 start.complete(p, SUFFIX_SLICE)713 } else {714 start.complete(p, SUFFIX_INDEX_EXPR)715 }716 } else if p.at(T!['(']) {717 args_desc(p);718 start.complete(p, SUFFIX_APPLY)719 } else {720 start.forget(p);721 break;722 };723 }724}725726fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {727 let lhs = lhs_basic(p)?;728729 suffix(p);730731 Ok(lhs)732}733fn name(p: &mut Parser) {734 let m = p.start();735 p.expect(IDENT);736 m.complete(p, NAME);737}738fn destruct_rest(p: &mut Parser) {739 let m = p.start();740 p.bump_assert(T![...]);741 if p.at(IDENT) {742 p.bump();743 }744 m.complete(p, DESTRUCT_REST);745}746fn destruct_object_field(p: &mut Parser) {747 let m = p.start();748 name(p);749 if p.at(T![:]) {750 p.bump();751 destruct(p);752 }753 if p.at(T![=]) {754 p.bump();755 expr(p);756 }757 m.complete(p, DESTRUCT_OBJECT_FIELD);758}759fn obj_local(p: &mut Parser) {760 let m = p.start();761 p.bump_assert(T![local]);762 bind(p);763 m.complete(p, OBJ_LOCAL);764}765fn destruct(p: &mut Parser) -> CompletedMarker {766 let m = p.start();767 let _ex = p.expected_syntax_name("destruction specifier");768 if p.at(T![?]) {769 p.bump();770 m.complete(p, DESTRUCT_SKIP)771 } else if p.at(T!['[']) {772 p.bump();773 // let mut had_rest = false;774 loop {775 if p.at(T![']']) {776 p.bump();777 break;778 } else if p.at(T![...]) {779 // let m_err = p.start_ranger();780 destruct_rest(p);781 // if had_rest {782 // p.custom_error(m_err.finish(p), "only one rest can be present in array");783 // }784 // had_rest = true;785 } else {786 destruct(p);787 }788 if p.at(T![,]) {789 p.bump();790 continue;791 }792 p.expect(T![']']);793 break;794 }795 m.complete(p, DESTRUCT_ARRAY)796 } else if p.at(T!['{']) {797 p.bump();798 let mut had_rest = false;799 loop {800 if p.at(T!['}']) {801 p.bump();802 break;803 } else if p.at(T![...]) {804 // let m_err = p.start_ranger();805 destruct_rest(p);806 // if had_rest {807 // p.custom_error(m_err.finish(p), "only one rest can be present in object");808 // }809 had_rest = true;810 } else {811 if had_rest {812 p.error_with_recovery_set(TS![]);813 }814 destruct_object_field(p);815 }816 if p.at(T![,]) {817 p.bump();818 continue;819 }820 p.expect(T!['}']);821 break;822 }823 m.complete(p, DESTRUCT_OBJECT)824 } else if p.at(IDENT) {825 name(p);826 m.complete(p, DESTRUCT_FULL)827 } else {828 m.forget(p);829 p.error_with_recovery_set(TS![; , '}', '(', :])830 }831}832fn bind(p: &mut Parser) {833 let m = p.start();834 if p.at(IDENT) && p.nth_at(1, T!['(']) {835 name(p);836 params_desc(p);837 p.expect(T![=]);838 expr(p);839 m.complete(p, BIND_FUNCTION)840 } else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {841 name(p);842 p.expect(T![=]);843 p.expect(T![function]);844 params_desc(p);845 expr(p);846 m.complete(p, BIND_FUNCTION)847 } else {848 destruct(p);849 p.expect(T![=]);850 expr(p);851 m.complete(p, BIND_DESTRUCT)852 };853}854fn text(p: &mut Parser) {855 assert!(Text::can_cast(p.current()));856 p.bump();857}858fn number(p: &mut Parser) {859 assert!(Number::can_cast(p.current()));860 p.bump();861}862fn literal(p: &mut Parser) {863 assert!(Literal::can_cast(p.current()));864 p.bump();865}866fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {867 let _e = p.expected_syntax_name("expression");868 Ok(if Literal::can_cast(p.current()) {869 let m = p.start();870 literal(p);871 m.complete(p, EXPR_LITERAL)872 } else if Text::can_cast(p.current()) {873 let m = p.start();874 text(p);875 m.complete(p, EXPR_STRING)876 } else if Number::can_cast(p.current()) {877 let m = p.start();878 number(p);879 m.complete(p, EXPR_NUMBER)880 } else if p.at(IDENT) {881 let m = p.start();882 name(p);883 m.complete(p, EXPR_VAR)884 } else if p.at(T![if]) {885 let m = p.start();886 p.bump();887 expr(p);888 p.expect(T![then]);889 expr(p).wrap(p, TRUE_EXPR, true);890 if p.at(T![else]) {891 p.bump();892 expr(p).wrap(p, FALSE_EXPR, true);893 }894 m.complete(p, EXPR_IF_THEN_ELSE)895 } else if p.at(T!['[']) {896 array(p)897 } else if p.at(T!['{']) {898 object(p)899 } else if p.at(T![function]) {900 let m = p.start();901 p.bump();902 params_desc(p);903 expr(p);904 m.complete(p, EXPR_FUNCTION)905 } else if p.at(T![error]) {906 let m = p.start();907 p.bump();908 expr(p);909 m.complete(p, EXPR_ERROR)910 } else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {911 let m = p.start();912 p.bump();913 text(p);914 m.complete(p, EXPR_IMPORT)915 } else if let Some(op) = UnaryOperatorKind::cast(p.current()) {916 let ((), right_binding_power) = op.binding_power();917918 let m = p.start();919 p.bump();920 let _ = expr_binding_power(p, right_binding_power)921 .map(|v| v.precede(p).complete(p, EXPR));922 m.complete(p, EXPR_UNARY)923 } else if p.at(T!['(']) {924 let m = p.start();925 p.bump();926 expr(p);927 p.expect(T![')']);928 m.complete(p, EXPR_PARENED)929 } else {930 return Err(p.error_with_no_skip());931 })932}933934impl Parse {935 pub fn syntax(&self) -> SyntaxNode {936 SyntaxNode::new_root(self.green_node.clone())937 }938}crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_in_binop_rhs.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__local_in_binop_rhs.snap
@@ -0,0 +1,34 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "a + local x = 1; x\n"
+---
+SOURCE_FILE@0..19
+ EXPR@0..18
+ EXPR_BINARY@0..18
+ EXPR@0..1
+ EXPR_VAR@0..1
+ NAME@0..1
+ IDENT@0..1 "a"
+ WHITESPACE@1..2 " "
+ PLUS@2..3 "+"
+ WHITESPACE@3..4 " "
+ EXPR@4..18
+ STMT_LOCAL@4..16
+ LOCAL_KW@4..9 "local"
+ WHITESPACE@9..10 " "
+ BIND_DESTRUCT@10..15
+ DESTRUCT_FULL@10..11
+ NAME@10..11
+ IDENT@10..11 "x"
+ WHITESPACE@11..12 " "
+ ASSIGN@12..13 "="
+ WHITESPACE@13..14 " "
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "1"
+ SEMI@15..16 ";"
+ WHITESPACE@16..17 " "
+ EXPR_VAR@17..18
+ NAME@17..18
+ IDENT@17..18 "x"
+ WHITESPACE@18..19 "\n"
crates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/tests.rs
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -224,6 +224,10 @@
unary_not_in_call => r#"
std.assertEqual(!false, true)
"#
+
+ local_in_binop_rhs => r#"
+ a + local x = 1; x
+ "#
);
#[test]