mago_ast/ast/
binary.rs

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