Skip to main content

microcad_lang/parse/
expression.rs

1// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{parse::*, parser::*, syntax::*};
5use microcad_syntax::ast;
6use microcad_syntax::ast::{Element, LiteralKind};
7
8impl FromAst for RangeFirst {
9    type AstNode = ast::ArrayItem;
10
11    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
12        if matches!(
13            node.expression,
14            ast::Expression::Literal(
15                ast::Literal {
16                    literal: ast::LiteralKind::Float(_)
17                        | ast::LiteralKind::String(_)
18                        | ast::LiteralKind::Quantity(_)
19                        | ast::LiteralKind::Bool(_),
20                    ..
21                },
22                ..
23            )
24        ) {
25            return Err(ParseError::InvalidRangeType {
26                src_ref: context.src_ref(&node.expression.span()),
27            });
28        }
29        Ok(RangeFirst(Box::new(Expression::from_ast(
30            &node.expression,
31            context,
32        )?)))
33    }
34}
35
36impl FromAst for RangeLast {
37    type AstNode = ast::ArrayItem;
38
39    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
40        if matches!(
41            node.expression,
42            ast::Expression::Literal(
43                ast::Literal {
44                    literal: ast::LiteralKind::Float(_)
45                        | ast::LiteralKind::String(_)
46                        | ast::LiteralKind::Quantity(_)
47                        | ast::LiteralKind::Bool(_),
48                    ..
49                },
50                ..
51            )
52        ) {
53            return Err(ParseError::InvalidRangeType {
54                src_ref: context.src_ref(&node.expression.span()),
55            });
56        }
57        Ok(RangeLast(Box::new(Expression::from_ast(
58            &node.expression,
59            context,
60        )?)))
61    }
62}
63
64impl FromAst for RangeExpression {
65    type AstNode = ast::ArrayRangeExpression;
66
67    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
68        Ok(RangeExpression {
69            first: RangeFirst::from_ast(&node.start, context)?,
70            last: RangeLast::from_ast(&node.end, context)?,
71            src_ref: context.src_ref(&node.span),
72        })
73    }
74}
75
76impl FromAst for ListExpression {
77    type AstNode = ast::ArrayListExpression;
78
79    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
80        node.items
81            .iter()
82            .map(|item| Expression::from_ast(&item.expression, context))
83            .collect::<Result<ListExpression, _>>()
84    }
85}
86
87impl FromAst for Marker {
88    type AstNode = ast::Identifier;
89
90    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
91        Ok(Marker {
92            id: Identifier::from_ast(node, context)?,
93            src_ref: context.src_ref(&node.span),
94        })
95    }
96}
97
98impl FromAst for Expression {
99    type AstNode = ast::Expression;
100
101    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
102        Ok(match node {
103            ast::Expression::Call(expr) => Expression::Call(Call::from_ast(expr, context)?),
104            ast::Expression::Literal(ast::Literal {
105                literal: LiteralKind::String(s),
106                span,
107                ..
108            }) => Expression::FormatString(FormatString(Refer::new(
109                vec![FormatStringInner::String(Refer::new(
110                    s.content.clone(),
111                    context.src_ref(&s.span),
112                ))],
113                context.src_ref(span),
114            ))),
115            ast::Expression::Literal(expr) => {
116                Expression::Literal(Literal::from_ast(expr, context)?)
117            }
118            ast::Expression::String(s) => {
119                Expression::FormatString(FormatString::from_ast(s, context)?)
120            }
121            ast::Expression::Tuple(t) => {
122                Expression::TupleExpression(TupleExpression::from_ast(t, context)?)
123            }
124            ast::Expression::ArrayRange(a) => Expression::ArrayExpression(ArrayExpression {
125                inner: ArrayExpressionInner::Range(RangeExpression::from_ast(a, context)?),
126                unit: a
127                    .ty
128                    .as_ref()
129                    .map(|ty| Unit::from_ast(ty, context))
130                    .transpose()?
131                    .unwrap_or_default(),
132                src_ref: context.src_ref(&a.span),
133            }),
134            ast::Expression::ArrayList(a) => Expression::ArrayExpression(ArrayExpression {
135                inner: ArrayExpressionInner::List(ListExpression::from_ast(a, context)?),
136                unit: a
137                    .ty
138                    .as_ref()
139                    .map(|ty| Unit::from_ast(ty, context))
140                    .transpose()?
141                    .unwrap_or_default(),
142                src_ref: context.src_ref(&a.span),
143            }),
144            ast::Expression::QualifiedName(n) => {
145                Expression::QualifiedName(QualifiedName::from_ast(n, context)?)
146            }
147            ast::Expression::Marker(m) => Expression::Marker(Marker::from_ast(m, context)?),
148            ast::Expression::BinaryOperation(binop) => Expression::BinaryOp {
149                lhs: Box::new(Expression::from_ast(&binop.lhs, context)?),
150                rhs: Box::new(Expression::from_ast(&binop.rhs, context)?),
151                op: binop.operation.as_str().into(),
152                src_ref: context.src_ref(&binop.span),
153            },
154            ast::Expression::UnaryOperation(unop) => Expression::UnaryOp {
155                rhs: Box::new(Expression::from_ast(&unop.rhs, context)?),
156                op: unop.operation.as_str().into(),
157                src_ref: context.src_ref(&unop.span),
158            },
159            ast::Expression::Block(b) => Expression::Body(Body::from_ast(b, context)?),
160            ast::Expression::ElementAccess(access) => match &access.element {
161                Element::Attribute(a) => Expression::AttributeAccess(
162                    Box::new(Expression::from_ast(&access.value, context)?),
163                    Identifier::from_ast(a, context)?,
164                    context.src_ref(&access.span),
165                ),
166                Element::Tuple(t) => Expression::PropertyAccess(
167                    Box::new(Expression::from_ast(&access.value, context)?),
168                    Identifier::from_ast(t, context)?,
169                    context.src_ref(&access.span),
170                ),
171                Element::Method(m) => Expression::MethodCall(
172                    Box::new(Expression::from_ast(&access.value, context)?),
173                    MethodCall::from_ast(m, context)?,
174                    context.src_ref(&access.span),
175                ),
176                Element::ArrayElement(e) => Expression::ArrayElementAccess(
177                    Box::new(Expression::from_ast(&access.value, context)?),
178                    Box::new(Expression::from_ast(e, context)?),
179                    context.src_ref(&access.span),
180                ),
181            },
182            ast::Expression::If(i) => Expression::If(Box::new(IfStatement::from_ast(i, context)?)),
183            ast::Expression::Error(span) => {
184                return Err(ParseError::InvalidExpression {
185                    src_ref: context.src_ref(span),
186                });
187            }
188        })
189    }
190}
191
192impl FromAst for TupleExpression {
193    type AstNode = ast::TupleExpression;
194
195    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
196        let mut args = ArgumentList::default();
197        for value in &node.values {
198            args.value
199                .try_push(Argument {
200                    id: value
201                        .name
202                        .as_ref()
203                        .map(|name| Identifier::from_ast(name, context))
204                        .transpose()?,
205                    expression: Expression::from_ast(&value.value, context)?,
206                    src_ref: context.src_ref(&value.span),
207                })
208                .map_err(ParseError::DuplicateArgument)?;
209        }
210
211        Ok(TupleExpression {
212            args,
213            src_ref: context.src_ref(&node.span),
214        })
215    }
216}
217
218/// Create TupleExpression from µcad code
219#[cfg(test)]
220#[macro_export]
221macro_rules! tuple_expression {
222    ($code:literal) => {{
223        use microcad_syntax::ast;
224        use $crate::parser::FromAst;
225        let context = $crate::parser::ParseContext::new($code);
226        let ast = $crate::parse::build_ast($code, &context).unwrap();
227        let statement = ast.statements.statements.first().or(ast.statements.tail.as_deref()).expect("empty source");
228        let tuple_expression = match statement {
229            ast::Statement::Expression(ast::ExpressionStatement {
230                expression: ast::Expression::Tuple(tuple),
231                ..
232            }) => tuple,
233            _ => panic!("non tuple source"),
234        };
235        TupleExpression::from_ast(&tuple_expression, &context).unwrap()
236    }};
237}