ezno_parser/statements/
if_statement.rs

1use crate::{
2	block::BlockOrSingleStatement, derive_ASTNode, expressions::MultipleExpression, ParseOptions,
3	TSXKeyword,
4};
5use get_field_by_type::GetFieldByType;
6use iterator_endiate::EndiateIteratorExt;
7use tokenizer_lib::sized_tokens::TokenStart;
8use visitable_derive::Visitable;
9
10use super::{ASTNode, ParseResult, Span, TSXToken, Token, TokenReader};
11
12/// A [if...else statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)
13#[apply(derive_ASTNode)]
14#[derive(Debug, Clone, PartialEq, Visitable, GetFieldByType)]
15#[get_field_by_type_target(Span)]
16pub struct IfStatement {
17	pub condition: MultipleExpression,
18	pub inner: BlockOrSingleStatement,
19	pub else_conditions: Vec<ConditionalElseStatement>,
20	pub trailing_else: Option<UnconditionalElseStatement>,
21	pub position: Span,
22}
23
24/// `... else if (...) { ... }`
25#[derive(Debug, Clone, PartialEq, Visitable)]
26#[apply(derive_ASTNode)]
27pub struct ConditionalElseStatement {
28	pub condition: MultipleExpression,
29	pub inner: BlockOrSingleStatement,
30	pub position: Span,
31}
32
33/// `... else { ... }`
34#[derive(Debug, Clone, PartialEq, Visitable)]
35#[apply(derive_ASTNode)]
36pub struct UnconditionalElseStatement {
37	pub inner: BlockOrSingleStatement,
38	pub position: Span,
39}
40
41impl ASTNode for IfStatement {
42	fn from_reader(
43		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
44		state: &mut crate::ParsingState,
45		options: &ParseOptions,
46	) -> ParseResult<Self> {
47		let start = state.expect_keyword(reader, TSXKeyword::If)?;
48
49		reader.expect_next(TSXToken::OpenParentheses)?;
50		let condition = MultipleExpression::from_reader(reader, state, options)?;
51		reader.expect_next(TSXToken::CloseParentheses)?;
52		let inner = BlockOrSingleStatement::from_reader(reader, state, options)?;
53		let (mut else_conditions, mut trailing_else) = (Vec::new(), None);
54		while let Some(Token(TSXToken::Keyword(TSXKeyword::Else), _)) = reader.peek() {
55			let Token(_, else_position) = reader.next().unwrap();
56			if matches!(reader.peek(), Some(Token(TSXToken::Keyword(TSXKeyword::If), _))) {
57				let value = ConditionalElseStatement::from_reader_sub_without_else(
58					reader,
59					state,
60					options,
61					else_position,
62				)?;
63				else_conditions.push(value);
64			} else {
65				let unconditional_else_statement =
66					UnconditionalElseStatement::from_reader_sub_without_else(
67						reader,
68						state,
69						options,
70						else_position,
71					)?;
72				trailing_else = Some(unconditional_else_statement);
73				break;
74			}
75		}
76		let position = start.union(if let Some(ref t) = trailing_else {
77			t.get_position()
78		} else if let Some(t) = else_conditions.last() {
79			t.get_position()
80		} else {
81			inner.get_position()
82		});
83		Ok(IfStatement { condition, inner, else_conditions, trailing_else, position })
84	}
85
86	fn get_position(&self) -> Span {
87		self.position
88	}
89
90	fn to_string_from_buffer<T: source_map::ToString>(
91		&self,
92		buf: &mut T,
93		options: &crate::ToStringOptions,
94		local: crate::LocalToStringInformation,
95	) {
96		buf.push_str("if");
97		options.push_gap_optionally(buf);
98		buf.push('(');
99		self.condition.to_string_from_buffer(buf, options, local);
100		buf.push(')');
101		options.push_gap_optionally(buf);
102		self.inner.to_string_from_buffer(buf, options, local.next_level());
103		if !options.pretty
104			&& matches!(self.inner, BlockOrSingleStatement::SingleStatement(_))
105			&& (self.else_conditions.is_empty() || self.trailing_else.is_none())
106		{
107			buf.push(';');
108		}
109
110		for (at_end, else_statement) in self.else_conditions.iter().endiate() {
111			options.push_gap_optionally(buf);
112			else_statement.to_string_from_buffer(buf, options, local);
113			if !options.pretty
114				&& matches!(else_statement.inner, BlockOrSingleStatement::SingleStatement(_))
115				&& at_end
116			{
117				buf.push(';');
118			}
119		}
120		if let Some(else_statement) = &self.trailing_else {
121			options.push_gap_optionally(buf);
122			else_statement.to_string_from_buffer(buf, options, local);
123		}
124	}
125}
126
127impl ASTNode for ConditionalElseStatement {
128	fn from_reader(
129		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
130		state: &mut crate::ParsingState,
131		options: &ParseOptions,
132	) -> ParseResult<Self> {
133		let else_start = state.expect_keyword(reader, TSXKeyword::Else)?;
134		Self::from_reader_sub_without_else(reader, state, options, else_start)
135	}
136
137	fn get_position(&self) -> Span {
138		self.position
139	}
140
141	fn to_string_from_buffer<T: source_map::ToString>(
142		&self,
143		buf: &mut T,
144		options: &crate::ToStringOptions,
145		local: crate::LocalToStringInformation,
146	) {
147		buf.push_str("else if");
148		options.push_gap_optionally(buf);
149		buf.push('(');
150		self.condition.to_string_from_buffer(buf, options, local);
151		buf.push(')');
152		options.push_gap_optionally(buf);
153		self.inner.to_string_from_buffer(buf, options, local.next_level());
154	}
155}
156
157impl ConditionalElseStatement {
158	fn from_reader_sub_without_else(
159		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
160		state: &mut crate::ParsingState,
161		options: &ParseOptions,
162		else_position: TokenStart,
163	) -> ParseResult<Self> {
164		let _ = state.expect_keyword(reader, TSXKeyword::If)?;
165		reader.expect_next(TSXToken::OpenParentheses)?;
166		let condition = MultipleExpression::from_reader(reader, state, options)?;
167		reader.expect_next(TSXToken::CloseParentheses)?;
168		let statements = BlockOrSingleStatement::from_reader(reader, state, options)?;
169		Ok(Self {
170			condition,
171			position: else_position.union(statements.get_position()),
172			inner: statements,
173		})
174	}
175}
176
177impl ASTNode for UnconditionalElseStatement {
178	fn from_reader(
179		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
180		state: &mut crate::ParsingState,
181		options: &ParseOptions,
182	) -> ParseResult<Self> {
183		let else_position = state.expect_keyword(reader, TSXKeyword::Else)?;
184		Self::from_reader_sub_without_else(reader, state, options, else_position)
185	}
186
187	fn get_position(&self) -> Span {
188		self.position
189	}
190
191	fn to_string_from_buffer<T: source_map::ToString>(
192		&self,
193		buf: &mut T,
194		options: &crate::ToStringOptions,
195		local: crate::LocalToStringInformation,
196	) {
197		buf.push_str("else");
198		if !options.pretty && matches!(self.inner, BlockOrSingleStatement::SingleStatement(_)) {
199			buf.push(' ');
200		}
201		options.push_gap_optionally(buf);
202		self.inner.to_string_from_buffer(buf, options, local.next_level());
203	}
204}
205
206impl UnconditionalElseStatement {
207	fn from_reader_sub_without_else(
208		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
209		state: &mut crate::ParsingState,
210		options: &ParseOptions,
211		else_position: TokenStart,
212	) -> ParseResult<Self> {
213		let statements = BlockOrSingleStatement::from_reader(reader, state, options)?;
214		Ok(Self { position: else_position.union(statements.get_position()), inner: statements })
215	}
216}