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 Imported {
24 moved: Box<StatementOrDeclaration>,
25 originally: Span,
27 from: source_map::SourceId,
28 },
29 #[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 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 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 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#[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#[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
376pub(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 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 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 if !at_end && options.pretty {
475 buf.push_new_line();
476 }
477 }
478}