nu_protocol/ast/
operator.rs

1use super::{Expr, Expression};
2use crate::{ShellError, Span};
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use strum_macros::{EnumIter, EnumMessage};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, EnumMessage)]
8pub enum Comparison {
9    #[strum(message = "Equal to")]
10    Equal,
11    #[strum(message = "Not equal to")]
12    NotEqual,
13    #[strum(message = "Less than")]
14    LessThan,
15    #[strum(message = "Greater than")]
16    GreaterThan,
17    #[strum(message = "Less than or equal to")]
18    LessThanOrEqual,
19    #[strum(message = "Greater than or equal to")]
20    GreaterThanOrEqual,
21    #[strum(message = "Contains regex match")]
22    RegexMatch,
23    #[strum(message = "Does not contain regex match")]
24    NotRegexMatch,
25    #[strum(message = "Is a member of (doesn't use regex)")]
26    In,
27    #[strum(message = "Is not a member of (doesn't use regex)")]
28    NotIn,
29    #[strum(message = "Contains a value of (doesn't use regex)")]
30    Has,
31    #[strum(message = "Does not contain a value of (doesn't use regex)")]
32    NotHas,
33    #[strum(message = "Starts with")]
34    StartsWith,
35    #[strum(message = "Does not start with")]
36    NotStartsWith,
37    #[strum(message = "Ends with")]
38    EndsWith,
39    #[strum(message = "Does not end with")]
40    NotEndsWith,
41}
42
43impl Comparison {
44    pub const fn as_str(&self) -> &'static str {
45        match self {
46            Self::Equal => "==",
47            Self::NotEqual => "!=",
48            Self::LessThan => "<",
49            Self::GreaterThan => ">",
50            Self::LessThanOrEqual => "<=",
51            Self::GreaterThanOrEqual => ">=",
52            Self::RegexMatch => "=~",
53            Self::NotRegexMatch => "!~",
54            Self::In => "in",
55            Self::NotIn => "not-in",
56            Self::Has => "has",
57            Self::NotHas => "not-has",
58            Self::StartsWith => "starts-with",
59            Self::NotStartsWith => "not-starts-with",
60            Self::EndsWith => "ends-with",
61            Self::NotEndsWith => "not-ends-with",
62        }
63    }
64}
65
66impl AsRef<str> for Comparison {
67    fn as_ref(&self) -> &str {
68        self.as_str()
69    }
70}
71
72impl fmt::Display for Comparison {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.write_str(self.as_str())
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, EnumMessage)]
79pub enum Math {
80    #[strum(message = "Add (Plus)")]
81    Add,
82    #[strum(message = "Subtract (Minus)")]
83    Subtract,
84    #[strum(message = "Multiply")]
85    Multiply,
86    #[strum(message = "Divide")]
87    Divide,
88    #[strum(message = "Floor division")]
89    FloorDivide,
90    #[strum(message = "Floor division remainder (Modulo)")]
91    Modulo,
92    #[strum(message = "Power of")]
93    Pow,
94    #[strum(message = "Concatenates two lists, two strings, or two binary values")]
95    Concatenate,
96}
97
98impl Math {
99    pub const fn as_str(&self) -> &'static str {
100        match self {
101            Self::Add => "+",
102            Self::Subtract => "-",
103            Self::Multiply => "*",
104            Self::Divide => "/",
105            Self::FloorDivide => "//",
106            Self::Modulo => "mod",
107            Self::Pow => "**",
108            Self::Concatenate => "++",
109        }
110    }
111}
112
113impl AsRef<str> for Math {
114    fn as_ref(&self) -> &str {
115        self.as_str()
116    }
117}
118
119impl fmt::Display for Math {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.write_str(self.as_str())
122    }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, EnumMessage)]
126pub enum Boolean {
127    #[strum(message = "Logical OR (short-circuiting)")]
128    Or,
129    #[strum(message = "Logical XOR")]
130    Xor,
131    #[strum(message = "Logical AND (short-circuiting)")]
132    And,
133}
134
135impl Boolean {
136    pub const fn as_str(&self) -> &'static str {
137        match self {
138            Self::Or => "or",
139            Self::Xor => "xor",
140            Self::And => "and",
141        }
142    }
143}
144
145impl AsRef<str> for Boolean {
146    fn as_ref(&self) -> &str {
147        self.as_str()
148    }
149}
150
151impl fmt::Display for Boolean {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        f.write_str(self.as_str())
154    }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, EnumMessage)]
158pub enum Bits {
159    #[strum(message = "Bitwise OR")]
160    BitOr,
161    #[strum(message = "Bitwise exclusive OR")]
162    BitXor,
163    #[strum(message = "Bitwise AND")]
164    BitAnd,
165    #[strum(message = "Bitwise shift left")]
166    ShiftLeft,
167    #[strum(message = "Bitwise shift right")]
168    ShiftRight,
169}
170
171impl AsRef<str> for Bits {
172    fn as_ref(&self) -> &str {
173        self.as_str()
174    }
175}
176
177impl Bits {
178    pub const fn as_str(&self) -> &'static str {
179        match self {
180            Self::BitOr => "bit-or",
181            Self::BitXor => "bit-xor",
182            Self::BitAnd => "bit-and",
183            Self::ShiftLeft => "bit-shl",
184            Self::ShiftRight => "bit-shr",
185        }
186    }
187}
188
189impl fmt::Display for Bits {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        f.write_str(self.as_str())
192    }
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, EnumMessage)]
196pub enum Assignment {
197    #[strum(message = "Assigns a value to a variable.")]
198    Assign,
199    #[strum(message = "Adds a value to a variable.")]
200    AddAssign,
201    #[strum(message = "Subtracts a value from a variable.")]
202    SubtractAssign,
203    #[strum(message = "Multiplies a variable by a value")]
204    MultiplyAssign,
205    #[strum(message = "Divides a variable by a value.")]
206    DivideAssign,
207    #[strum(message = "Concatenates a variable with a list, string or binary.")]
208    ConcatenateAssign,
209}
210
211impl AsRef<str> for Assignment {
212    fn as_ref(&self) -> &str {
213        self.as_str()
214    }
215}
216
217impl Assignment {
218    pub const fn as_str(&self) -> &'static str {
219        match self {
220            Self::Assign => "=",
221            Self::AddAssign => "+=",
222            Self::SubtractAssign => "-=",
223            Self::MultiplyAssign => "*=",
224            Self::DivideAssign => "/=",
225            Self::ConcatenateAssign => "++=",
226        }
227    }
228}
229
230impl fmt::Display for Assignment {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        f.write_str(self.as_str())
233    }
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
237pub enum Operator {
238    Comparison(Comparison),
239    Math(Math),
240    Boolean(Boolean),
241    Bits(Bits),
242    Assignment(Assignment),
243}
244
245impl Operator {
246    pub const fn as_str(&self) -> &'static str {
247        match self {
248            Self::Comparison(comparison) => comparison.as_str(),
249            Self::Math(math) => math.as_str(),
250            Self::Boolean(boolean) => boolean.as_str(),
251            Self::Bits(bits) => bits.as_str(),
252            Self::Assignment(assignment) => assignment.as_str(),
253        }
254    }
255
256    pub const fn precedence(&self) -> u8 {
257        match self {
258            Self::Math(Math::Pow) => 100,
259            Self::Math(Math::Multiply)
260            | Self::Math(Math::Divide)
261            | Self::Math(Math::Modulo)
262            | Self::Math(Math::FloorDivide) => 95,
263            Self::Math(Math::Add) | Self::Math(Math::Subtract) => 90,
264            Self::Bits(Bits::ShiftLeft) | Self::Bits(Bits::ShiftRight) => 85,
265            Self::Comparison(Comparison::NotRegexMatch)
266            | Self::Comparison(Comparison::RegexMatch)
267            | Self::Comparison(Comparison::StartsWith)
268            | Self::Comparison(Comparison::NotStartsWith)
269            | Self::Comparison(Comparison::EndsWith)
270            | Self::Comparison(Comparison::NotEndsWith)
271            | Self::Comparison(Comparison::LessThan)
272            | Self::Comparison(Comparison::LessThanOrEqual)
273            | Self::Comparison(Comparison::GreaterThan)
274            | Self::Comparison(Comparison::GreaterThanOrEqual)
275            | Self::Comparison(Comparison::Equal)
276            | Self::Comparison(Comparison::NotEqual)
277            | Self::Comparison(Comparison::In)
278            | Self::Comparison(Comparison::NotIn)
279            | Self::Comparison(Comparison::Has)
280            | Self::Comparison(Comparison::NotHas)
281            | Self::Math(Math::Concatenate) => 80,
282            Self::Bits(Bits::BitAnd) => 75,
283            Self::Bits(Bits::BitXor) => 70,
284            Self::Bits(Bits::BitOr) => 60,
285            Self::Boolean(Boolean::And) => 50,
286            Self::Boolean(Boolean::Xor) => 45,
287            Self::Boolean(Boolean::Or) => 40,
288            Self::Assignment(_) => 10,
289        }
290    }
291}
292
293impl fmt::Display for Operator {
294    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295        f.write_str(self.as_str())
296    }
297}
298
299#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, EnumIter)]
300pub enum RangeInclusion {
301    Inclusive,
302    RightExclusive,
303}
304
305#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
306pub struct RangeOperator {
307    pub inclusion: RangeInclusion,
308    pub span: Span,
309    pub next_op_span: Span,
310}
311
312impl fmt::Display for RangeOperator {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        match self.inclusion {
315            RangeInclusion::Inclusive => write!(f, ".."),
316            RangeInclusion::RightExclusive => write!(f, "..<"),
317        }
318    }
319}
320
321pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
322    match op {
323        Expression {
324            expr: Expr::Operator(operator),
325            ..
326        } => Ok(*operator),
327        Expression { span, expr, .. } => Err(ShellError::UnknownOperator {
328            op_token: format!("{expr:?}"),
329            span: *span,
330        }),
331    }
332}