microcad_lang/eval/statements/
mod.rs1use microcad_lang_base::PushDiag;
9
10use crate::{eval::*, model::*, symbol::SymbolDef};
11
12mod assignment_statement;
13mod expression_statement;
14mod if_statement;
15mod marker;
16mod return_statement;
17
18impl Eval for Statement {
19 fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
20 match self {
21 Self::Assignment(a) => {
22 a.eval(context)?;
23 Ok(Value::None)
24 }
25 Self::If(i) => i.eval(context),
26 Self::Expression(e) => e.eval(context),
27 Self::Return(r) => r.eval(context),
28
29 Self::Workbench(..)
30 | Self::Module(..)
31 | Self::Function(..)
32 | Self::InnerAttribute(..)
33 | Self::InnerDocComment(..)
34 | Self::Use(..)
35 | Self::Init(..) => Ok(Value::None),
36 }
37 }
38}
39
40impl Eval<Option<Model>> for Statement {
41 fn eval(&self, context: &mut EvalContext) -> EvalResult<Option<Model>> {
42 let model: Option<Model> = match self {
43 Self::Module(m) => {
44 m.eval(context)?;
45 None
46 }
47 Self::Assignment(a) => {
48 a.eval(context)?;
49 None
50 }
51 Self::If(i) => i.eval(context)?,
52 Self::Expression(e) => e.eval(context)?,
53
54 Self::Workbench(..)
55 | Self::Function(..)
56 | Self::Use(..)
57 | Self::Init(..)
58 | Self::Return(..)
59 | Self::InnerAttribute(..)
60 | Self::InnerDocComment(..) => None,
61 };
62
63 if let Some(ref model) = model {
64 if model.deduce_output_type() == OutputType::InvalidMixed {
65 context.error(self, EvalError::CannotMixGeometry)?;
66 }
67 }
68
69 Ok(model)
70 }
71}
72
73impl Eval<Value> for StatementList {
74 fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
75 let mut result = Value::None;
76 for statement in self.iter() {
77 log::trace!("Evaluating statement: {statement}");
78 match statement.eval(context)? {
79 Value::Return(result) => {
80 return Ok(Value::Return(result));
81 }
82 value => result = value,
83 }
84 }
85 Ok(result)
86 }
87}
88
89impl Eval<Attributes> for StatementList {
91 fn eval(&self, context: &mut EvalContext) -> EvalResult<Attributes> {
92 let mut attributes = Vec::new();
93 for statement in self.iter() {
94 if let Statement::InnerAttribute(attribute) = statement {
95 attributes.append(&mut attribute.eval(context)?);
96 }
97 }
98
99 Ok(Attributes(attributes))
100 }
101}
102
103impl Eval<Models> for StatementList {
104 fn eval(&self, context: &mut EvalContext) -> EvalResult<Models> {
105 let mut models = Models::default();
106 let mut output_type = OutputType::NotDetermined;
107
108 let kind = context
110 .current_symbol()
111 .map(|symbol| {
112 symbol.with_def(|def| match def {
113 SymbolDef::Workbench(workbench_definition) => {
114 let frame = context.stack.current_frame().expect("Some stack frame");
115 match frame {
116 StackFrame::Workbench(..) => Some(workbench_definition.kind.value),
117 _ => None,
118 }
119 }
120 SymbolDef::SourceFile(_) | SymbolDef::Builtin(_) => None,
121 _ => unreachable!(),
122 })
123 })
124 .unwrap_or_default();
125
126 for statement in self.iter() {
127 if let Some(model) = statement.eval(context)? {
128 output_type = output_type.merge(&model.deduce_output_type());
129
130 if let Some(kind) = kind {
132 let expected_output_type = match kind {
133 WorkbenchKind::Part => OutputType::Geometry3D,
134 WorkbenchKind::Sketch => OutputType::Geometry2D,
135 WorkbenchKind::Operation => OutputType::NotDetermined,
136 };
137
138 if expected_output_type != OutputType::NotDetermined
139 && output_type != expected_output_type
140 {
141 context.error(
142 statement,
143 EvalError::WorkbenchInvalidOutput {
144 kind,
145 produced: output_type,
146 expected: expected_output_type,
147 },
148 )?;
149 }
150 }
151
152 if output_type == OutputType::InvalidMixed {
153 context.error(statement, EvalError::CannotMixGeometry)?;
154 }
155 models.push(model);
156 }
157 }
158 Ok(models)
159 }
160}