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 Context) -> EvalResult<Value> {
21        match self {
22            Self::Workbench(w) => {
23                context.grant(w.as_ref())?;
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                context.grant(i)?;
40                Ok(Value::None)
41            }
42            Self::Init(i) => {
43                context.grant(i.as_ref())?;
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 Context) -> EvalResult<Option<Model>> {
53        let model: Option<Model> = match self {
54            Self::Workbench(w) => {
55                context.grant(w.as_ref())?;
56                None
57            }
58            Self::Module(m) => {
59                m.eval(context)?;
60
61                context.grant(m.as_ref())?;
62                None
63            }
64            Self::Function(f) => {
65                context.grant(f.as_ref())?;
66                None
67            }
68            Self::Init(i) => {
69                context.grant(i.as_ref())?;
70                None
71            }
72            Self::Return(r) => {
73                context.grant(r)?;
74                None
75            }
76            Self::Use(u) => {
77                u.eval(context)?;
78                None
79            }
80            Self::Assignment(a) => {
81                a.eval(context)?;
82                None
83            }
84            Self::If(i) => i.eval(context)?,
85            Self::Expression(e) => e.eval(context)?,
86            Self::InnerAttribute(a) => {
87                context.grant(a)?;
88                Default::default()
89            }
90        };
91
92        if let Some(ref model) = model {
93            if model.deduce_output_type() == OutputType::InvalidMixed {
94                context.error(self, EvalError::CannotMixGeometry)?;
95            }
96        }
97
98        Ok(model)
99    }
100}
101
102impl Eval<Value> for StatementList {
103    fn eval(&self, context: &mut Context) -> EvalResult<Value> {
104        for statement in self.iter() {
105            if let Value::Return(result) = statement.eval(context)? {
106                return Ok(*result);
107            }
108        }
109        Ok(Value::None)
110    }
111}
112
113/// Parse inner attributes of a statement list.
114impl Eval<Attributes> for StatementList {
115    fn eval(&self, context: &mut Context) -> EvalResult<Attributes> {
116        let mut attributes = Vec::new();
117        for statement in self.iter() {
118            if let Statement::InnerAttribute(attribute) = statement {
119                attributes.append(&mut attribute.eval(context)?);
120            }
121        }
122
123        Ok(Attributes(attributes))
124    }
125}
126
127impl Eval<Models> for StatementList {
128    fn eval(&self, context: &mut Context) -> EvalResult<Models> {
129        let mut models = Models::default();
130        let mut output_type = OutputType::NotDetermined;
131
132        for statement in self.iter() {
133            if let Some(model) = statement.eval(context)? {
134                output_type = output_type.merge(&model.deduce_output_type());
135                if output_type == OutputType::InvalidMixed {
136                    context.error(statement, EvalError::CannotMixGeometry)?;
137                }
138                models.push(model);
139            }
140        }
141        models.deduce_output_type();
142        Ok(models)
143    }
144}