microcad_lang/eval/
expression.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{eval::*, model::*};
5
6impl Eval for RangeFirst {
7    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
8        let value: Value = self.0.eval(context)?;
9        Ok(match value {
10            Value::Integer(_) => value,
11            value => {
12                context.error(
13                    self,
14                    EvalError::ExpectedType {
15                        expected: Type::Integer,
16                        found: value.ty(),
17                    },
18                )?;
19
20                Value::None
21            }
22        })
23    }
24}
25
26impl Eval for RangeLast {
27    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
28        let value: Value = self.0.eval(context)?;
29        Ok(match value {
30            Value::Integer(_) => value,
31            value => {
32                context.error(
33                    self,
34                    EvalError::ExpectedType {
35                        expected: Type::Integer,
36                        found: value.ty(),
37                    },
38                )?;
39
40                Value::None
41            }
42        })
43    }
44}
45
46impl Eval for RangeExpression {
47    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
48        Ok(
49            match (self.first.eval(context)?, self.last.eval(context)?) {
50                (Value::Integer(first), Value::Integer(last)) => Value::Array(Array::from_values(
51                    (first..last + 1).map(Value::Integer).collect(),
52                    Type::Integer,
53                )),
54                (_, _) => Value::None,
55            },
56        )
57    }
58}
59
60impl Eval for ArrayExpression {
61    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
62        match &self.inner {
63            ArrayExpressionInner::Range(range_expression) => range_expression.eval(context),
64            ArrayExpressionInner::List(expressions) => {
65                let value_list = ValueList::new(
66                    expressions
67                        .iter()
68                        .map(|expr| expr.eval(context))
69                        .collect::<Result<_, _>>()?,
70                );
71
72                match value_list.types().common_type() {
73                    Some(common_type) => {
74                        match Value::Array(Array::from_values(value_list, common_type)) * self.unit
75                        {
76                            Ok(value) => Ok(value),
77                            Err(err) => {
78                                context.error(self, err)?;
79                                Ok(Value::None)
80                            }
81                        }
82                    }
83                    None => {
84                        context.error(
85                            self,
86                            EvalError::ArrayElementsDifferentTypes(value_list.types()),
87                        )?;
88                        Ok(Value::None)
89                    }
90                }
91            }
92        }
93    }
94}
95
96impl Eval<Option<Symbol>> for QualifiedName {
97    fn eval(&self, context: &mut EvalContext) -> EvalResult<Option<Symbol>> {
98        match context.lookup(self) {
99            Ok(symbol) => Ok(Some(symbol.clone())),
100            Err(error) => {
101                context.error(self, error)?;
102                Ok(None)
103            }
104        }
105    }
106}
107
108impl Eval for QualifiedName {
109    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
110        context.lookup(self)?.with_def(|def| match def {
111            SymbolDefinition::Constant(.., value) | SymbolDefinition::Argument(_, value) => {
112                Ok(value.clone())
113            }
114            SymbolDefinition::ConstExpression(.., expr) => expr.eval(context),
115            SymbolDefinition::SourceFile(_) => Ok(Value::None),
116
117            SymbolDefinition::Module(ns) => {
118                context.error(self, EvalError::UnexpectedNested("mod", ns.id.clone()))?;
119                Ok(Value::None)
120            }
121            SymbolDefinition::Workbench(w) => {
122                context.error(
123                    self,
124                    EvalError::UnexpectedNested(w.kind.as_str(), w.id.clone()),
125                )?;
126                Ok(Value::None)
127            }
128            SymbolDefinition::Function(f) => {
129                context.error(self, EvalError::UnexpectedNested("function", f.id.clone()))?;
130                Ok(Value::None)
131            }
132            SymbolDefinition::Builtin(bm) => {
133                context.error(self, EvalError::UnexpectedNested("builtin", bm.id.clone()))?;
134                Ok(Value::None)
135            }
136            SymbolDefinition::Alias(_, id, _) => {
137                // Alias should have been resolved within previous lookup()
138                unreachable!(
139                    "Unexpected alias {id} in value expression at {}",
140                    self.src_ref()
141                )
142            }
143            SymbolDefinition::UseAll(_, name) => {
144                unreachable!("Unexpected use {name} in value expression")
145            }
146            #[cfg(test)]
147            SymbolDefinition::Tester(..) => {
148                unreachable!()
149            }
150        })
151    }
152}
153
154impl Expression {
155    /// Evaluate an expression together with an attribute list.
156    ///
157    /// The attribute list will be also evaluated and the resulting attributes
158    /// will be assigned to the resulting value.
159    pub fn eval_with_attribute_list(
160        &self,
161        attribute_list: &AttributeList,
162        context: &mut EvalContext,
163    ) -> EvalResult<Value> {
164        let value = self.eval(context)?;
165        match value {
166            Value::Model(model) => {
167                let attributes = attribute_list.eval(context)?;
168                model.borrow_mut().attributes = attributes.clone();
169                Ok(Value::Model(model))
170            }
171            Value::None => Ok(Value::None),
172            _ => {
173                if !attribute_list.is_empty() {
174                    context.error(
175                        attribute_list,
176                        AttributeError::CannotAssignAttribute(self.to_string()),
177                    )?;
178                }
179                Ok(value)
180            }
181        }
182    }
183}
184
185impl Eval for Expression {
186    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
187        log::trace!("Evaluating expression:\n{self}");
188        let result = match self {
189            Self::Literal(literal) => literal.eval(context),
190            Self::FormatString(format_string) => format_string.eval(context),
191            Self::ArrayExpression(array_expression) => array_expression.eval(context),
192            Self::TupleExpression(tuple_expression) => tuple_expression.eval(context),
193            Self::BinaryOp {
194                lhs,
195                op,
196                rhs,
197                src_ref: _,
198            } => {
199                let lhs: Value = lhs.eval(context)?;
200                let rhs: Value = rhs.eval(context)?;
201                if lhs.is_invalid() || rhs.is_invalid() {
202                    return Ok(Value::None);
203                }
204
205                match Value::binary_op(lhs, rhs, op.as_str()) {
206                    Err(err) => {
207                        context.error(self, err)?;
208                        Ok(Value::None)
209                    }
210                    Ok(value) => Ok(value),
211                }
212            }
213            Self::UnaryOp {
214                op,
215                rhs,
216                src_ref: _,
217            } => {
218                let value: Value = rhs.eval(context)?;
219                value.unary_op(op.as_str()).map_err(EvalError::ValueError)
220            }
221            Self::ArrayElementAccess(lhs, rhs, _) => {
222                let lhs = lhs.eval(context)?;
223                let rhs = rhs.eval(context)?;
224
225                match (lhs, rhs) {
226                    (Value::Array(list), Value::Integer(index)) => {
227                        let index = index as usize;
228                        if index < list.len() {
229                            match list.get(index) {
230                                Some(value) => Ok(value.clone()),
231                                None => Err(EvalError::ListIndexOutOfBounds {
232                                    index,
233                                    len: list.len(),
234                                }),
235                            }
236                        } else {
237                            context.error(
238                                self,
239                                EvalError::ListIndexOutOfBounds {
240                                    index,
241                                    len: list.len(),
242                                },
243                            )?;
244                            Ok(Value::None)
245                        }
246                    }
247                    _ => unimplemented!(),
248                }
249            }
250            Self::MethodCall(lhs, method_call, _) => method_call.eval(context, lhs),
251            Self::Call(call) => call.eval(context),
252            Self::Body(body) => {
253                if let Some(model) = body.eval(context)? {
254                    Ok(model.into())
255                } else {
256                    Ok(Value::None)
257                }
258            }
259            Self::QualifiedName(qualified_name) => qualified_name.eval(context),
260            Self::Marker(marker) => {
261                let model: Option<Model> = marker.eval(context)?;
262                Ok(model.map(Value::Model).unwrap_or_default())
263            }
264            // Access a property `x` of an expression `circle.x`
265            Self::PropertyAccess(lhs, id, src_ref) => {
266                let value: Value = lhs.eval(context)?;
267                match value {
268                    Value::Tuple(tuple) => match tuple.by_id(id) {
269                        Some(value) => return Ok(value.clone()),
270                        None => context.error(src_ref, EvalError::PropertyNotFound(id.clone()))?,
271                    },
272                    Value::Model(model) => match model.borrow().get_property(id) {
273                        Some(prop) => return Ok(prop.clone()),
274                        None => context.error(src_ref, EvalError::PropertyNotFound(id.clone()))?,
275                    },
276                    _ => {}
277                }
278
279                Ok(Value::None)
280            }
281            Self::AttributeAccess(lhs, identifier, src_ref) => {
282                let value: Value = lhs.eval(context)?;
283                let value = value.get_attribute_value(identifier);
284                if value == Value::None {
285                    context.error(src_ref, AttributeError::NotFound(identifier.clone()))?;
286                }
287                Ok(value)
288            }
289            expr => todo!("{expr:?}"),
290        };
291        match result {
292            Ok(value) => {
293                log::trace!("Evaluated expression:\n{self:?}\n--- into ---\n{value:?}");
294                Ok(value)
295            }
296            Err(err) => {
297                context.error(self, err)?;
298                Ok(Value::None)
299            }
300        }
301    }
302}
303
304impl Eval<Option<Model>> for Expression {
305    fn eval(&self, context: &mut EvalContext) -> EvalResult<Option<Model>> {
306        Ok(match self.eval(context)? {
307            Value::Model(model) => Some(model),
308            _ => None,
309        })
310    }
311}