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#[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#[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#[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}