microcad_lang/parse/
workbench.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{find_rule, find_rule_exact, parse::*, parser::*, rc::*, syntax::*};
5
6impl Parse for Refer<WorkbenchKind> {
7    fn parse(pair: Pair) -> ParseResult<Self> {
8        match pair.as_str() {
9            "part" => Ok(Refer::new(WorkbenchKind::Part, pair.into())),
10            "sketch" => Ok(Refer::new(WorkbenchKind::Sketch, pair.into())),
11            "op" => Ok(Refer::new(WorkbenchKind::Operation, pair.into())),
12            _ => Err(ParseError::UnexpectedToken),
13        }
14    }
15}
16
17impl Parse for Rc<WorkbenchDefinition> {
18    fn parse(pair: Pair) -> ParseResult<Self> {
19        Ok(WorkbenchDefinition {
20            doc: find_rule!(pair, doc_block)?,
21            visibility: find_rule!(pair, visibility)?,
22            attribute_list: find_rule!(pair, attribute_list)?,
23            kind: find_rule_exact!(pair, workbench_kind)?,
24            id: find_rule!(pair, identifier)?,
25            plan: find_rule!(pair, parameter_list)?,
26            body: {
27                let body = crate::find_rule!(pair, body)?;
28                check_statements(&body)?;
29                body
30            },
31            src_ref: pair.into(),
32        }
33        .into())
34    }
35}
36
37fn check_statements(body: &Body) -> ParseResult<()> {
38    if let (Some(first_init), Some(last_init)) = (
39        body.iter()
40            .position(|stmt| matches!(stmt, Statement::Init(_))),
41        body.iter()
42            .rposition(|stmt| matches!(stmt, Statement::Init(_))),
43    ) {
44        for (n, stmt) in body.iter().enumerate() {
45            match stmt {
46                // ignore inits
47                Statement::Init(_) => (),
48
49                // RULE: Illegal statements in workbenches
50                Statement::Module(_) | Statement::Workbench(_) | Statement::Return(_) => {
51                    return Err(ParseError::IllegalWorkbenchStatement(stmt.src_ref()));
52                }
53
54                // RULE: Ony use or assignments before initializers
55                Statement::Use(_) => {
56                    if n > first_init && n < last_init {
57                        return Err(ParseError::CodeBetweenInitializers(stmt.src_ref()));
58                    }
59                }
60
61                // Some assignments are post init statements
62                Statement::Assignment(a_stmt) => match a_stmt.assignment.qualifier() {
63                    Qualifier::Const => {
64                        return Err(ParseError::IllegalWorkbenchStatement(stmt.src_ref()))
65                    }
66                    Qualifier::Value => {
67                        if n > first_init {
68                            return Err(ParseError::CodeBetweenInitializers(a_stmt.src_ref()));
69                        }
70                    }
71                    Qualifier::Prop => {
72                        if n < last_init {
73                            if n > first_init {
74                                return Err(ParseError::CodeBetweenInitializers(a_stmt.src_ref()));
75                            }
76                            return Err(ParseError::StatementNotAllowedPriorInitializers(
77                                a_stmt.src_ref(),
78                            ));
79                        }
80                    }
81                },
82
83                // Post init statements
84                Statement::If(_)
85                | Statement::InnerAttribute(_)
86                | Statement::Expression(_)
87                | Statement::Function(_) => {
88                    // RULE: No code between initializers
89                    if n < last_init {
90                        if n > first_init {
91                            return Err(ParseError::CodeBetweenInitializers(stmt.src_ref()));
92                        }
93                        return Err(ParseError::StatementNotAllowedPriorInitializers(
94                            stmt.src_ref(),
95                        ));
96                    }
97                }
98            }
99        }
100    }
101    Ok(())
102}