Skip to main content

mago_syntax/ast/ast/
binary.rs

1use 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::token::GetPrecedence;
10use crate::token::Precedence;
11
12/// Represents a PHP binary operator.
13#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
14#[serde(tag = "type", content = "value")]
15pub enum BinaryOperator<'arena> {
16    Addition(Span),              // `+`
17    Subtraction(Span),           // `-`
18    Multiplication(Span),        // `*`
19    Division(Span),              // `/`
20    Modulo(Span),                // `%`
21    Exponentiation(Span),        // `**`
22    BitwiseAnd(Span),            // `&`
23    BitwiseOr(Span),             // `|`
24    BitwiseXor(Span),            // `^`
25    LeftShift(Span),             // `<<`
26    RightShift(Span),            // `>>`
27    NullCoalesce(Span),          // `??`
28    Equal(Span),                 // `==`
29    NotEqual(Span),              // `!=`
30    Identical(Span),             // `===`
31    NotIdentical(Span),          // `!==`
32    AngledNotEqual(Span),        // `<>`
33    LessThan(Span),              // `<`
34    LessThanOrEqual(Span),       // `<=`
35    GreaterThan(Span),           // `>`
36    GreaterThanOrEqual(Span),    // `>=`
37    Spaceship(Span),             // `<=>`
38    StringConcat(Span),          // `.`
39    Instanceof(Keyword<'arena>), // `instanceof`
40    And(Span),                   // `&&`
41    Or(Span),                    // `||`
42    LowAnd(Keyword<'arena>),     // `and`
43    LowOr(Keyword<'arena>),      // `or`
44    LowXor(Keyword<'arena>),     // `xor`
45}
46
47/// Represents a PHP binary operation.
48///
49/// A binary operation is an operation that takes two operands, a left-hand side and a right-hand side.
50#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
51pub struct Binary<'arena> {
52    pub lhs: &'arena Expression<'arena>,
53    pub operator: BinaryOperator<'arena>,
54    pub rhs: &'arena Expression<'arena>,
55}
56
57impl<'arena> BinaryOperator<'arena> {
58    #[inline]
59    #[must_use]
60    pub const fn is_constant(&self) -> bool {
61        !matches!(self, Self::Instanceof(_))
62    }
63
64    #[inline]
65    #[must_use]
66    pub const fn is_multiplicative(&self) -> bool {
67        matches!(self, Self::Multiplication(_) | Self::Division(_) | Self::Modulo(_))
68    }
69
70    #[inline]
71    #[must_use]
72    pub const fn is_additive(&self) -> bool {
73        matches!(self, Self::Addition(_) | Self::Subtraction(_))
74    }
75
76    #[inline]
77    #[must_use]
78    pub const fn is_arithmetic(&self) -> bool {
79        matches!(
80            self,
81            Self::Addition(_)
82                | Self::Subtraction(_)
83                | Self::Multiplication(_)
84                | Self::Division(_)
85                | Self::Modulo(_)
86                | Self::Exponentiation(_)
87        )
88    }
89
90    #[inline]
91    #[must_use]
92    pub const fn is_bit_shift(&self) -> bool {
93        matches!(self, Self::LeftShift(_) | Self::RightShift(_))
94    }
95
96    #[inline]
97    #[must_use]
98    pub const fn is_bitwise(&self) -> bool {
99        matches!(
100            self,
101            Self::BitwiseAnd(_) | Self::BitwiseOr(_) | Self::BitwiseXor(_) | Self::LeftShift(_) | Self::RightShift(_)
102        )
103    }
104
105    #[inline]
106    #[must_use]
107    pub const fn is_equality(&self) -> bool {
108        matches!(
109            self,
110            Self::Equal(_)
111                | Self::NotEqual(_)
112                | Self::Identical(_)
113                | Self::NotIdentical(_)
114                | Self::AngledNotEqual(_)
115                | Self::Spaceship(_)
116        )
117    }
118
119    #[must_use]
120    pub const fn is_negated_equality(&self) -> bool {
121        matches!(self, Self::NotEqual(_) | Self::NotIdentical(_) | Self::AngledNotEqual(_))
122    }
123
124    #[inline]
125    #[must_use]
126    pub const fn is_identity(&self) -> bool {
127        matches!(self, Self::Identical(_) | Self::NotIdentical(_))
128    }
129
130    #[inline]
131    #[must_use]
132    pub const fn is_comparison(&self) -> bool {
133        matches!(
134            self,
135            Self::Equal(_)
136                | Self::NotEqual(_)
137                | Self::Identical(_)
138                | Self::NotIdentical(_)
139                | Self::AngledNotEqual(_)
140                | Self::LessThan(_)
141                | Self::LessThanOrEqual(_)
142                | Self::GreaterThan(_)
143                | Self::GreaterThanOrEqual(_)
144                | Self::Spaceship(_)
145        )
146    }
147
148    #[inline]
149    #[must_use]
150    pub const fn is_logical(&self) -> bool {
151        matches!(self, Self::And(_) | Self::Or(_) | Self::LowAnd(_) | Self::LowOr(_) | Self::LowXor(_))
152    }
153
154    #[inline]
155    #[must_use]
156    pub const fn is_low_precedence(&self) -> bool {
157        matches!(self, Self::LowAnd(_) | Self::LowOr(_) | Self::LowXor(_))
158    }
159
160    #[inline]
161    #[must_use]
162    pub const fn is_concatenation(&self) -> bool {
163        matches!(self, Self::StringConcat(_))
164    }
165
166    #[inline]
167    #[must_use]
168    pub const fn is_null_coalesce(&self) -> bool {
169        matches!(self, Self::NullCoalesce(_))
170    }
171
172    #[inline]
173    #[must_use]
174    pub const fn is_instanceof(&self) -> bool {
175        matches!(self, Self::Instanceof(_))
176    }
177
178    #[inline]
179    #[must_use]
180    pub fn as_str(&self) -> &'arena str {
181        match self {
182            Self::Addition(_) => "+",
183            Self::Subtraction(_) => "-",
184            Self::Multiplication(_) => "*",
185            Self::Division(_) => "/",
186            Self::Modulo(_) => "%",
187            Self::Exponentiation(_) => "**",
188            Self::BitwiseAnd(_) => "&",
189            Self::BitwiseOr(_) => "|",
190            Self::BitwiseXor(_) => "^",
191            Self::LeftShift(_) => "<<",
192            Self::RightShift(_) => ">>",
193            Self::NullCoalesce(_) => "??",
194            Self::Equal(_) => "==",
195            Self::NotEqual(_) => "!=",
196            Self::Identical(_) => "===",
197            Self::NotIdentical(_) => "!==",
198            Self::AngledNotEqual(_) => "<>",
199            Self::LessThan(_) => "<",
200            Self::LessThanOrEqual(_) => "<=",
201            Self::GreaterThan(_) => ">",
202            Self::GreaterThanOrEqual(_) => ">=",
203            Self::Spaceship(_) => "<=>",
204            Self::StringConcat(_) => ".",
205            Self::And(_) => "&&",
206            Self::Or(_) => "||",
207            Self::Instanceof(keyword) => keyword.value,
208            Self::LowAnd(keyword) => keyword.value,
209            Self::LowOr(keyword) => keyword.value,
210            Self::LowXor(keyword) => keyword.value,
211        }
212    }
213
214    #[inline]
215    #[must_use]
216    pub const fn is_same_as(&self, other: &Self) -> bool {
217        matches!(
218            (self, other),
219            (Self::Addition(_), Self::Addition(_))
220                | (Self::Subtraction(_), Self::Subtraction(_))
221                | (Self::Multiplication(_), Self::Multiplication(_))
222                | (Self::Division(_), Self::Division(_))
223                | (Self::Modulo(_), Self::Modulo(_))
224                | (Self::Exponentiation(_), Self::Exponentiation(_))
225                | (Self::BitwiseAnd(_), Self::BitwiseAnd(_))
226                | (Self::BitwiseOr(_), Self::BitwiseOr(_))
227                | (Self::BitwiseXor(_), Self::BitwiseXor(_))
228                | (Self::LeftShift(_), Self::LeftShift(_))
229                | (Self::RightShift(_), Self::RightShift(_))
230                | (Self::NullCoalesce(_), Self::NullCoalesce(_))
231                | (Self::Equal(_), Self::Equal(_))
232                | (Self::NotEqual(_), Self::NotEqual(_))
233                | (Self::Identical(_), Self::Identical(_))
234                | (Self::NotIdentical(_), Self::NotIdentical(_))
235                | (Self::AngledNotEqual(_), Self::AngledNotEqual(_))
236                | (Self::LessThan(_), Self::LessThan(_))
237                | (Self::LessThanOrEqual(_), Self::LessThanOrEqual(_))
238                | (Self::GreaterThan(_), Self::GreaterThan(_))
239                | (Self::GreaterThanOrEqual(_), Self::GreaterThanOrEqual(_))
240                | (Self::Spaceship(_), Self::Spaceship(_))
241                | (Self::StringConcat(_), Self::StringConcat(_))
242                | (Self::Instanceof(_), Self::Instanceof(_))
243                | (Self::And(_), Self::And(_))
244                | (Self::Or(_), Self::Or(_))
245                | (Self::LowAnd(_), Self::LowAnd(_))
246                | (Self::LowOr(_), Self::LowOr(_))
247                | (Self::LowXor(_), Self::LowXor(_))
248        )
249    }
250}
251
252impl GetPrecedence for BinaryOperator<'_> {
253    #[inline]
254    fn precedence(&self) -> Precedence {
255        match self {
256            Self::Addition(_) | Self::Subtraction(_) => Precedence::AddSub,
257            Self::Multiplication(_) | Self::Division(_) | Self::Modulo(_) => Precedence::MulDivMod,
258            Self::Exponentiation(_) => Precedence::Pow,
259            Self::BitwiseAnd(_) => Precedence::BitwiseAnd,
260            Self::BitwiseOr(_) => Precedence::BitwiseOr,
261            Self::BitwiseXor(_) => Precedence::BitwiseXor,
262            Self::LeftShift(_) | Self::RightShift(_) => Precedence::BitShift,
263            Self::NullCoalesce(_) => Precedence::NullCoalesce,
264            Self::Equal(_)
265            | Self::NotEqual(_)
266            | Self::Identical(_)
267            | Self::NotIdentical(_)
268            | Self::AngledNotEqual(_)
269            | Self::Spaceship(_) => Precedence::Equality,
270            Self::LessThan(_) | Self::LessThanOrEqual(_) | Self::GreaterThan(_) | Self::GreaterThanOrEqual(_) => {
271                Precedence::Comparison
272            }
273            Self::StringConcat(_) => Precedence::Concat,
274            Self::And(_) => Precedence::And,
275            Self::Or(_) => Precedence::Or,
276            Self::LowAnd(_) => Precedence::KeyAnd,
277            Self::LowOr(_) => Precedence::KeyOr,
278            Self::LowXor(_) => Precedence::KeyXor,
279            Self::Instanceof(_) => Precedence::Instanceof,
280        }
281    }
282}
283
284impl HasSpan for BinaryOperator<'_> {
285    fn span(&self) -> Span {
286        match self {
287            Self::Addition(span) => *span,
288            Self::Subtraction(span) => *span,
289            Self::Multiplication(span) => *span,
290            Self::Division(span) => *span,
291            Self::Modulo(span) => *span,
292            Self::Exponentiation(span) => *span,
293            Self::BitwiseAnd(span) => *span,
294            Self::BitwiseOr(span) => *span,
295            Self::BitwiseXor(span) => *span,
296            Self::LeftShift(span) => *span,
297            Self::RightShift(span) => *span,
298            Self::NullCoalesce(span) => *span,
299            Self::Equal(span) => *span,
300            Self::NotEqual(span) => *span,
301            Self::Identical(span) => *span,
302            Self::NotIdentical(span) => *span,
303            Self::AngledNotEqual(span) => *span,
304            Self::LessThan(span) => *span,
305            Self::LessThanOrEqual(span) => *span,
306            Self::GreaterThan(span) => *span,
307            Self::GreaterThanOrEqual(span) => *span,
308            Self::Spaceship(span) => *span,
309            Self::StringConcat(span) => *span,
310            Self::Instanceof(keyword) => keyword.span(),
311            Self::And(span) => *span,
312            Self::Or(span) => *span,
313            Self::LowAnd(keyword) => keyword.span(),
314            Self::LowOr(keyword) => keyword.span(),
315            Self::LowXor(keyword) => keyword.span(),
316        }
317    }
318}
319
320impl HasSpan for Binary<'_> {
321    fn span(&self) -> Span {
322        self.lhs.span().join(self.rhs.span())
323    }
324}