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