microcad_lang/parse/
expression.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{parse::*, parser::*, syntax::*};
5
6impl Parse for RangeFirst {
7    fn parse(pair: Pair) -> ParseResult<Self> {
8        Ok(Self(Box::new(
9            pair.find(Rule::expression)
10                .or(pair
11                    .find(Rule::integer_literal)
12                    .map(|i| Expression::Literal(Literal::Integer(i))))
13                .expect("Expression"),
14        )))
15    }
16}
17
18impl Parse for RangeLast {
19    fn parse(pair: Pair) -> ParseResult<Self> {
20        Ok(Self(Box::new(
21            pair.find(Rule::expression)
22                .or(pair
23                    .find(Rule::integer_literal)
24                    .map(|i| Expression::Literal(Literal::Integer(i))))
25                .expect("Expression"),
26        )))
27    }
28}
29
30impl Parse for RangeExpression {
31    fn parse(pair: Pair) -> ParseResult<Self> {
32        Ok(Self {
33            first: pair.find(Rule::range_start).expect("Range start"),
34            last: pair.find(Rule::range_end).expect("Range end"),
35            src_ref: pair.src_ref(),
36        })
37    }
38}
39
40impl Parse for ListExpression {
41    fn parse(pair: Pair) -> ParseResult<Self> {
42        pair.inner()
43            .filter_map(|pair| match pair.as_rule() {
44                Rule::expression => Some(Expression::parse(pair)),
45                _ => None,
46            })
47            .collect::<Result<Vec<_>, _>>()
48    }
49}
50
51impl Parse for ArrayExpression {
52    fn parse(pair: Pair) -> ParseResult<Self> {
53        Ok(Self {
54            inner: pair
55                .find(Rule::range_expression)
56                .map(ArrayExpressionInner::Range)
57                .or(pair
58                    .find(Rule::list_expression)
59                    .map(ArrayExpressionInner::List))
60                .unwrap_or_default(),
61            unit: pair.find(Rule::unit).unwrap_or_default(),
62            src_ref: pair.clone().into(),
63        })
64    }
65}
66
67impl Parse for Marker {
68    fn parse(pair: Pair) -> ParseResult<Self> {
69        Parser::ensure_rule(&pair, Rule::marker);
70        Ok(Self {
71            id: Identifier::parse(pair.inner().next().expect(INTERNAL_PARSE_ERROR))?,
72            src_ref: pair.src_ref(),
73        })
74    }
75}
76
77lazy_static::lazy_static! {
78    /// Expression parser
79    static ref PRATT_PARSER: pest::pratt_parser::PrattParser<Rule> = {
80        use pest::pratt_parser::{Assoc, Op,PrattParser};
81        use Assoc::*;
82        use Rule::*;
83
84        // Precedence is defined lowest to highest
85        PrattParser::new()
86            // Addition and subtract have equal precedence
87            .op(Op::infix(or, Left) | Op::infix(and, Left))
88            .op(Op::infix(equal, Left) | Op::infix(not_equal, Left))
89            .op(Op::infix(greater_than, Left) | Op::infix(less_than, Left))
90            .op(Op::infix(less_equal, Left) | Op::infix(greater_equal, Left))
91            .op(Op::infix(add, Left) | Op::infix(subtract, Left))
92            .op(Op::infix(multiply, Left) | Op::infix(divide, Left))
93            .op(Op::infix(r#union, Left) | Op::infix(intersect, Left))
94            .op(Op::infix(power_xor, Left))
95            .op(Op::infix(near, Left))
96            .op(Op::prefix(unary_minus))
97            .op(Op::prefix(unary_plus))
98            .op(Op::prefix(unary_not))
99            .op(Op::postfix(method_call))
100            .op(Op::postfix(array_element_access))
101            .op(Op::postfix(tuple_element_access))
102            .op(Op::postfix(attribute_access))
103    };
104}
105
106impl Expression {
107    /// Generate literal from string
108    pub fn literal_from_str(s: &str) -> ParseResult<Self> {
109        use std::str::FromStr;
110        if s.len() > 1 && s.starts_with('"') && s.ends_with('"') {
111            Ok(Self::FormatString(FormatString::from_str(s)?))
112        } else {
113            Ok(Self::Literal(Literal::from_str(s)?))
114        }
115    }
116}
117
118impl Parse for Expression {
119    fn parse(pair: Pair) -> ParseResult<Self> {
120        Parser::ensure_rule(&pair, Rule::expression);
121
122        PRATT_PARSER
123            .map_primary(|primary| {
124                match (
125                    Pair::new(primary.clone(), pair.source_hash()),
126                    primary.as_rule(),
127                ) {
128                    (primary, Rule::literal) => Ok(Self::Literal(Literal::parse(primary)?)),
129                    (primary, Rule::expression) => Ok(Self::parse(primary)?),
130                    (primary, Rule::array_expression) => {
131                        Ok(Self::ArrayExpression(ArrayExpression::parse(primary)?))
132                    }
133                    (primary, Rule::tuple_expression) => {
134                        Ok(Self::TupleExpression(TupleExpression::parse(primary)?))
135                    }
136                    (primary, Rule::format_string) => {
137                        Ok(Self::FormatString(FormatString::parse(primary)?))
138                    }
139                    (primary, Rule::body) => Ok(Self::Body(Body::parse(primary)?)),
140                    (primary, Rule::call) => Ok(Self::Call(Call::parse(primary)?)),
141                    (primary, Rule::qualified_name) => {
142                        Ok(Self::QualifiedName(QualifiedName::parse(primary)?))
143                    }
144                    (primary, Rule::marker) => Ok(Self::Marker(Marker::parse(primary)?)),
145                    rule => unreachable!(
146                        "Expression::parse expected atom, found {:?} {:?}",
147                        rule,
148                        pair.as_span().as_str()
149                    ),
150                }
151            })
152            .map_infix(|lhs, op, rhs| {
153                let op = match op.as_rule() {
154                    Rule::add => "+",
155                    Rule::subtract => "-",
156                    Rule::multiply => "*",
157                    Rule::divide => "/",
158                    Rule::r#union => "|",
159                    Rule::intersect => "&",
160                    Rule::power_xor => "^",
161                    Rule::greater_than => ">",
162                    Rule::less_than => "<",
163                    Rule::less_equal => "≤",
164                    Rule::greater_equal => "≥",
165                    Rule::equal => "==",
166                    Rule::near => "~",
167                    Rule::not_equal => "!=",
168                    Rule::and => "&",
169                    Rule::or => "|",
170
171                    rule => unreachable!(
172                        "Expression::parse expected infix operation, found {:?}",
173                        rule
174                    ),
175                };
176                Ok(Self::BinaryOp {
177                    lhs: Box::new(lhs?),
178                    op: op.into(),
179                    rhs: Box::new(rhs?),
180                    src_ref: pair.clone().into(),
181                })
182            })
183            .map_prefix(|op, rhs| {
184                let op = match op.as_rule() {
185                    Rule::unary_minus => '-',
186                    Rule::unary_plus => '+',
187                    Rule::unary_not => '!',
188                    _ => unreachable!(),
189                };
190
191                Ok(Self::UnaryOp {
192                    op: op.into(),
193                    rhs: Box::new(rhs?),
194                    src_ref: pair.clone().into(),
195                })
196            })
197            .map_postfix(|lhs, op| {
198                match (Pair::new(op.clone(), pair.source_hash()), op.as_rule()) {
199                    (op, Rule::array_element_access) => Ok(Self::ArrayElementAccess(
200                        Box::new(lhs?),
201                        Box::new(Self::parse(op)?),
202                        pair.clone().into(),
203                    )),
204                    (op, Rule::attribute_access) => {
205                        let op = op.inner().next().expect(INTERNAL_PARSE_ERROR);
206                        Ok(Self::AttributeAccess(
207                            Box::new(lhs?),
208                            Identifier::parse(op)?,
209                            pair.clone().into(),
210                        ))
211                    }
212                    (op, Rule::tuple_element_access) => {
213                        let op = op.inner().next().expect(INTERNAL_PARSE_ERROR);
214                        match op.as_rule() {
215                            Rule::identifier => Ok(Self::PropertyAccess(
216                                Box::new(lhs?),
217                                Identifier::parse(op)?,
218                                pair.clone().into(),
219                            )),
220                            rule => unreachable!("Expected identifier or int, found {:?}", rule),
221                        }
222                    }
223                    (op, Rule::method_call) => Ok(Self::MethodCall(
224                        Box::new(lhs?),
225                        MethodCall::parse(op)?,
226                        pair.clone().into(),
227                    )),
228                    rule => {
229                        unreachable!("Expr::parse expected postfix operation, found {:?}", rule)
230                    }
231                }
232            })
233            .parse(
234                pair.pest_pair()
235                    .clone()
236                    .into_inner()
237                    .filter(|pair| pair.as_rule() != Rule::COMMENT), // Filter comments
238            )
239    }
240}
241
242impl Parse for TupleExpression {
243    fn parse(pair: Pair) -> ParseResult<Self> {
244        Ok(TupleExpression {
245            args: crate::find_rule!(pair, argument_list)?,
246            src_ref: pair.clone().into(),
247        })
248    }
249}
250
251/// Create TupleExpression from µcad code
252#[macro_export]
253macro_rules! tuple_expression {
254    ($code:literal) => {{
255        $crate::parse!(
256            TupleExpression,
257            $crate::parser::Rule::tuple_expression,
258            $code
259        )
260    }};
261}