git.delta.rocks / jrsonnet / refs/commits / 0b2a41fa24cc

difftreelog

fix(rowan) statements bind the tighest

nmxzmtonYaroslav Bolyukin2026-04-01parent: #6612618.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
before · crates/jrsonnet-rowan-parser/src/parser.rs
1use 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}
after · crates/jrsonnet-rowan-parser/src/parser.rs
1use 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}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		let m = p.start();368		p.bump();369		destruct(p);370		p.expect(T![in]);371		expr(p);372		m.complete(p, FOR_SPEC)373	} else if p.at(T![if]) {374		let m = p.start();375		p.bump();376		expr(p);377		m.complete(p, IF_SPEC)378	} else {379		unreachable!()380	}381}382383fn comma(p: &mut Parser) -> bool {384	comma_with_alternatives(p, TS![])385}386fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {387	if p.at(T![,]) {388		p.bump();389		true390	} else if p.at_ts(set) {391		let _ex = p.expected_syntax_name("comma");392		p.expect_with_recovery_set(T![,], TS![]);393		true394	} else {395		false396	}397}398399fn field_name(p: &mut Parser) {400	let _e = p.expected_syntax_name("field name");401	let m = p.start();402	if p.at(T!['[']) {403		p.bump();404		expr(p);405		p.expect(T![']']);406		m.complete(p, FIELD_NAME_DYNAMIC);407	} else if p.at(IDENT) {408		name(p);409		m.complete(p, FIELD_NAME_FIXED);410	} else if Text::can_cast(p.current()) {411		text(p);412		m.complete(p, FIELD_NAME_FIXED);413	} else {414		m.forget(p);415		// Recover with ::, :::416		p.error_with_recovery_set(TS![; : '(']);417	}418}419fn visibility(p: &mut Parser) {420	let m = p.start();421	if !p.at_ts(TS![:]) {422		p.error_with_recovery_set(TS![=]);423	}424	p.bump();425	'colons: {426		if !p.at_ts(TS![:]) {427			break 'colons;428		}429		p.bump();430		if !p.at_ts(TS![:]) {431			break 'colons;432		}433		p.bump();434	}435	m.complete(p, VISIBILITY);436}437fn assertion(p: &mut Parser) {438	let m = p.start();439	p.bump_assert(T![assert]);440	expr(p);441	if p.at(T![:]) {442		p.bump();443		expr(p);444	}445	m.complete(p, ASSERTION);446}447fn object(p: &mut Parser) -> CompletedMarker {448	let m_t = p.start();449	let m = p.start();450	p.bump_assert(T!['{']);451452	let mut elems = 0;453	let mut compspecs = Vec::new();454	let mut asserts = Vec::new();455	loop {456		if p.at(T!['}']) {457			p.bump();458			break;459		}460		if p.at_ts(TS![for]) {461			if elems == 0 {462				let m = p.start();463				m.complete_missing(p, ExpectedSyntax::Named("field definition"));464			}465			while p.at_ts(COMPSPEC) {466				compspecs.push(compspec(p));467			}468			if comma_with_alternatives(p, TS![;]) {469				continue;470			}471			p.expect(R_BRACE);472			break;473		}474		let m = p.start();475		if p.at(T![local]) {476			obj_local(p);477			m.complete(p, MEMBER_BIND_STMT);478		} else if p.at(T![assert]) {479			assertion(p);480			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));481		} else {482			field_name(p);483			if p.at(T![+]) {484				p.bump();485			}486			let params = if p.at(T!['(']) {487				params_desc(p);488				visibility(p);489				expr(p);490				true491			} else {492				visibility(p);493				if p.at(T![function]) {494					p.bump_assert(T![function]);495					params_desc(p);496					expr(p);497					true498				} else {499					expr(p);500					false501				}502			};503			elems += 1;504505			if params {506				m.complete(p, MEMBER_FIELD_METHOD)507			} else {508				m.complete(p, MEMBER_FIELD_NORMAL)509			};510		}511		while p.at_ts(COMPSPEC) {512			compspecs.push(compspec(p));513		}514		if comma_with_alternatives(p, TS![;]) {515			continue;516		}517		p.expect(R_BRACE);518		break;519	}520521	if elems > 1 && !compspecs.is_empty() {522		for errored in compspecs {523			errored.wrap_error(524				p,525				"compspec may only be used if there is only one object element",526				true,527			);528		}529		m.complete(p, OBJ_BODY_MEMBER_LIST);530	} else if !compspecs.is_empty() {531		for errored in asserts {532			errored.wrap_error(p, "asserts can't be used in object comprehensions", true);533		}534		m.complete(p, OBJ_BODY_COMP);535	} else {536		m.complete(p, OBJ_BODY_MEMBER_LIST);537	}538	m_t.complete(p, EXPR_OBJECT)539}540fn param(p: &mut Parser) {541	let m = p.start();542	destruct(p);543	if p.at(T![=]) {544		p.bump();545		expr(p);546	}547	m.complete(p, PARAM);548}549fn params_desc(p: &mut Parser) -> CompletedMarker {550	let m = p.start();551	p.bump_assert(T!['(']);552553	loop {554		if p.at(T![')']) {555			p.bump();556			break;557		}558		param(p);559		if comma(p) {560			continue;561		}562		p.expect(T![')']);563		break;564	}565566	m.complete(p, PARAMS_DESC)567}568fn args_desc(p: &mut Parser) {569	let m = p.start();570	p.bump_assert(T!['(']);571572	let started_named = Cell::new(false);573	let mut unnamed_after_named = Vec::new();574575	loop {576		if p.at(T![')']) {577			break;578		}579580		let m = p.start();581		if p.at(IDENT) && p.nth_at(1, T![=]) {582			name(p);583			p.bump();584			expr(p);585			m.complete(p, ARG);586			started_named.set(true);587		} else {588			expr(p);589			let arg = m.complete(p, ARG);590			if started_named.get() {591				unnamed_after_named.push(arg);592			}593		}594		if comma(p) {595			continue;596		}597		break;598	}599	p.expect(T![')']);600	if p.at(T![tailstrict]) {601		p.bump();602	}603604	for errored in unnamed_after_named {605		errored.wrap_error(p, "can't use positional arguments after named", true);606	}607608	m.complete(p, ARGS_DESC);609}610611fn array(p: &mut Parser) -> CompletedMarker {612	// Start the list node613	let m = p.start();614	p.bump_assert(T!['[']);615616	let mut compspecs = Vec::new();617	let mut elems = 0;618619	loop {620		if p.at(T![']']) {621			p.bump();622			break;623		}624		if elems != 0 && p.at_ts(TS![for]) {625			while p.at_ts(COMPSPEC) {626				compspecs.push(compspec(p));627			}628			if comma(p) {629				continue;630			}631			p.expect(T![']']);632			break;633		}634		expr(p);635		elems += 1;636		while p.at_ts(COMPSPEC) {637			compspecs.push(compspec(p));638		}639		if comma(p) {640			continue;641		}642		p.expect(T![']']);643		break;644	}645646	if elems > 1 && !compspecs.is_empty() {647		for spec in compspecs {648			spec.wrap_error(649				p,650				"compspec may only be used if there is only one array element",651				true,652			);653		}654655		m.complete(p, EXPR_ARRAY)656	} else if !compspecs.is_empty() {657		m.complete(p, EXPR_ARRAY_COMP)658	} else {659		m.complete(p, EXPR_ARRAY)660	}661}662/// Returns true if it was slice, false if just index663#[must_use]664fn slice_desc_or_index(p: &mut Parser) -> bool {665	let m = p.start();666	p.bump();667	// Start668	if !p.at(T![:]) {669		expr(p);670	}671	if p.at(T![:]) {672		p.bump();673		// End674		if !p.at_ts(TS![']' :]) {675			expr(p).wrap(p, SLICE_DESC_END, true);676		}677		if p.at(T![:]) {678			p.bump();679			// Step680			if !p.at(T![']']) {681				expr(p).wrap(p, SLICE_DESC_STEP, true);682			}683		}684	} else {685		// It was not a slice686		p.expect(T![']']);687		m.forget(p);688		return false;689	}690	p.expect(T![']']);691	m.complete(p, SLICE_DESC);692	true693}694695fn suffix(p: &mut Parser) {696	loop {697		let start = p.start();698		let _marker: CompletedMarker = if p.at(T![?]) {699			p.bump();700			p.expect(T![.]);701			if p.at(IDENT) {702				name(p);703				start.complete(p, SUFFIX_INDEX)704			} else if p.at(T!['[']) {705				p.bump();706				expr(p);707				p.expect(T![']']);708				start.complete(p, SUFFIX_INDEX_EXPR)709			} else {710				start.complete_missing(p, ExpectedSyntax::Named("index"))711			}712		} else if p.at(T![.]) {713			p.bump();714			name(p);715			start.complete(p, SUFFIX_INDEX)716		} else if p.at(T!['[']) {717			if slice_desc_or_index(p) {718				start.complete(p, SUFFIX_SLICE)719			} else {720				start.complete(p, SUFFIX_INDEX_EXPR)721			}722		} else if p.at(T!['(']) {723			args_desc(p);724			start.complete(p, SUFFIX_APPLY)725		} else {726			start.forget(p);727			break;728		};729	}730}731732fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {733	let lhs = lhs_basic(p)?;734735	suffix(p);736737	Ok(lhs)738}739fn name(p: &mut Parser) {740	let m = p.start();741	p.expect(IDENT);742	m.complete(p, NAME);743}744fn destruct_rest(p: &mut Parser) {745	let m = p.start();746	p.bump_assert(T![...]);747	if p.at(IDENT) {748		p.bump();749	}750	m.complete(p, DESTRUCT_REST);751}752fn destruct_object_field(p: &mut Parser) {753	let m = p.start();754	name(p);755	if p.at(T![:]) {756		p.bump();757		destruct(p);758	}759	if p.at(T![=]) {760		p.bump();761		expr(p);762	}763	m.complete(p, DESTRUCT_OBJECT_FIELD);764}765fn obj_local(p: &mut Parser) {766	let m = p.start();767	p.bump_assert(T![local]);768	bind(p);769	m.complete(p, OBJ_LOCAL);770}771fn destruct(p: &mut Parser) -> CompletedMarker {772	let m = p.start();773	let _ex = p.expected_syntax_name("destruction specifier");774	if p.at(T![?]) {775		p.bump();776		m.complete(p, DESTRUCT_SKIP)777	} else if p.at(T!['[']) {778		p.bump();779		// let mut had_rest = false;780		loop {781			if p.at(T![']']) {782				p.bump();783				break;784			} else if p.at(T![...]) {785				// let m_err = p.start_ranger();786				destruct_rest(p);787			// if had_rest {788			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");789			// }790			// had_rest = true;791			} else {792				destruct(p);793			}794			if p.at(T![,]) {795				p.bump();796				continue;797			}798			p.expect(T![']']);799			break;800		}801		m.complete(p, DESTRUCT_ARRAY)802	} else if p.at(T!['{']) {803		p.bump();804		let mut had_rest = false;805		loop {806			if p.at(T!['}']) {807				p.bump();808				break;809			} else if p.at(T![...]) {810				// let m_err = p.start_ranger();811				destruct_rest(p);812				// if had_rest {813				// 	p.custom_error(m_err.finish(p), "only one rest can be present in object");814				// }815				had_rest = true;816			} else {817				if had_rest {818					p.error_with_recovery_set(TS![]);819				}820				destruct_object_field(p);821			}822			if p.at(T![,]) {823				p.bump();824				continue;825			}826			p.expect(T!['}']);827			break;828		}829		m.complete(p, DESTRUCT_OBJECT)830	} else if p.at(IDENT) {831		name(p);832		m.complete(p, DESTRUCT_FULL)833	} else {834		m.forget(p);835		p.error_with_recovery_set(TS![; , '}', '(', :])836	}837}838fn bind(p: &mut Parser) {839	let m = p.start();840	if p.at(IDENT) && p.nth_at(1, T!['(']) {841		name(p);842		params_desc(p);843		p.expect(T![=]);844		expr(p);845		m.complete(p, BIND_FUNCTION)846	} else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {847		name(p);848		p.expect(T![=]);849		p.expect(T![function]);850		params_desc(p);851		expr(p);852		m.complete(p, BIND_FUNCTION)853	} else {854		destruct(p);855		p.expect(T![=]);856		expr(p);857		m.complete(p, BIND_DESTRUCT)858	};859}860fn text(p: &mut Parser) {861	assert!(Text::can_cast(p.current()));862	p.bump();863}864fn number(p: &mut Parser) {865	assert!(Number::can_cast(p.current()));866	p.bump();867}868fn literal(p: &mut Parser) {869	assert!(Literal::can_cast(p.current()));870	p.bump();871}872fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {873	let _e = p.expected_syntax_name("expression");874	Ok(if Literal::can_cast(p.current()) {875		let m = p.start();876		literal(p);877		m.complete(p, EXPR_LITERAL)878	} else if Text::can_cast(p.current()) {879		let m = p.start();880		text(p);881		m.complete(p, EXPR_STRING)882	} else if Number::can_cast(p.current()) {883		let m = p.start();884		number(p);885		m.complete(p, EXPR_NUMBER)886	} else if p.at(IDENT) {887		let m = p.start();888		name(p);889		m.complete(p, EXPR_VAR)890	} else if p.at(T![if]) {891		let m = p.start();892		p.bump();893		expr(p);894		p.expect(T![then]);895		expr(p).wrap(p, TRUE_EXPR, true);896		if p.at(T![else]) {897			p.bump();898			expr(p).wrap(p, FALSE_EXPR, true);899		}900		m.complete(p, EXPR_IF_THEN_ELSE)901	} else if p.at(T!['[']) {902		array(p)903	} else if p.at(T!['{']) {904		object(p)905	} else if p.at(T![function]) {906		let m = p.start();907		p.bump();908		params_desc(p);909		expr(p);910		m.complete(p, EXPR_FUNCTION)911	} else if p.at(T![error]) {912		let m = p.start();913		p.bump();914		expr(p);915		m.complete(p, EXPR_ERROR)916	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {917		let m = p.start();918		p.bump();919		text(p);920		m.complete(p, EXPR_IMPORT)921	} else if let Some(op) = UnaryOperatorKind::cast(p.current()) {922		let ((), right_binding_power) = op.binding_power();923924		let m = p.start();925		p.bump();926		let _ = expr_binding_power(p, right_binding_power)927			.map(|v| v.precede(p).complete(p, EXPR));928		m.complete(p, EXPR_UNARY)929	} else if p.at(T!['(']) {930		let m = p.start();931		p.bump();932		expr(p);933		p.expect(T![')']);934		m.complete(p, EXPR_PARENED)935	} else {936		return Err(p.error_with_no_skip());937	})938}939940impl Parse {941	pub fn syntax(&self) -> SyntaxNode {942		SyntaxNode::new_root(self.green_node.clone())943	}944}
addedcrates/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"
modifiedcrates/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]