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