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#[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), Subtraction(Span), Multiplication(Span), Division(Span), Modulo(Span), Exponentiation(Span), BitwiseAnd(Span), BitwiseOr(Span), BitwiseXor(Span), LeftShift(Span), RightShift(Span), NullCoalesce(Span), Equal(Span), NotEqual(Span), Identical(Span), NotIdentical(Span), AngledNotEqual(Span), LessThan(Span), LessThanOrEqual(Span), GreaterThan(Span), GreaterThanOrEqual(Span), Spaceship(Span), StringConcat(Span), Instanceof(Keyword), And(Span), Or(Span), LowAnd(Keyword), LowOr(Keyword), LowXor(Keyword), Elvis(Span), }
50
51#[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}