mago_syntax/ast/ast/control_flow/
if.rs1use serde::Serialize;
2use strum::Display;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6
7use crate::ast::ast::expression::Expression;
8use crate::ast::ast::keyword::Keyword;
9use crate::ast::ast::statement::Statement;
10use crate::ast::ast::terminator::Terminator;
11use crate::ast::sequence::Sequence;
12
13#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
27pub struct If<'arena> {
28 pub r#if: Keyword<'arena>,
29 pub left_parenthesis: Span,
30 pub condition: &'arena Expression<'arena>,
31 pub right_parenthesis: Span,
32 pub body: IfBody<'arena>,
33}
34
35#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
39#[serde(tag = "type", content = "value")]
40pub enum IfBody<'arena> {
41 Statement(IfStatementBody<'arena>),
42 ColonDelimited(IfColonDelimitedBody<'arena>),
43}
44
45#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
47pub struct IfStatementBody<'arena> {
48 pub statement: &'arena Statement<'arena>,
49 pub else_if_clauses: Sequence<'arena, IfStatementBodyElseIfClause<'arena>>,
50 pub else_clause: Option<IfStatementBodyElseClause<'arena>>,
51}
52
53#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
55pub struct IfStatementBodyElseIfClause<'arena> {
56 pub elseif: Keyword<'arena>,
57 pub left_parenthesis: Span,
58 pub condition: &'arena Expression<'arena>,
59 pub right_parenthesis: Span,
60 pub statement: &'arena Statement<'arena>,
61}
62
63#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
65pub struct IfStatementBodyElseClause<'arena> {
66 pub r#else: Keyword<'arena>,
67 pub statement: &'arena Statement<'arena>,
68}
69
70#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
72pub struct IfColonDelimitedBody<'arena> {
73 pub colon: Span,
74 pub statements: Sequence<'arena, Statement<'arena>>,
75 pub else_if_clauses: Sequence<'arena, IfColonDelimitedBodyElseIfClause<'arena>>,
76 pub else_clause: Option<IfColonDelimitedBodyElseClause<'arena>>,
77 pub endif: Keyword<'arena>,
78 pub terminator: Terminator<'arena>,
79}
80
81#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
83pub struct IfColonDelimitedBodyElseIfClause<'arena> {
84 pub elseif: Keyword<'arena>,
85 pub left_parenthesis: Span,
86 pub condition: &'arena Expression<'arena>,
87 pub right_parenthesis: Span,
88 pub colon: Span,
89 pub statements: Sequence<'arena, Statement<'arena>>,
90}
91
92#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
94pub struct IfColonDelimitedBodyElseClause<'arena> {
95 pub r#else: Keyword<'arena>,
96 pub colon: Span,
97 pub statements: Sequence<'arena, Statement<'arena>>,
98}
99
100impl<'arena> IfBody<'arena> {
101 #[must_use]
102 pub const fn has_else_clause(&self) -> bool {
103 match &self {
104 IfBody::Statement(if_statement_body) => if_statement_body.else_clause.is_some(),
105 IfBody::ColonDelimited(if_colon_delimited_body) => if_colon_delimited_body.else_clause.is_some(),
106 }
107 }
108
109 #[must_use]
110 pub fn has_else_if_clauses(&self) -> bool {
111 match &self {
112 IfBody::Statement(if_statement_body) => !if_statement_body.else_if_clauses.is_empty(),
113 IfBody::ColonDelimited(if_colon_delimited_body) => !if_colon_delimited_body.else_if_clauses.is_empty(),
114 }
115 }
116
117 #[must_use]
118 pub fn statements(&self) -> &[Statement<'arena>] {
119 match &self {
120 IfBody::Statement(if_statement_body) => std::slice::from_ref(if_statement_body.statement),
121 IfBody::ColonDelimited(if_colon_delimited_body) => if_colon_delimited_body.statements.as_slice(),
122 }
123 }
124
125 #[must_use]
126 pub fn else_statements(&self) -> Option<&[Statement<'arena>]> {
127 match &self {
128 IfBody::Statement(if_statement_body) => {
129 if_statement_body.else_clause.as_ref().map(|e| std::slice::from_ref(e.statement))
130 }
131 IfBody::ColonDelimited(if_colon_delimited_body) => {
132 if_colon_delimited_body.else_clause.as_ref().map(|e| e.statements.as_slice())
133 }
134 }
135 }
136
137 #[must_use]
138 pub fn else_if_statements(&self) -> Vec<&[Statement<'arena>]> {
139 match &self {
140 IfBody::Statement(if_statement_body) => {
141 if_statement_body.else_if_clauses.iter().map(|e| std::slice::from_ref(e.statement)).collect()
142 }
143 IfBody::ColonDelimited(if_colon_delimited_body) => {
144 if_colon_delimited_body.else_if_clauses.iter().map(|e| e.statements.as_slice()).collect()
145 }
146 }
147 }
148
149 #[must_use]
150 pub fn else_if_clauses(&self) -> Vec<(&Expression<'arena>, &[Statement<'arena>])> {
151 match &self {
152 IfBody::Statement(if_statement_body) => if_statement_body
153 .else_if_clauses
154 .iter()
155 .map(|e| (e.condition, std::slice::from_ref(e.statement)))
156 .collect(),
157 IfBody::ColonDelimited(if_colon_delimited_body) => {
158 if_colon_delimited_body.else_if_clauses.iter().map(|e| (e.condition, e.statements.as_slice())).collect()
159 }
160 }
161 }
162}
163
164impl HasSpan for If<'_> {
165 fn span(&self) -> Span {
166 Span::between(self.r#if.span(), self.body.span())
167 }
168}
169
170impl HasSpan for IfBody<'_> {
171 fn span(&self) -> Span {
172 match self {
173 IfBody::Statement(body) => body.span(),
174 IfBody::ColonDelimited(body) => body.span(),
175 }
176 }
177}
178
179impl HasSpan for IfStatementBody<'_> {
180 fn span(&self) -> Span {
181 let span = self.statement.span();
182
183 Span::between(
184 span,
185 self.else_clause.as_ref().map_or_else(|| self.else_if_clauses.span(span.file_id, span.end), HasSpan::span),
186 )
187 }
188}
189
190impl HasSpan for IfStatementBodyElseIfClause<'_> {
191 fn span(&self) -> Span {
192 Span::between(self.elseif.span(), self.statement.span())
193 }
194}
195
196impl HasSpan for IfStatementBodyElseClause<'_> {
197 fn span(&self) -> Span {
198 Span::between(self.r#else.span(), self.statement.span())
199 }
200}
201
202impl HasSpan for IfColonDelimitedBody<'_> {
203 fn span(&self) -> Span {
204 Span::between(self.colon, self.terminator.span())
205 }
206}
207
208impl HasSpan for IfColonDelimitedBodyElseIfClause<'_> {
209 fn span(&self) -> Span {
210 Span::between(self.elseif.span(), self.statements.span(self.colon.file_id, self.colon.end))
211 }
212}
213
214impl HasSpan for IfColonDelimitedBodyElseClause<'_> {
215 fn span(&self) -> Span {
216 Span::between(self.r#else.span(), self.statements.span(self.colon.file_id, self.colon.end))
217 }
218}