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::If(if_) => {
266                if let Some(model) = if_.eval(context)? {
267                    Ok(model.into())
268                } else {
269                    Ok(Value::None)
270                }
271            }
272            Self::QualifiedName(qualified_name) => qualified_name.eval(context),
273            Self::Marker(marker) => {
274                let model: Option<Model> = marker.eval(context)?;
275                Ok(model.map(Value::Model).unwrap_or_default())
276            }
277            // Access a property `x` of an expression `circle.x`
278            Self::PropertyAccess(lhs, id, src_ref) => {
279                let value: Value = lhs.eval(context)?;
280                match value {
281                    Value::Tuple(tuple) => match tuple.by_id(id) {
282                        Some(value) => return Ok(value.clone()),
283                        None => context.error(src_ref, EvalError::PropertyNotFound(id.clone()))?,
284                    },
285                    Value::Model(model) => match model.borrow().get_property(id) {
286                        Some(prop) => return Ok(prop.clone()),
287                        None => context.error(src_ref, EvalError::PropertyNotFound(id.clone()))?,
288                    },
289                    _ => {}
290                }
291
292                Ok(Value::None)
293            }
294            Self::AttributeAccess(lhs, identifier, src_ref) => {
295                let value: Value = lhs.eval(context)?;
296                let value = value.get_attribute_value(identifier);
297                if value == Value::None {
298                    context.error(src_ref, AttributeError::NotFound(identifier.clone()))?;
299                }
300                Ok(value)
301            }
302            expr => todo!("{expr:?}"),
303        };
304        match result {
305            Ok(value) => {
306                log::trace!("Evaluated expression:\n{self:?}\n--- into ---\n{value:?}");
307                Ok(value)
308            }
309            Err(err) => {
310                context.error(self, err)?;
311                Ok(Value::None)
312            }
313        }
314    }
315}
316
317impl Eval<Option<Model>> for Expression {
318    fn eval(&self, context: &mut EvalContext) -> EvalResult<Option<Model>> {
319        Ok(match self.eval(context)? {
320            Value::Model(model) => Some(model),
321            _ => None,
322        })
323    }
324}