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