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::*, rc::*, 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::if_statement) => {
141                        let statement = IfStatement::parse(primary)?;
142                        if !statement.is_complete() {
143                            Err(ParseError::IncompleteIfExpression(statement.src_ref()))
144                        } else {
145                            Ok(Self::If(Box::new(statement)))
146                        }
147                    },
148                    (primary, Rule::call) => Ok(Self::Call(Call::parse(primary)?)),
149                    (primary, Rule::qualified_name) => {
150                        Ok(Self::QualifiedName(QualifiedName::parse(primary)?))
151                    }
152                    (primary, Rule::marker) => Ok(Self::Marker(Marker::parse(primary)?)),
153                    rule => unreachable!(
154                        "Expression::parse expected atom, found {:?} {:?}",
155                        rule,
156                        pair.as_span().as_str()
157                    ),
158                }
159            })
160            .map_infix(|lhs, op, rhs| {
161                let op = match op.as_rule() {
162                    Rule::add => "+",
163                    Rule::subtract => "-",
164                    Rule::multiply => "*",
165                    Rule::divide => "/",
166                    Rule::r#union => "|",
167                    Rule::intersect => "&",
168                    Rule::power_xor => "^",
169                    Rule::greater_than => ">",
170                    Rule::less_than => "<",
171                    Rule::less_equal => "≤",
172                    Rule::greater_equal => "≥",
173                    Rule::equal => "==",
174                    Rule::near => "~",
175                    Rule::not_equal => "!=",
176                    Rule::and => "&",
177                    Rule::or => "|",
178
179                    rule => unreachable!(
180                        "Expression::parse expected infix operation, found {:?}",
181                        rule
182                    ),
183                };
184                Ok(Self::BinaryOp {
185                    lhs: Box::new(lhs?),
186                    op: op.into(),
187                    rhs: Box::new(rhs?),
188                    src_ref: pair.clone().into(),
189                })
190            })
191            .map_prefix(|op, rhs| {
192                let op = match op.as_rule() {
193                    Rule::unary_minus => '-',
194                    Rule::unary_plus => '+',
195                    Rule::unary_not => '!',
196                    _ => unreachable!(),
197                };
198
199                Ok(Self::UnaryOp {
200                    op: op.into(),
201                    rhs: Box::new(rhs?),
202                    src_ref: pair.clone().into(),
203                })
204            })
205            .map_postfix(|lhs, op| {
206                match (Pair::new(op.clone(), pair.source_hash()), op.as_rule()) {
207                    (op, Rule::array_element_access) => Ok(Self::ArrayElementAccess(
208                        Box::new(lhs?),
209                        Box::new(Self::parse(op)?),
210                        pair.clone().into(),
211                    )),
212                    (op, Rule::attribute_access) => {
213                        let op = op.inner().next().expect(INTERNAL_PARSE_ERROR);
214                        Ok(Self::AttributeAccess(
215                            Box::new(lhs?),
216                            Identifier::parse(op)?,
217                            pair.clone().into(),
218                        ))
219                    }
220                    (op, Rule::tuple_element_access) => {
221                        let op = op.inner().next().expect(INTERNAL_PARSE_ERROR);
222                        match op.as_rule() {
223                            Rule::identifier => Ok(Self::PropertyAccess(
224                                Box::new(lhs?),
225                                Identifier::parse(op)?,
226                                pair.clone().into(),
227                            )),
228                            rule => unreachable!("Expected identifier or int, found {:?}", rule),
229                        }
230                    }
231                    (op, Rule::method_call) => Ok(Self::MethodCall(
232                        Box::new(lhs?),
233                        MethodCall::parse(op)?,
234                        pair.clone().into(),
235                    )),
236                    rule => {
237                        unreachable!("Expr::parse expected postfix operation, found {:?}", rule)
238                    }
239                }
240            })
241            .parse(
242                pair.pest_pair()
243                    .clone()
244                    .into_inner()
245                    .filter(|pair| pair.as_rule() != Rule::COMMENT), // Filter comments
246            )
247    }
248}
249
250impl Parse for Rc<Expression> {
251    fn parse(pair: Pair) -> ParseResult<Self> {
252        Ok(Rc::new(Expression::parse(pair)?))
253    }
254}
255
256impl Parse for TupleExpression {
257    fn parse(pair: Pair) -> ParseResult<Self> {
258        Ok(TupleExpression {
259            args: crate::find_rule!(pair, argument_list)?,
260            src_ref: pair.clone().into(),
261        })
262    }
263}
264
265/// Create TupleExpression from µcad code
266#[macro_export]
267macro_rules! tuple_expression {
268    ($code:literal) => {{
269        $crate::parse!(
270            TupleExpression,
271            $crate::parser::Rule::tuple_expression,
272            $code
273        )
274    }};
275}