microcad_lang/eval/statements/
mod.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Workbench definition syntax element evaluation
5// Copyright © 2025 The µcad authors <info@ucad.xyz>
6// SPDX-License-Identifier: AGPL-3.0-or-later
7
8use crate::{eval::*, model::*};
9
10mod assignment_statement;
11mod expression_statement;
12mod if_statement;
13mod marker;
14mod return_statement;
15mod use_statement;
16
17pub use use_statement::*;
18
19impl Eval for Statement {
20    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
21        match self {
22            Self::Workbench(w) => {
23                w.grant(context)?;
24                Ok(Value::None)
25            }
26            Self::Module(m) => m.eval(context),
27            Self::Function(f) => f.eval(context),
28            Self::Use(u) => {
29                u.eval(context)?;
30                Ok(Value::None)
31            }
32            Self::Assignment(a) => {
33                a.eval(context)?;
34                Ok(Value::None)
35            }
36            Self::If(i) => i.eval(context),
37            Self::Expression(e) => e.eval(context),
38            Self::InnerAttribute(i) => {
39                i.grant(context)?;
40                Ok(Value::None)
41            }
42            Self::Init(i) => {
43                i.grant(context)?;
44                Ok(Value::None)
45            }
46            Self::Return(r) => r.eval(context),
47        }
48    }
49}
50
51impl Eval<Option<Model>> for Statement {
52    fn eval(&self, context: &mut EvalContext) -> EvalResult<Option<Model>> {
53        let model: Option<Model> = match self {
54            Self::Workbench(w) => {
55                w.grant(context)?;
56                None
57            }
58            Self::Module(m) => {
59                m.eval(context)?;
60                None
61            }
62            Self::Function(f) => {
63                f.grant(context)?;
64                None
65            }
66            Self::Init(i) => {
67                i.grant(context)?;
68                None
69            }
70            Self::Return(r) => {
71                r.grant(context)?;
72                None
73            }
74            Self::Use(u) => {
75                u.eval(context)?;
76                None
77            }
78            Self::Assignment(a) => {
79                a.eval(context)?;
80                None
81            }
82            Self::If(i) => i.eval(context)?,
83            Self::Expression(e) => e.eval(context)?,
84            Self::InnerAttribute(a) => {
85                a.grant(context)?;
86                None
87            }
88        };
89
90        if let Some(ref model) = model {
91            if model.deduce_output_type() == OutputType::InvalidMixed {
92                context.error(self, EvalError::CannotMixGeometry)?;
93            }
94        }
95
96        Ok(model)
97    }
98}
99
100impl Eval<Value> for StatementList {
101    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
102        for statement in self.iter() {
103            if let Value::Return(result) = statement.eval(context)? {
104                return Ok(*result);
105            }
106        }
107        Ok(Value::None)
108    }
109}
110
111/// Parse inner attributes of a statement list.
112impl Eval<Attributes> for StatementList {
113    fn eval(&self, context: &mut EvalContext) -> EvalResult<Attributes> {
114        let mut attributes = Vec::new();
115        for statement in self.iter() {
116            if let Statement::InnerAttribute(attribute) = statement {
117                attributes.append(&mut attribute.eval(context)?);
118            }
119        }
120
121        Ok(Attributes(attributes))
122    }
123}
124
125impl Eval<Models> for StatementList {
126    fn eval(&self, context: &mut EvalContext) -> EvalResult<Models> {
127        let mut models = Models::default();
128        let mut output_type = OutputType::NotDetermined;
129
130        for statement in self.iter() {
131            if let Some(model) = statement.eval(context)? {
132                output_type = output_type.merge(&model.deduce_output_type());
133                if output_type == OutputType::InvalidMixed {
134                    context.error(statement, EvalError::CannotMixGeometry)?;
135                }
136                models.push(model);
137            }
138        }
139        models.deduce_output_type();
140        Ok(models)
141    }
142}