Skip to main content

microcad_lang/parse/
statement.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{parse::*, parser::*, syntax::*};
5use microcad_syntax::ast;
6
7/// Note: These constructors are a workaround until the assignment in microcad-lang is split up
8impl Assignment {
9    fn from_ast_local(
10        node: &ast::LocalAssignment,
11        context: &ParseContext,
12    ) -> Result<Self, ParseError> {
13        Ok(Assignment {
14            doc: None,
15            visibility: Visibility::Private,
16            id: Identifier::from_ast(&node.name, context)?,
17            qualifier: Qualifier::Value,
18            specified_type: node
19                .ty
20                .as_ref()
21                .map(|ty| TypeAnnotation::from_ast(ty, context))
22                .transpose()?,
23            expression: Expression::from_ast(&node.value, context)?,
24            src_ref: context.src_ref(&node.span),
25        })
26    }
27
28    fn from_ast_prop(
29        node: &ast::PropertyAssignment,
30        context: &ParseContext,
31    ) -> Result<Self, ParseError> {
32        Ok(Assignment {
33            doc: node
34                .doc
35                .as_ref()
36                .map(|doc| DocBlock::from_ast(doc, context))
37                .transpose()?,
38            visibility: Visibility::Private,
39            id: Identifier::from_ast(&node.name, context)?,
40            qualifier: Qualifier::Prop,
41            specified_type: node
42                .ty
43                .as_ref()
44                .map(|ty| TypeAnnotation::from_ast(ty, context))
45                .transpose()?,
46            expression: Expression::from_ast(&node.value, context)?,
47            src_ref: context.src_ref(&node.span),
48        })
49    }
50
51    fn from_ast_const(
52        node: &ast::ConstAssignment,
53        context: &ParseContext,
54    ) -> Result<Self, ParseError> {
55        Ok(Assignment {
56            doc: node
57                .doc
58                .as_ref()
59                .map(|doc| DocBlock::from_ast(doc, context))
60                .transpose()?,
61            visibility: node
62                .visibility
63                .as_ref()
64                .map(|v| Visibility::from_ast(v, context))
65                .transpose()?
66                .unwrap_or_default(),
67            id: Identifier::from_ast(&node.name, context)?,
68            qualifier: Qualifier::Const,
69            specified_type: node
70                .ty
71                .as_ref()
72                .map(|ty| TypeAnnotation::from_ast(ty, context))
73                .transpose()?,
74            expression: Expression::from_ast(&node.value, context)?,
75            src_ref: context.src_ref(&node.span),
76        })
77    }
78}
79
80/// Note: These constructors are a workaround until the assignment in microcad-lang is split up
81impl AssignmentStatement {
82    fn from_ast_local(
83        node: &ast::LocalAssignment,
84        context: &ParseContext,
85    ) -> Result<Self, ParseError> {
86        Ok(AssignmentStatement {
87            attribute_list: AttributeList::from_ast(&node.attributes, context)?,
88            assignment: std::rc::Rc::new(Assignment::from_ast_local(node, context)?),
89            src_ref: context.src_ref(&node.span),
90        })
91    }
92
93    fn from_ast_prop(
94        node: &ast::PropertyAssignment,
95        context: &ParseContext,
96    ) -> Result<Self, ParseError> {
97        Ok(AssignmentStatement {
98            attribute_list: AttributeList::from_ast(&node.attributes, context)?,
99            assignment: std::rc::Rc::new(Assignment::from_ast_prop(node, context)?),
100            src_ref: context.src_ref(&node.span),
101        })
102    }
103
104    fn from_ast_const(
105        node: &ast::ConstAssignment,
106        context: &ParseContext,
107    ) -> Result<Self, ParseError> {
108        Ok(AssignmentStatement {
109            attribute_list: AttributeList::from_ast(&node.attributes, context)?,
110            assignment: std::rc::Rc::new(Assignment::from_ast_const(node, context)?),
111            src_ref: context.src_ref(&node.span),
112        })
113    }
114}
115
116impl FromAst for IfStatement {
117    type AstNode = ast::If;
118
119    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
120        Ok(IfStatement {
121            if_ref: context.src_ref(&node.if_span),
122            cond: Expression::from_ast(&node.condition, context)?,
123            body: Body::from_ast(&node.body, context)?,
124            next_if_ref: node
125                .next_if_span
126                .as_ref()
127                .map(|span| context.src_ref(span)),
128            next_if: node
129                .next_if
130                .as_ref()
131                .map(|next| IfStatement::from_ast(next, context))
132                .transpose()?
133                .map(Box::new),
134            else_ref: node.else_span.as_ref().map(|span| context.src_ref(span)),
135            body_else: node
136                .else_body
137                .as_ref()
138                .map(|body| Body::from_ast(body, context))
139                .transpose()?,
140            src_ref: context.src_ref(&node.span),
141        })
142    }
143}
144
145impl FromAst for ExpressionStatement {
146    type AstNode = ast::ExpressionStatement;
147
148    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
149        Ok(ExpressionStatement {
150            src_ref: context.src_ref(&node.span),
151            attribute_list: AttributeList::from_ast(&node.attributes, context)?,
152            expression: Expression::from_ast(&node.expression, context)?,
153        })
154    }
155}
156
157impl FromAst for Statement {
158    type AstNode = ast::Statement;
159
160    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
161        Ok(match node {
162            ast::Statement::Module(module) => Statement::Module(std::rc::Rc::new(
163                ModuleDefinition::from_ast(module, context)?,
164            )),
165            ast::Statement::Use(statement) => {
166                Statement::Use(UseStatement::from_ast(statement, context)?)
167            }
168            ast::Statement::Expression(ast::ExpressionStatement {
169                expression: ast::Expression::If(if_statement),
170                ..
171            }) => Statement::If(IfStatement::from_ast(if_statement, context)?),
172            ast::Statement::Expression(statement) => {
173                Statement::Expression(ExpressionStatement::from_ast(statement, context)?)
174            }
175            ast::Statement::Workbench(w) => {
176                Statement::Workbench(<std::rc::Rc<WorkbenchDefinition>>::from_ast(w, context)?)
177            }
178            ast::Statement::Function(f) => {
179                Statement::Function(std::rc::Rc::new(FunctionDefinition::from_ast(f, context)?))
180            }
181            ast::Statement::Init(i) => {
182                Statement::Init(std::rc::Rc::new(InitDefinition::from_ast(i, context)?))
183            }
184            ast::Statement::Return(r) => Statement::Return(ReturnStatement::from_ast(r, context)?),
185            ast::Statement::InnerAttribute(a) => {
186                Statement::InnerAttribute(Attribute::from_ast(a, context)?)
187            }
188            ast::Statement::InnerDocComment(i) => {
189                Statement::InnerDocComment(InnerDocComment::from_ast(i, context)?)
190            }
191            ast::Statement::LocalAssignment(a) => {
192                Statement::Assignment(AssignmentStatement::from_ast_local(a, context)?)
193            }
194            ast::Statement::Property(a) => {
195                Statement::Assignment(AssignmentStatement::from_ast_prop(a, context)?)
196            }
197            ast::Statement::Const(a) => {
198                Statement::Assignment(AssignmentStatement::from_ast_const(a, context)?)
199            }
200            ast::Statement::Comment(_) => unreachable!("comments are filtered out"),
201            ast::Statement::Error(span) => {
202                return Err(ParseError::InvalidStatement {
203                    src_ref: context.src_ref(span),
204                });
205            }
206        })
207    }
208}
209
210impl FromAst for ReturnStatement {
211    type AstNode = ast::Return;
212
213    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
214        Ok(ReturnStatement {
215            keyword_ref: context.src_ref(&node.keyword_span),
216            result: node
217                .value
218                .as_ref()
219                .map(|res| Expression::from_ast(res, context))
220                .transpose()?,
221            src_ref: context.src_ref(&node.span),
222        })
223    }
224}
225
226impl FromAst for StatementList {
227    type AstNode = ast::StatementList;
228
229    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
230        Ok(StatementList(
231            node.statements
232                .iter()
233                .chain(node.tail.iter().map(|tail| tail.as_ref()))
234                .filter(|statement| !matches!(statement, ast::Statement::Comment(_)))
235                .map(|statement| Statement::from_ast(statement, context))
236                .collect::<Result<Vec<_>, _>>()?,
237        ))
238    }
239}
240
241impl FromAst for Qualifier {
242    type AstNode = ast::AssignmentQualifier;
243
244    fn from_ast(node: &Self::AstNode, _context: &ParseContext) -> Result<Self, ParseError> {
245        Ok(match node {
246            ast::AssignmentQualifier::Const => Qualifier::Const,
247            ast::AssignmentQualifier::Prop => Qualifier::Prop,
248        })
249    }
250}