Skip to main content

microcad_syntax/ast/
expression.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::Span;
5use crate::ast::{Identifier, ItemExtras, Literal, SingleType, Statement, StringLiteral};
6use std::num::ParseIntError;
7
8/// An operator for binary operators, together with a span
9#[derive(Debug, PartialEq)]
10pub struct Operator {
11    /// The source span for the operator
12    pub span: Span,
13    /// The type of the operator
14    pub operation: OperatorType,
15}
16
17/// The type of the operator for binary operations
18#[derive(Debug, PartialEq)]
19#[allow(missing_docs)]
20pub enum OperatorType {
21    Add,
22    Subtract,
23    Multiply,
24    Divide,
25    Union,
26    Intersect,
27    PowerXor,
28    GreaterThan,
29    LessThan,
30    GreaterEqual,
31    LessEqual,
32    Equal,
33    Near,
34    NotEqual,
35    And,
36    Or,
37    Xor,
38}
39
40impl OperatorType {
41    /// Get the symbolic representation for the operator
42    pub fn as_str(&self) -> &'static str {
43        match self {
44            Self::Add => "+",
45            Self::Subtract => "-",
46            Self::Multiply => "*",
47            Self::Divide => "/",
48            Self::Union => "|",
49            Self::Intersect => "&",
50            Self::PowerXor => "^",
51            Self::GreaterThan => ">",
52            Self::LessThan => "<",
53            Self::GreaterEqual => "≥",
54            Self::LessEqual => "≤",
55            Self::Equal => "==",
56            Self::Near => "~",
57            Self::NotEqual => "!=",
58            Self::And => "&",
59            Self::Or => "|",
60            Self::Xor => "^",
61        }
62    }
63}
64
65/// An operator for unary operators, together with a span
66#[derive(Debug, PartialEq)]
67pub struct UnaryOperator {
68    /// The source span for the unary operator
69    pub span: Span,
70    /// The type of the unary operator
71    pub operation: UnaryOperatorType,
72}
73
74/// The type of the operator for unary operations
75#[derive(Debug, PartialEq)]
76#[allow(missing_docs)]
77pub enum UnaryOperatorType {
78    Minus,
79    Plus,
80    Not,
81}
82
83impl UnaryOperatorType {
84    /// Get the symbolic representation for the operator
85    pub fn as_str(&self) -> &'static str {
86        match self {
87            UnaryOperatorType::Minus => "-",
88            UnaryOperatorType::Plus => "+",
89            UnaryOperatorType::Not => "!",
90        }
91    }
92}
93
94/// Any expression.
95#[derive(Debug, PartialEq)]
96#[allow(missing_docs)]
97pub enum Expression {
98    Literal(Literal),
99    Tuple(TupleExpression),
100    ArrayRange(ArrayRangeExpression),
101    ArrayList(ArrayListExpression),
102    String(FormatString),
103    QualifiedName(QualifiedName),
104    Marker(Identifier),
105    BinaryOperation(BinaryOperation),
106    UnaryOperation(UnaryOperation),
107    Block(StatementList),
108    Call(Call),
109    ElementAccess(ElementAccess),
110    If(If),
111    Error(Span),
112}
113
114impl Expression {
115    /// Get the source span for the identifier
116    pub fn span(&self) -> Span {
117        match self {
118            Expression::Literal(ex) => ex.span(),
119            Expression::Tuple(ex) => ex.span.clone(),
120            Expression::ArrayRange(ex) => ex.span.clone(),
121            Expression::ArrayList(ex) => ex.span.clone(),
122            Expression::String(ex) => ex.span.clone(),
123            Expression::QualifiedName(ex) => ex.span.clone(),
124            Expression::Marker(ex) => ex.span.clone(),
125            Expression::BinaryOperation(ex) => ex.span.clone(),
126            Expression::UnaryOperation(ex) => ex.span.clone(),
127            Expression::Block(ex) => ex.span.clone(),
128            Expression::Call(ex) => ex.span.clone(),
129            Expression::ElementAccess(ex) => ex.span.clone(),
130            Expression::If(ex) => ex.span.clone(),
131            Expression::Error(span) => span.clone(),
132        }
133    }
134}
135
136/// A string containing a format expression
137#[derive(Debug, PartialEq)]
138#[allow(missing_docs)]
139pub struct FormatString {
140    pub span: Span,
141    pub extras: ItemExtras,
142    pub parts: Vec<StringPart>,
143}
144
145/// A part of a [`FormatString`]
146#[derive(Debug, PartialEq)]
147#[allow(missing_docs)]
148pub enum StringPart {
149    Char(StringCharacter),
150    Content(StringLiteral),
151    Expression(StringExpression),
152}
153
154/// A single character that is part of a [`FormatString`]
155#[derive(Debug, PartialEq)]
156#[allow(missing_docs)]
157pub struct StringCharacter {
158    pub span: Span,
159    pub character: char,
160}
161
162/// A format expression that is part of a [`FormatString`]
163#[derive(Debug, PartialEq)]
164#[allow(missing_docs)]
165pub struct StringExpression {
166    pub span: Span,
167    pub extras: ItemExtras,
168    pub expression: Box<Expression>,
169    pub specification: Box<StringFormatSpecification>,
170}
171
172/// The format specification for a [`StringExpression`], specifying the with and precision for number formatting
173///
174/// All parts of the specification are optional
175#[derive(Debug, PartialEq)]
176#[allow(missing_docs)]
177pub struct StringFormatSpecification {
178    pub span: Span,
179    pub precision: Option<Result<u32, (ParseIntError, Span)>>,
180    pub width: Option<Result<u32, (ParseIntError, Span)>>,
181}
182
183impl StringFormatSpecification {
184    /// Check if an part of the specification is specified
185    pub fn is_some(&self) -> bool {
186        self.precision.is_some() || self.width.is_some()
187    }
188}
189
190/// An item that is part of a tuple expression
191#[derive(Debug, PartialEq)]
192#[allow(missing_docs)]
193pub struct TupleItem {
194    pub span: Span,
195    pub extras: ItemExtras,
196    pub name: Option<Identifier>,
197    pub value: Expression,
198}
199
200impl TupleItem {
201    pub(crate) fn dummy(span: Span) -> Self {
202        TupleItem {
203            span: span.clone(),
204            extras: ItemExtras::default(),
205            name: None,
206            value: Expression::Error(span),
207        }
208    }
209}
210
211/// A tuple expression, a fixed size set of items that don't need to be the same type
212#[derive(Debug, PartialEq)]
213#[allow(missing_docs)]
214pub struct TupleExpression {
215    pub span: Span,
216    pub extras: ItemExtras,
217    pub values: Vec<TupleItem>,
218}
219
220/// An array range, containing all values from the start value (inclusive) till then end value (exclusive)
221#[derive(Debug, PartialEq)]
222#[allow(missing_docs)]
223pub struct ArrayRangeExpression {
224    pub span: Span,
225    pub extras: ItemExtras,
226    pub start: Box<ArrayItem>,
227    pub end: Box<ArrayItem>,
228    pub ty: Option<SingleType>,
229}
230
231/// An array specified as a list of items
232#[derive(Debug, PartialEq)]
233#[allow(missing_docs)]
234pub struct ArrayListExpression {
235    pub span: Span,
236    pub extras: ItemExtras,
237    pub items: Vec<ArrayItem>,
238    pub ty: Option<SingleType>,
239}
240
241/// An item that can be part of an array expression
242#[derive(Debug, PartialEq)]
243#[allow(missing_docs)]
244pub struct ArrayItem {
245    pub span: Span,
246    pub extras: ItemExtras,
247    pub expression: Expression,
248}
249
250/// A qualified name, containing one or more [`Literal`]s seperated by `::`
251#[derive(Debug, PartialEq)]
252#[allow(missing_docs)]
253pub struct QualifiedName {
254    pub span: Span,
255    pub extras: ItemExtras,
256    pub parts: Vec<Identifier>,
257}
258
259/// A binary operation
260#[derive(Debug, PartialEq)]
261#[allow(missing_docs)]
262pub struct BinaryOperation {
263    pub span: Span,
264    pub lhs: Box<Expression>,
265    pub operation: Operator,
266    pub rhs: Box<Expression>,
267}
268
269/// A unary operation
270#[derive(Debug, PartialEq)]
271#[allow(missing_docs)]
272pub struct UnaryOperation {
273    pub span: Span,
274    pub extras: ItemExtras,
275    pub operation: UnaryOperator,
276    pub rhs: Box<Expression>,
277}
278
279/// A function call
280#[derive(Debug, PartialEq)]
281#[allow(missing_docs)]
282pub struct Call {
283    pub span: Span,
284    pub extras: ItemExtras,
285    pub name: QualifiedName,
286    pub arguments: ArgumentList,
287}
288
289/// An expression that access an element from another expression.
290///
291/// Either accessing an array or tuple item, accessing an attribute of a value or a method call.
292#[derive(Debug, PartialEq)]
293#[allow(missing_docs)]
294pub struct ElementAccess {
295    pub span: Span,
296    pub value: Box<Expression>,
297    pub element: Element,
298}
299
300/// The possible element access types
301#[derive(Debug, PartialEq)]
302#[allow(missing_docs)]
303pub enum Element {
304    Attribute(Identifier),
305    Tuple(Identifier),
306    Method(Call),
307    ArrayElement(Box<Expression>),
308}
309
310/// An if expression, can be used as either a statement or expression
311#[derive(Debug, PartialEq)]
312#[allow(missing_docs)]
313pub struct If {
314    pub span: Span,
315    pub if_span: Span,
316    pub extras: ItemExtras,
317    pub condition: Box<Expression>,
318    pub body: StatementList,
319    pub next_if_span: Option<Span>,
320    pub next_if: Option<Box<If>>,
321    pub else_span: Option<Span>,
322    pub else_body: Option<StatementList>,
323}
324
325/// A list of statements, with an optional "tail" expression
326#[derive(Debug, PartialEq)]
327#[allow(missing_docs)]
328pub struct StatementList {
329    pub span: Span,
330    pub extras: ItemExtras,
331    pub statements: Vec<Statement>,
332    pub tail: Option<Box<Statement>>,
333}
334
335impl StatementList {
336    pub(crate) fn dummy(span: Span) -> Self {
337        StatementList {
338            span,
339            extras: ItemExtras::default(),
340            statements: Vec::default(),
341            tail: None,
342        }
343    }
344}
345
346/// A list of arguments to a function call
347#[derive(Debug, PartialEq)]
348#[allow(missing_docs)]
349pub struct ArgumentList {
350    pub span: Span,
351    pub extras: ItemExtras,
352    pub arguments: Vec<Argument>,
353}
354
355impl ArgumentList {
356    pub(crate) fn dummy(span: Span) -> Self {
357        ArgumentList {
358            span,
359            extras: ItemExtras::default(),
360            arguments: Vec::new(),
361        }
362    }
363}
364
365/// A function argument that is part of an [`ArgumentList`]
366#[derive(Debug, PartialEq)]
367#[allow(missing_docs)]
368pub enum Argument {
369    Unnamed(UnnamedArgument),
370    Named(NamedArgument),
371}
372
373impl Argument {
374    /// The name of the argument, if specified
375    pub fn name(&self) -> Option<&Identifier> {
376        match self {
377            Argument::Unnamed(_) => None,
378            Argument::Named(arg) => Some(&arg.name),
379        }
380    }
381
382    /// The value of the argument
383    pub fn value(&self) -> &Expression {
384        match self {
385            Argument::Unnamed(arg) => &arg.value,
386            Argument::Named(arg) => &arg.value,
387        }
388    }
389
390    /// The span of the argument
391    pub fn span(&self) -> &Span {
392        match self {
393            Argument::Unnamed(arg) => &arg.span,
394            Argument::Named(arg) => &arg.span,
395        }
396    }
397}
398
399/// An argument without specified name
400#[derive(Debug, PartialEq)]
401#[allow(missing_docs)]
402pub struct UnnamedArgument {
403    pub span: Span,
404    pub extras: ItemExtras,
405    pub value: Expression,
406}
407
408/// An argument with a specified name
409#[derive(Debug, PartialEq)]
410#[allow(missing_docs)]
411pub struct NamedArgument {
412    pub span: Span,
413    pub extras: ItemExtras,
414    pub name: Identifier,
415    pub value: Expression,
416}