Skip to main content

microcad_lang/syntax/expression/
mod.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad syntax elements related to expressions
5
6mod array_expression;
7mod marker;
8mod range_expression;
9mod tuple_expression;
10
11pub use array_expression::*;
12pub use marker::*;
13pub use range_expression::*;
14pub use tuple_expression::*;
15
16use crate::{syntax::*, value::*};
17use microcad_lang_base::{Refer, SrcRef, SrcReferrer, TreeDisplay, TreeState};
18
19/// List of expressions.
20pub type ListExpression = Vec<Expression>;
21
22/// Any expression.
23#[derive(Clone, Default)]
24pub enum Expression {
25    /// Something went wrong (and an error will be reported)
26    #[default]
27    Invalid,
28    /// An integer, float, color or bool literal: 1, 1.0, #00FF00, false
29    Literal(Literal),
30    /// A string that contains format expressions: "value = {a}"
31    FormatString(FormatString),
32    /// A list: [a, b, c]
33    ArrayExpression(ArrayExpression),
34    /// A tuple: (a, b, c)
35    TupleExpression(TupleExpression),
36    /// A body: `{}`.
37    Body(Body),
38    /// An if statement: `if {} else {}`.
39    If(Box<IfStatement>),
40    /// A call: `ops::subtract()`.
41    Call(Call),
42    /// A qualified name: `foo::bar`.
43    QualifiedName(QualifiedName),
44    /// A marker expression: `@input`.
45    Marker(Marker),
46    /// A binary operation: `a + b`
47    BinaryOp {
48        /// Left-hand side
49        lhs: Box<Expression>,
50        /// Operator  ('+', '-', '/', '*', '<', '>', '≤', '≥', '&', '|')
51        op: Refer<String>,
52        /// Right -hand side
53        rhs: Box<Expression>,
54        /// Source code reference
55        src_ref: SrcRef,
56    },
57    /// A unary operation: !a
58    UnaryOp {
59        /// Operator ('+', '-', '!')
60        op: Refer<String>,
61        /// Right -hand side
62        rhs: Box<Expression>,
63        /// Source code reference
64        src_ref: SrcRef,
65    },
66    /// Access an element of a list (`a[0]`) or a tuple (`a.0` or `a.b`)
67    ArrayElementAccess(Box<Expression>, Box<Expression>, SrcRef),
68    /// Access an element of a tuple: `a.b`.
69    PropertyAccess(Box<Expression>, Identifier, SrcRef),
70
71    /// Access an attribute of a model: `a#b`.
72    AttributeAccess(Box<Expression>, Identifier, SrcRef),
73
74    /// Call to a method: `[2,3].len()`
75    /// First expression must evaluate to a value
76    MethodCall(Box<Expression>, MethodCall, SrcRef),
77}
78
79impl SingleIdentifier for Expression {
80    /// If the expression includes just one identifier, e.g. `a` or `a * (a + 2)`
81    fn single_identifier(&self) -> Option<&Identifier> {
82        match &self {
83            Expression::Invalid
84            | Expression::Literal(..)
85            | Expression::FormatString(..)
86            | Expression::Marker(..)
87            | Expression::PropertyAccess(..)
88            | Expression::AttributeAccess(..)
89            | Expression::MethodCall(..)
90            | Expression::ArrayExpression(..)
91            | Expression::TupleExpression(..)
92            | Expression::Body(..)
93            | Expression::If(..)
94            | Expression::Call(..) => None,
95
96            Expression::QualifiedName(qualified_name) => qualified_name.single_identifier(),
97            Expression::BinaryOp {
98                lhs,
99                op: _,
100                rhs,
101                src_ref: _,
102            } => {
103                let l = lhs.single_identifier();
104                let r = rhs.single_identifier();
105                if l == r || r.is_none() {
106                    l
107                } else if l.is_none() {
108                    r
109                } else {
110                    None
111                }
112            }
113            Expression::UnaryOp {
114                op: _,
115                rhs,
116                src_ref: _,
117            } => rhs.single_identifier(),
118            Expression::ArrayElementAccess(expression, ..) => expression.single_identifier(),
119        }
120    }
121}
122
123impl SrcReferrer for Expression {
124    fn src_ref(&self) -> SrcRef {
125        match self {
126            Self::Invalid => SrcRef(None),
127            Self::Literal(l) => l.src_ref(),
128            Self::FormatString(fs) => fs.src_ref(),
129            Self::ArrayExpression(le) => le.src_ref(),
130            Self::TupleExpression(te) => te.src_ref(),
131            Self::Call(c) => c.src_ref(),
132            Self::Body(b) => b.src_ref(),
133            Self::If(i) => i.src_ref(),
134            Self::QualifiedName(q) => q.src_ref(),
135            Self::Marker(m) => m.src_ref(),
136            Self::BinaryOp {
137                lhs: _,
138                op: _,
139                rhs: _,
140                src_ref,
141            } => src_ref.clone(),
142            Self::UnaryOp {
143                op: _,
144                rhs: _,
145                src_ref,
146            } => src_ref.clone(),
147            Self::ArrayElementAccess(_, _, src_ref) => src_ref.clone(),
148            Self::PropertyAccess(_, _, src_ref) => src_ref.clone(),
149            Self::AttributeAccess(_, _, src_ref) => src_ref.clone(),
150            Self::MethodCall(_, _, src_ref) => src_ref.clone(),
151        }
152    }
153}
154
155impl std::fmt::Display for Expression {
156    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
157        match self {
158            Self::Literal(literal) => write!(f, "{literal}"),
159            Self::FormatString(format_string) => write!(f, "{format_string}"),
160            Self::ArrayExpression(array_expression) => write!(f, "{array_expression}"),
161            Self::TupleExpression(tuple_expression) => write!(f, "{tuple_expression}"),
162            Self::BinaryOp {
163                lhs,
164                op,
165                rhs,
166                src_ref: _,
167            } => write!(f, "{lhs} {op} {rhs}"),
168            Self::UnaryOp {
169                op,
170                rhs,
171                src_ref: _,
172            } => write!(f, "{op}{rhs}"),
173            Self::ArrayElementAccess(lhs, rhs, _) => write!(f, "{lhs}[{rhs}]"),
174            Self::PropertyAccess(lhs, rhs, _) => write!(f, "{lhs}.{rhs}"),
175            Self::AttributeAccess(lhs, rhs, _) => write!(f, "{lhs}#{rhs}"),
176            Self::MethodCall(lhs, method_call, _) => write!(f, "{lhs}.{method_call}"),
177            Self::Call(call) => write!(f, "{call}"),
178            Self::Body(body) => write!(f, "{body}"),
179            Self::If(if_) => write!(f, "{if_}"),
180            Self::QualifiedName(qualified_name) => write!(f, "{qualified_name}"),
181            Self::Marker(marker) => write!(f, "{marker}"),
182            _ => unimplemented!(),
183        }
184    }
185}
186
187impl std::fmt::Debug for Expression {
188    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189        match self {
190            Self::Literal(literal) => write!(f, "{literal}"),
191            Self::FormatString(format_string) => write!(f, "{format_string:?}"),
192            Self::ArrayExpression(array_expression) => write!(f, "{array_expression:?}"),
193            Self::TupleExpression(tuple_expression) => write!(f, "{tuple_expression:?}"),
194            Self::BinaryOp {
195                lhs,
196                op,
197                rhs,
198                src_ref: _,
199            } => write!(f, "{lhs:?} {op} {rhs:?}"),
200            Self::UnaryOp {
201                op,
202                rhs,
203                src_ref: _,
204            } => write!(f, "{op}{rhs:?}"),
205            Self::ArrayElementAccess(lhs, rhs, _) => write!(f, "{lhs:?}[{rhs:?}]"),
206            Self::PropertyAccess(lhs, rhs, _) => write!(f, "{lhs:?}.{rhs:?}"),
207            Self::AttributeAccess(lhs, rhs, _) => write!(f, "{lhs:?}#{rhs:?}"),
208            Self::MethodCall(lhs, method_call, _) => write!(f, "{lhs:?}.{method_call:?}"),
209            Self::Call(call) => write!(f, "{call:?}"),
210            Self::Body(body) => write!(f, "{body:?}"),
211            Self::If(if_) => write!(f, "{if_:?}"),
212            Self::QualifiedName(qualified_name) => write!(f, "{qualified_name:?}"),
213            Self::Marker(marker) => write!(f, "{marker:?}"),
214            _ => unimplemented!(),
215        }
216    }
217}
218
219impl TreeDisplay for Value {
220    fn tree_print(&self, f: &mut std::fmt::Formatter, depth: TreeState) -> std::fmt::Result {
221        write!(f, "{:depth$}Value: {value}", "", value = self)
222    }
223}
224
225impl TreeDisplay for Expression {
226    fn tree_print(&self, f: &mut std::fmt::Formatter, mut depth: TreeState) -> std::fmt::Result {
227        match self {
228            Expression::Literal(literal) => literal.tree_print(f, depth),
229            Expression::FormatString(format_string) => format_string.tree_print(f, depth),
230            Expression::ArrayExpression(array_expression) => array_expression.tree_print(f, depth),
231            Expression::TupleExpression(tuple_expression) => tuple_expression.tree_print(f, depth),
232            Expression::BinaryOp {
233                lhs,
234                op,
235                rhs,
236                src_ref: _,
237            } => {
238                writeln!(f, "{:depth$}BinaryOp '{op}':", "")?;
239                depth.indent();
240                lhs.tree_print(f, depth)?;
241                rhs.tree_print(f, depth)
242            }
243            Expression::UnaryOp {
244                op,
245                rhs,
246                src_ref: _,
247            } => {
248                writeln!(f, "{:depth$}UnaryOp '{op}':", "")?;
249                depth.indent();
250                rhs.tree_print(f, depth)
251            }
252            Expression::ArrayElementAccess(lhs, rhs, _) => {
253                writeln!(f, "{:depth$}ArrayElementAccess:", "")?;
254                depth.indent();
255                lhs.tree_print(f, depth)?;
256                rhs.tree_print(f, depth)
257            }
258            Expression::PropertyAccess(lhs, rhs, _) => {
259                writeln!(f, "{:depth$}FieldAccess:", "")?;
260                depth.indent();
261                lhs.tree_print(f, depth)?;
262                rhs.tree_print(f, depth)
263            }
264            Expression::AttributeAccess(lhs, rhs, _) => {
265                writeln!(f, "{:depth$}AttributeAccess:", "")?;
266                depth.indent();
267                lhs.tree_print(f, depth)?;
268                rhs.tree_print(f, depth)
269            }
270            Expression::MethodCall(lhs, method_call, _) => {
271                writeln!(f, "{:depth$}MethodCall:", "")?;
272                depth.indent();
273                lhs.tree_print(f, depth)?;
274                method_call.tree_print(f, depth)
275            }
276            Expression::Call(call) => call.tree_print(f, depth),
277            Expression::Body(body) => body.tree_print(f, depth),
278            Expression::If(if_) => if_.tree_print(f, depth),
279            Expression::QualifiedName(qualified_name) => qualified_name.tree_print(f, depth),
280            Expression::Marker(marker) => marker.tree_print(f, depth),
281            Expression::Invalid => write!(f, "{}", microcad_lang_base::invalid!(EXPRESSION)),
282        }
283    }
284}
285
286impl AsRef<Self> for Expression {
287    fn as_ref(&self) -> &Self {
288        self
289    }
290}
291
292impl PartialEq for Expression {
293    fn eq(&self, other: &Self) -> bool {
294        match (self, other) {
295            (Self::Literal(l0), Self::Literal(r0)) => l0 == r0,
296            (Self::FormatString(l0), Self::FormatString(r0)) => l0 == r0,
297            (Self::ArrayExpression(l0), Self::ArrayExpression(r0)) => l0 == r0,
298            (Self::TupleExpression(l0), Self::TupleExpression(r0)) => l0 == r0,
299            (Self::QualifiedName(l0), Self::QualifiedName(r0)) => l0 == r0,
300            (
301                Self::BinaryOp {
302                    lhs: l_lhs,
303                    op: l_op,
304                    rhs: l_rhs,
305                    src_ref: l_src_ref,
306                },
307                Self::BinaryOp {
308                    lhs: r_lhs,
309                    op: r_op,
310                    rhs: r_rhs,
311                    src_ref: r_src_ref,
312                },
313            ) => l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs && l_src_ref == r_src_ref,
314            (
315                Self::UnaryOp {
316                    op: l_op,
317                    rhs: l_rhs,
318                    src_ref: l_src_ref,
319                },
320                Self::UnaryOp {
321                    op: r_op,
322                    rhs: r_rhs,
323                    src_ref: r_src_ref,
324                },
325            ) => l_op == r_op && l_rhs == r_rhs && l_src_ref == r_src_ref,
326            (Self::ArrayElementAccess(l0, l1, l2), Self::ArrayElementAccess(r0, r1, r2)) => {
327                l0 == r0 && l1 == r1 && l2 == r2
328            }
329            _ => unreachable!("PartialEq implemented for const expressions only"),
330        }
331    }
332}