ezno_parser/
block.rs

1use derive_enum_from_into::EnumFrom;
2use iterator_endiate::EndiateIteratorExt;
3use tokenizer_lib::{sized_tokens::TokenReaderWithTokenEnds, Token};
4use visitable_derive::Visitable;
5
6use super::{ASTNode, Span, TSXToken, TokenReader};
7use crate::{
8	declarations::{export::Exportable, ExportDeclaration},
9	derive_ASTNode, expect_semi_colon,
10	marker::MARKER,
11	Declaration, Decorated, Marker, ParseOptions, ParseResult, Statement, TSXKeyword, VisitOptions,
12	Visitable,
13};
14
15#[apply(derive_ASTNode)]
16#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType, EnumFrom)]
17#[get_field_by_type_target(Span)]
18#[visit_self(under = statement)]
19pub enum StatementOrDeclaration {
20	Statement(Statement),
21	Declaration(Declaration),
22	/// For bundling,
23	Imported {
24		moved: Box<StatementOrDeclaration>,
25		/// from the import statement
26		originally: Span,
27		from: source_map::SourceId,
28	},
29	/// TODO under cfg
30	#[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(0))]
31	Marker(#[visit_skip_field] Marker<Statement>, Span),
32}
33
34impl StatementOrDeclaration {
35	pub(crate) fn requires_semi_colon(&self) -> bool {
36		// TODO maybe more?
37		match self {
38			Self::Statement(stmt) => stmt.requires_semi_colon(),
39			Self::Declaration(dec) => matches!(
40				dec,
41				Declaration::Variable(..)
42					| Declaration::Export(Decorated {
43						on: ExportDeclaration::Default { .. }
44							| ExportDeclaration::Item {
45								exported: Exportable::ImportAll { .. }
46									| Exportable::ImportParts { .. }
47									| Exportable::Parts { .. },
48								..
49							},
50						..
51					}) | Declaration::Import(..)
52					| Declaration::TypeAlias(..)
53			),
54			Self::Imported { moved, .. } => moved.requires_semi_colon(),
55			Self::Marker(..) => false,
56		}
57	}
58}
59
60impl ASTNode for StatementOrDeclaration {
61	fn get_position(&self) -> Span {
62		*get_field_by_type::GetFieldByType::get(self)
63	}
64
65	fn from_reader(
66		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
67		state: &mut crate::ParsingState,
68		options: &ParseOptions,
69	) -> ParseResult<Self> {
70		// Exclusively for generator
71		if options.interpolation_points
72			&& matches!(reader.peek(), Some(Token(TSXToken::Identifier(i), _)) if i == MARKER)
73		{
74			let Token(_, position) = reader.next().unwrap();
75			let marker_id = state.new_partial_point_marker(position);
76			return Ok(Self::Marker(marker_id, position.with_length(0)));
77		}
78
79		if Declaration::is_declaration_start(reader, options) {
80			let dec = Declaration::from_reader(reader, state, options)?;
81			// TODO nested blocks? Interfaces...?
82			Ok(StatementOrDeclaration::Declaration(dec))
83		} else {
84			if let Some(Token(TSXToken::Keyword(TSXKeyword::Enum | TSXKeyword::Type), _)) =
85				reader.peek()
86			{
87				if reader.peek_n(1).map_or(false, |t| !t.0.is_symbol()) {
88					return Ok(StatementOrDeclaration::Declaration(Declaration::from_reader(
89						reader, state, options,
90					)?));
91				}
92			}
93
94			let stmt = Statement::from_reader(reader, state, options)?;
95			Ok(StatementOrDeclaration::Statement(stmt))
96		}
97	}
98
99	fn to_string_from_buffer<T: source_map::ToString>(
100		&self,
101		buf: &mut T,
102		options: &crate::ToStringOptions,
103		local: crate::LocalToStringInformation,
104	) {
105		match self {
106			StatementOrDeclaration::Statement(item) => {
107				item.to_string_from_buffer(buf, options, local);
108			}
109			StatementOrDeclaration::Declaration(item) => {
110				item.to_string_from_buffer(buf, options, local);
111			}
112			StatementOrDeclaration::Marker(_, _) => {
113				assert!(options.expect_markers, "Unexpected marker in AST");
114			}
115			StatementOrDeclaration::Imported { moved, from, originally: _ } => {
116				moved.to_string_from_buffer(buf, options, local.change_source(*from));
117			}
118		}
119	}
120}
121
122/// A "block" of braced statements and declarations
123#[apply(derive_ASTNode)]
124#[derive(Debug, Clone, get_field_by_type::GetFieldByType)]
125#[get_field_by_type_target(Span)]
126pub struct Block(pub Vec<StatementOrDeclaration>, pub Span);
127
128impl Eq for Block {}
129
130impl PartialEq for Block {
131	fn eq(&self, other: &Self) -> bool {
132		self.0 == other.0
133	}
134}
135
136pub struct BlockLike<'a> {
137	pub items: &'a Vec<StatementOrDeclaration>,
138}
139
140pub struct BlockLikeMut<'a> {
141	pub items: &'a mut Vec<StatementOrDeclaration>,
142}
143
144impl<'a> From<&'a Block> for BlockLike<'a> {
145	fn from(block: &'a Block) -> Self {
146		BlockLike { items: &block.0 }
147	}
148}
149
150impl<'a> From<&'a mut Block> for BlockLikeMut<'a> {
151	fn from(block: &'a mut Block) -> Self {
152		BlockLikeMut { items: &mut block.0 }
153	}
154}
155
156impl ASTNode for Block {
157	fn from_reader(
158		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
159		state: &mut crate::ParsingState,
160		options: &ParseOptions,
161	) -> ParseResult<Self> {
162		let start = reader.expect_next(TSXToken::OpenBrace)?;
163		let items = parse_statements_and_declarations(reader, state, options)?;
164		let end_span = reader.expect_next_get_end(TSXToken::CloseBrace)?;
165		Ok(Self(items, start.union(end_span)))
166	}
167
168	fn to_string_from_buffer<T: source_map::ToString>(
169		&self,
170		buf: &mut T,
171		options: &crate::ToStringOptions,
172		local: crate::LocalToStringInformation,
173	) {
174		if options.pretty && self.0.is_empty() {
175			buf.push_str("{}");
176		} else {
177			buf.push('{');
178			if local.depth > 0 && options.pretty {
179				buf.push_new_line();
180			}
181			statements_and_declarations_to_string(&self.0, buf, options, local);
182			if options.pretty && !self.0.is_empty() {
183				buf.push_new_line();
184			}
185			if local.depth > 1 {
186				options.add_indent(local.depth - 1, buf);
187			}
188			buf.push('}');
189		}
190	}
191
192	fn get_position(&self) -> Span {
193		self.1
194	}
195}
196
197impl Block {
198	pub fn items(&self) -> core::slice::Iter<'_, StatementOrDeclaration> {
199		self.0.iter()
200	}
201
202	pub fn items_mut(&mut self) -> core::slice::IterMut<'_, StatementOrDeclaration> {
203		self.0.iter_mut()
204	}
205}
206
207impl Visitable for Block {
208	fn visit<TData>(
209		&self,
210		visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
211		data: &mut TData,
212		options: &VisitOptions,
213		chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
214	) {
215		if options.visit_nested_blocks || chain.is_empty() {
216			{
217				visitors.visit_block(&crate::block::BlockLike { items: &self.0 }, data, chain);
218			}
219			let items = self.items();
220			if options.reverse_statements {
221				items.rev().for_each(|item| item.visit(visitors, data, options, chain));
222			} else {
223				items.for_each(|item| item.visit(visitors, data, options, chain));
224			}
225		}
226	}
227
228	fn visit_mut<TData>(
229		&mut self,
230		visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
231		data: &mut TData,
232		options: &VisitOptions,
233		chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
234	) {
235		if options.visit_nested_blocks || chain.is_empty() {
236			{
237				visitors.visit_block_mut(
238					&mut crate::block::BlockLikeMut { items: &mut self.0 },
239					data,
240					chain,
241				);
242			}
243			let items = self.items_mut();
244			if options.reverse_statements {
245				items.for_each(|statement| statement.visit_mut(visitors, data, options, chain));
246			} else {
247				items
248					.rev()
249					.for_each(|statement| statement.visit_mut(visitors, data, options, chain));
250			}
251		}
252	}
253}
254
255/// For ifs and other statements
256#[derive(Debug, Clone, PartialEq, EnumFrom)]
257#[apply(derive_ASTNode!)]
258pub enum BlockOrSingleStatement {
259	Braced(Block),
260	SingleStatement(Box<Statement>),
261}
262
263impl Visitable for BlockOrSingleStatement {
264	fn visit<TData>(
265		&self,
266		visitors: &mut (impl crate::visiting::VisitorReceiver<TData> + ?Sized),
267		data: &mut TData,
268		options: &VisitOptions,
269		chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
270	) {
271		match self {
272			BlockOrSingleStatement::Braced(b) => {
273				b.visit(visitors, data, options, chain);
274			}
275			BlockOrSingleStatement::SingleStatement(s) => {
276				s.visit(visitors, data, options, chain);
277				visitors.visit_statement(
278					crate::visiting::BlockItem::SingleStatement(s),
279					data,
280					chain,
281				);
282			}
283		}
284	}
285
286	fn visit_mut<TData>(
287		&mut self,
288		visitors: &mut (impl crate::visiting::VisitorMutReceiver<TData> + ?Sized),
289		data: &mut TData,
290		options: &VisitOptions,
291		chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
292	) {
293		match self {
294			BlockOrSingleStatement::Braced(ref mut b) => {
295				b.visit_mut(visitors, data, options, chain);
296			}
297			BlockOrSingleStatement::SingleStatement(ref mut s) => {
298				s.visit_mut(visitors, data, options, chain);
299				visitors.visit_statement_mut(
300					crate::visiting::BlockItemMut::SingleStatement(s),
301					data,
302					chain,
303				);
304			}
305		}
306	}
307}
308
309impl From<Statement> for BlockOrSingleStatement {
310	fn from(stmt: Statement) -> Self {
311		Self::SingleStatement(Box::new(stmt))
312	}
313}
314
315impl ASTNode for BlockOrSingleStatement {
316	fn get_position(&self) -> Span {
317		match self {
318			BlockOrSingleStatement::Braced(blk) => blk.get_position(),
319			BlockOrSingleStatement::SingleStatement(stmt) => stmt.get_position(),
320		}
321	}
322
323	fn from_reader(
324		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
325		state: &mut crate::ParsingState,
326		options: &ParseOptions,
327	) -> ParseResult<Self> {
328		let stmt = Statement::from_reader(reader, state, options)?;
329		Ok(match stmt {
330			Statement::Block(blk) => Self::Braced(blk),
331			stmt => {
332				if stmt.requires_semi_colon() {
333					let _ = expect_semi_colon(
334						reader,
335						&state.line_starts,
336						stmt.get_position().end,
337						options,
338					)?;
339				}
340				Box::new(stmt).into()
341			}
342		})
343	}
344
345	fn to_string_from_buffer<T: source_map::ToString>(
346		&self,
347		buf: &mut T,
348		options: &crate::ToStringOptions,
349		local: crate::LocalToStringInformation,
350	) {
351		if buf.should_halt() {
352			return;
353		}
354		match self {
355			BlockOrSingleStatement::Braced(block) => {
356				block.to_string_from_buffer(buf, options, local);
357			}
358			BlockOrSingleStatement::SingleStatement(statement) => {
359				if let Statement::Empty(..) = &**statement {
360					buf.push(';');
361				} else if options.pretty && !options.single_statement_on_new_line {
362					buf.push_new_line();
363					options.push_gap_optionally(buf);
364					statement.to_string_from_buffer(buf, options, local.next_level());
365				} else {
366					statement.to_string_from_buffer(buf, options, local);
367					if statement.requires_semi_colon() {
368						buf.push(';');
369					}
370				}
371			}
372		}
373	}
374}
375
376/// Parse statements, regardless of bracing or not
377pub(crate) fn parse_statements_and_declarations(
378	reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
379	state: &mut crate::ParsingState,
380	options: &ParseOptions,
381) -> ParseResult<Vec<StatementOrDeclaration>> {
382	let mut items = Vec::new();
383	while let Some(Token(token_type, _)) = reader.peek() {
384		if let TSXToken::EOS | TSXToken::CloseBrace = token_type {
385			break;
386		}
387
388		let item = StatementOrDeclaration::from_reader(reader, state, options)?;
389		let requires_semi_colon = item.requires_semi_colon();
390		let end = item.get_position().end;
391
392		let blank_lines_after_statement = if requires_semi_colon {
393			expect_semi_colon(reader, &state.line_starts, end, options)?
394		} else if options.retain_blank_lines {
395			let Token(kind, next) = reader.peek().ok_or_else(crate::parse_lexing_error)?;
396			if let TSXToken::EOS = kind {
397				1
398			} else {
399				let lines =
400					state.line_starts.byte_indexes_crosses_lines(end as usize, next.0 as usize);
401				lines.saturating_sub(1)
402			}
403		} else {
404			0
405		};
406
407		if let (true, StatementOrDeclaration::Statement(Statement::Empty(..))) =
408			(items.is_empty(), &item)
409		{
410			continue;
411		}
412		items.push(item);
413		for _ in 0..blank_lines_after_statement {
414			// TODO span
415			let span = Span { start: end, end, source: () };
416			items.push(StatementOrDeclaration::Statement(Statement::Empty(span)));
417		}
418	}
419	Ok(items)
420}
421
422pub fn statements_and_declarations_to_string<T: source_map::ToString>(
423	items: &[StatementOrDeclaration],
424	buf: &mut T,
425	options: &crate::ToStringOptions,
426	local: crate::LocalToStringInformation,
427) {
428	let mut last_was_empty = false;
429	for (at_end, item) in items.iter().endiate() {
430		if !options.pretty {
431			if let StatementOrDeclaration::Statement(Statement::Expression(
432				crate::expressions::MultipleExpression::Single(crate::Expression::Null(..)),
433			)) = item
434			{
435				continue;
436			}
437		}
438
439		if options.pretty {
440			// Don't print more than two lines in a row
441			if let StatementOrDeclaration::Statement(
442				Statement::AestheticSemiColon(_) | Statement::Empty(_),
443			) = item
444			{
445				if last_was_empty {
446					continue;
447				}
448				last_was_empty = true;
449			} else {
450				last_was_empty = false;
451			}
452		}
453
454		if let (false, StatementOrDeclaration::Declaration(dec)) =
455			(options.include_type_annotations, item)
456		{
457			match dec {
458				Declaration::Function(item) if item.on.name.is_declare => {
459					continue;
460				}
461				Declaration::Class(item) if item.on.name.is_declare => {
462					continue;
463				}
464				_ => {}
465			}
466		}
467
468		options.add_indent(local.depth, buf);
469		item.to_string_from_buffer(buf, options, local);
470		if (!at_end || options.trailing_semicolon) && item.requires_semi_colon() {
471			buf.push(';');
472		}
473		// TODO only append new line if something added
474		if !at_end && options.pretty {
475			buf.push_new_line();
476		}
477	}
478}