1mod for_statement;
2mod if_statement;
3mod switch_statement;
4mod try_catch_statement;
5mod while_statement;
6
7use crate::{
8 declarations::variable::{declarations_to_string, VariableDeclarationItem},
9 derive_ASTNode,
10 tokens::token_as_identifier,
11 ParseError, ParseErrors,
12};
13use derive_enum_from_into::{EnumFrom, EnumTryInto};
14use derive_partial_eq_extras::PartialEqExtras;
15use get_field_by_type::GetFieldByType;
16use std::fmt::Debug;
17
18use super::{
19 expressions::MultipleExpression, ASTNode, Block, Expression, ParseOptions, ParseResult, Span,
20 TSXKeyword, TSXToken, Token, TokenReader,
21};
22use crate::errors::parse_lexing_error;
23pub use for_statement::{ForLoopCondition, ForLoopStatement, ForLoopStatementInitialiser};
24pub use if_statement::*;
25pub use switch_statement::{SwitchBranch, SwitchStatement};
26pub use try_catch_statement::TryCatchStatement;
27use visitable_derive::Visitable;
28pub use while_statement::{DoWhileStatement, WhileStatement};
29
30#[apply(derive_ASTNode)]
32#[derive(Debug, Clone, Visitable, EnumFrom, EnumTryInto, PartialEqExtras, GetFieldByType)]
33#[get_field_by_type_target(Span)]
34#[try_into_references(&, &mut)]
35#[partial_eq_ignore_types(Span)]
36pub enum Statement {
37 Expression(MultipleExpression),
38 Block(Block),
40 Debugger(Span),
41 If(IfStatement),
43 ForLoop(ForLoopStatement),
44 Switch(SwitchStatement),
45 WhileLoop(WhileStatement),
46 DoWhileLoop(DoWhileStatement),
47 TryCatch(TryCatchStatement),
48 Return(ReturnStatement),
50 Continue(Option<String>, Span),
52 Break(Option<String>, Span),
54 Throw(ThrowStatement),
56 Comment(String, Span),
58 MultiLineComment(String, Span),
59 Labelled {
60 position: Span,
61 name: String,
62 statement: Box<Statement>,
63 },
64 VarVariable(VarVariableStatement),
65 Empty(Span),
66 AestheticSemiColon(Span),
68}
69
70#[apply(derive_ASTNode)]
71#[derive(Debug, Clone, Visitable, PartialEqExtras, GetFieldByType)]
72#[get_field_by_type_target(Span)]
73pub struct ReturnStatement(pub Option<MultipleExpression>, pub Span);
74
75#[apply(derive_ASTNode)]
76#[derive(Debug, Clone, Visitable, PartialEqExtras, GetFieldByType)]
77#[get_field_by_type_target(Span)]
78pub struct ThrowStatement(pub Box<MultipleExpression>, pub Span);
79
80impl Eq for Statement {}
81
82impl ASTNode for Statement {
83 fn get_position(&self) -> Span {
84 *get_field_by_type::GetFieldByType::get(self)
85 }
86
87 fn from_reader(
88 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
89 state: &mut crate::ParsingState,
90 options: &ParseOptions,
91 ) -> ParseResult<Self> {
92 if let Some(Token(TSXToken::Colon, _)) = reader.peek_n(1) {
94 let (name, label_name_pos) = token_as_identifier(reader.next().unwrap(), "label name")?;
95 let _colon = reader.next().unwrap();
96 let statement = Statement::from_reader(reader, state, options).map(Box::new)?;
97 if statement.requires_semi_colon() {
98 let _ = crate::expect_semi_colon(
99 reader,
100 &state.line_starts,
101 statement.get_position().start,
102 options,
103 )?;
104 }
105 let position = label_name_pos.union(statement.get_position());
107 return Ok(Statement::Labelled { name, statement, position });
108 }
109
110 let Token(token, _s) = &reader.peek().ok_or_else(parse_lexing_error)?;
111
112 match token {
113 TSXToken::Keyword(TSXKeyword::Var) => {
114 let stmt = VarVariableStatement::from_reader(reader, state, options)?;
115 Ok(Statement::VarVariable(stmt))
116 }
117 TSXToken::Keyword(TSXKeyword::Throw) => {
118 let Token(_, start) = reader.next().unwrap();
119 let expression = MultipleExpression::from_reader(reader, state, options)?;
120 let position = start.union(expression.get_position());
121 Ok(Statement::Throw(ThrowStatement(Box::new(expression), position)))
122 }
123 TSXToken::Keyword(TSXKeyword::If) => {
124 IfStatement::from_reader(reader, state, options).map(Into::into)
125 }
126 TSXToken::Keyword(TSXKeyword::For) => {
127 ForLoopStatement::from_reader(reader, state, options).map(Into::into)
128 }
129 TSXToken::Keyword(TSXKeyword::Switch) => {
130 SwitchStatement::from_reader(reader, state, options).map(Into::into)
131 }
132 TSXToken::Keyword(TSXKeyword::While) => {
133 WhileStatement::from_reader(reader, state, options).map(Into::into)
134 }
135 TSXToken::Keyword(TSXKeyword::Do) => {
136 DoWhileStatement::from_reader(reader, state, options).map(Into::into)
137 }
138 TSXToken::Keyword(TSXKeyword::Try) => {
139 TryCatchStatement::from_reader(reader, state, options).map(Into::into)
140 }
141 TSXToken::OpenBrace => Block::from_reader(reader, state, options).map(Statement::Block),
142 TSXToken::Keyword(TSXKeyword::Debugger) => {
143 Ok(Statement::Debugger(reader.next().unwrap().get_span()))
144 }
145 TSXToken::Keyword(TSXKeyword::Return) => Ok({
146 let Token(_, start) = reader.next().unwrap();
147 state.append_keyword_at_pos(start.0, TSXKeyword::Return);
148 let next = reader.peek().ok_or_else(parse_lexing_error)?;
149 if on_different_lines_or_line_end(&state.line_starts, start, next) {
150 let position = start.with_length(TSXKeyword::Return.length() as usize);
151 Statement::Return(ReturnStatement(None, position))
152 } else {
153 let multiple_expression =
154 MultipleExpression::from_reader(reader, state, options)?;
155 let position = start.union(multiple_expression.get_position());
156 Statement::Return(ReturnStatement(Some(multiple_expression), position))
157 }
158 }),
159 TSXToken::Keyword(TSXKeyword::Break) => {
160 let Token(_break_token, start) = reader.next().unwrap();
161 state.append_keyword_at_pos(start.0, TSXKeyword::Break);
162 let next = reader.peek().ok_or_else(parse_lexing_error)?;
163 if on_different_lines_or_line_end(&state.line_starts, start, next) {
164 Ok(Statement::Break(
165 None,
166 start.with_length(TSXKeyword::Break.length() as usize),
167 ))
168 } else {
169 let (label, position) =
170 token_as_identifier(reader.next().unwrap(), "break label")?;
171 Ok(Statement::Break(Some(label), start.union(position)))
172 }
173 }
174 TSXToken::Keyword(TSXKeyword::Continue) => {
175 let Token(_continue_token, start) = reader.next().unwrap();
176 state.append_keyword_at_pos(start.0, TSXKeyword::Continue);
177 let next = reader.peek().ok_or_else(parse_lexing_error)?;
178 if on_different_lines_or_line_end(&state.line_starts, start, next) {
179 Ok(Statement::Continue(
180 None,
181 start.with_length(TSXKeyword::Continue.length() as usize),
182 ))
183 } else {
184 let (label, position) =
185 token_as_identifier(reader.next().unwrap(), "continue label")?;
186 Ok(Statement::Continue(Some(label), start.union(position)))
187 }
188 }
189 TSXToken::Comment(_) => {
190 if let Token(TSXToken::Comment(comment), start) = reader.next().unwrap() {
191 let position = start.with_length(comment.len() + 2);
192 Ok(Statement::Comment(comment, position))
193 } else {
194 unreachable!()
195 }
196 }
197 TSXToken::MultiLineComment(_) => {
198 if let Token(TSXToken::MultiLineComment(comment), start) = reader.next().unwrap() {
199 let position = start.with_length(comment.len() + 2);
200 Ok(Statement::MultiLineComment(comment, position))
201 } else {
202 unreachable!()
203 }
204 }
205 TSXToken::SemiColon => {
206 Ok(Statement::AestheticSemiColon(reader.next().unwrap().get_span()))
207 }
208 TSXToken::EOS => {
209 reader.next();
210 Ok(Statement::Empty(Span { start: 0, end: 0, source: () }))
211 }
212 _ => {
214 let expr = MultipleExpression::from_reader(reader, state, options)?;
215 if let (true, Expression::Marker { .. }) = (options.partial_syntax, expr.get_rhs())
216 {
217 Err(ParseError::new(
218 ParseErrors::ExpectedIdentifier,
219 reader.next().unwrap().get_span(),
220 ))
221 } else {
222 Ok(Statement::Expression(expr))
223 }
224 }
225 }
226 }
227
228 fn to_string_from_buffer<T: source_map::ToString>(
229 &self,
230 buf: &mut T,
231 options: &crate::ToStringOptions,
232 local: crate::LocalToStringInformation,
233 ) {
234 match self {
235 Statement::Empty(..) => {}
236 Statement::AestheticSemiColon(..) => buf.push(';'),
237 Statement::Return(ReturnStatement(expression, _)) => {
238 buf.push_str("return");
239 if let Some(expression) = expression {
240 buf.push(' ');
241 expression.to_string_from_buffer(buf, options, local);
242 }
243 }
244 Statement::If(is) => is.to_string_from_buffer(buf, options, local),
245 Statement::ForLoop(fl) => fl.to_string_from_buffer(buf, options, local),
246 Statement::Switch(ss) => ss.to_string_from_buffer(buf, options, local),
247 Statement::WhileLoop(ws) => ws.to_string_from_buffer(buf, options, local),
248 Statement::DoWhileLoop(dws) => dws.to_string_from_buffer(buf, options, local),
249 Statement::TryCatch(tcs) => tcs.to_string_from_buffer(buf, options, local),
250 Statement::Comment(comment, _) => {
251 if options.should_add_comment(comment.as_str()) {
252 buf.push_str("//");
253 buf.push_str_contains_new_line(comment.as_str().trim_end());
254 }
255 }
256 Statement::MultiLineComment(comment, _) => {
257 if options.should_add_comment(comment) {
258 buf.push_str("/*");
259 if options.pretty {
260 for (idx, line) in comment.split('\n').enumerate() {
262 if idx > 0 {
263 buf.push_new_line();
264 }
265 options.add_indent(local.depth, buf);
266 buf.push_str(line.trim());
267 }
268 } else {
269 buf.push_str_contains_new_line(comment.as_str());
270 }
271 buf.push_str("*/");
272 }
273 }
274 Statement::Block(block) => {
275 block.to_string_from_buffer(buf, options, local.next_level());
276 }
277 Statement::Debugger(_) => buf.push_str("debugger"),
278 Statement::Continue(label, _) => {
279 buf.push_str("continue");
280 if let Some(label) = label {
281 buf.push(' ');
282 buf.push_str(label);
283 }
284 }
285 Statement::Break(label, _) => {
286 buf.push_str("break");
287 if let Some(label) = label {
288 buf.push(' ');
289 buf.push_str(label);
290 }
291 }
292 Statement::Expression(val) => {
293 val.to_string_on_left(buf, options, local);
294 }
295 Statement::Labelled { name, statement, .. } => {
296 buf.push_str(name);
297 buf.push_str(": ");
298
299 if let Statement::Empty(..) = &**statement {
300 buf.push(';');
301 } else {
302 statement.to_string_from_buffer(buf, options, local);
304 if statement.requires_semi_colon() {
305 buf.push(';');
306 }
307 }
308 }
309 Statement::Throw(ThrowStatement(thrown_expression, _)) => {
310 buf.push_str("throw ");
311 thrown_expression.to_string_from_buffer(buf, options, local);
312 }
313 Statement::VarVariable(var_stmt) => var_stmt.to_string_from_buffer(buf, options, local),
314 }
315 }
316}
317
318impl Statement {
319 #[must_use]
321 pub fn is_comment(&self) -> bool {
322 matches!(self, Statement::Comment(..) | Statement::MultiLineComment(..))
323 }
324
325 pub(crate) fn requires_semi_colon(&self) -> bool {
326 matches!(
327 self,
328 Statement::VarVariable(_)
329 | Statement::Expression(_)
330 | Statement::DoWhileLoop(_)
331 | Statement::Continue(..)
332 | Statement::Break(..)
333 | Statement::Return(..)
334 | Statement::Throw(..)
335 )
336 }
337}
338
339#[apply(derive_ASTNode)]
340#[derive(Debug, PartialEq, Clone, Visitable, get_field_by_type::GetFieldByType)]
341#[get_field_by_type_target(Span)]
342pub struct VarVariableStatement {
343 pub declarations: Vec<VariableDeclarationItem<Option<Expression>>>,
344 pub position: Span,
345}
346
347impl ASTNode for VarVariableStatement {
348 fn get_position(&self) -> Span {
349 self.position
350 }
351
352 fn from_reader(
353 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
354 state: &mut crate::ParsingState,
355 options: &ParseOptions,
356 ) -> ParseResult<Self> {
357 let Token(_, start) = reader.next().unwrap();
358 let mut declarations = Vec::new();
359 loop {
360 let value =
361 VariableDeclarationItem::<Option<Expression>>::from_reader(reader, state, options)?;
362 if value.expression.is_none()
363 && !matches!(value.name.get_ast_ref(), crate::VariableField::Name(_))
364 {
365 return Err(crate::ParseError::new(
366 crate::ParseErrors::DestructuringRequiresValue,
367 value.name.get_ast_ref().get_position(),
368 ));
369 }
370 declarations.push(value);
371 if let Some(Token(TSXToken::Comma, _)) = reader.peek() {
372 reader.next();
373 } else {
374 break;
375 }
376 }
377
378 let position = if let Some(last) = declarations.last() {
379 start.union(last.get_position())
380 } else {
381 let position = start.with_length(3);
382 if options.partial_syntax {
383 position
384 } else {
385 return Err(ParseError::new(ParseErrors::ExpectedDeclaration, position));
386 }
387 };
388
389 Ok(VarVariableStatement { declarations, position })
390 }
391
392 fn to_string_from_buffer<T: source_map::ToString>(
393 &self,
394 buf: &mut T,
395 options: &crate::ToStringOptions,
396 local: crate::LocalToStringInformation,
397 ) {
398 buf.push_str("var ");
399 declarations_to_string(&self.declarations, buf, options, local, false);
400 }
401}
402
403fn on_different_lines_or_line_end(
404 line_starts: &source_map::LineStarts,
405 keyword_position: crate::TokenStart,
406 Token(kind, next): &Token<TSXToken, crate::TokenStart>,
407) -> bool {
408 matches!(kind, TSXToken::SemiColon | TSXToken::CloseBrace | TSXToken::EOS)
409 || line_starts.byte_indexes_on_different_lines(keyword_position.0 as usize, next.0 as usize)
410}