microcad_lang/value/
mod.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Evaluation entities.
5//!
6//! Every evaluation of any *symbol* leads to a [`Value`] which then might continued
7//! to process or ends up as the overall evaluation result.
8
9mod argument_value;
10mod argument_value_list;
11mod array;
12mod matrix;
13mod parameter_value;
14mod parameter_value_list;
15mod quantity;
16mod tuple;
17mod value_access;
18mod value_error;
19mod value_list;
20
21pub use argument_value::*;
22pub use argument_value_list::*;
23pub use array::*;
24pub use matrix::*;
25pub use parameter_value::*;
26pub use parameter_value_list::*;
27pub use quantity::*;
28pub use tuple::*;
29pub use value_access::*;
30pub use value_error::*;
31pub use value_list::*;
32
33use crate::{model::*, syntax::*, ty::*, *};
34use microcad_core::*;
35
36pub(crate) type ValueResult<Type = Value> = std::result::Result<Type, ValueError>;
37
38/// A variant value with attached source code reference.
39#[derive(Clone, Default, PartialEq)]
40pub enum Value {
41    /// Invalid value (used for error handling).
42    #[default]
43    None,
44    /// A quantity value.
45    Quantity(Quantity),
46    /// A boolean value.
47    Bool(bool),
48    /// An integer value.
49    Integer(Integer),
50    /// A string value.
51    String(String),
52    /// A list of values with a common type.
53    Array(Array),
54    /// A tuple of named items.
55    Tuple(Box<Tuple>),
56    /// A matrix.
57    Matrix(Box<Matrix>),
58    /// A model in the model tree.
59    Model(Model),
60    /// Return value
61    Return(Box<Value>),
62}
63
64impl Value {
65    /// Check if the value is invalid.
66    pub fn is_invalid(&self) -> bool {
67        matches!(self, Value::None)
68    }
69
70    /// Calculate the power of two values, if possible.
71    pub fn pow(&self, rhs: &Value) -> ValueResult {
72        match (&self, rhs) {
73            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity(lhs.pow(rhs))),
74            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity(lhs.pow_int(rhs))),
75            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs.pow(*rhs as u32))),
76            _ => Err(ValueError::InvalidOperator("^".to_string())),
77        }
78    }
79
80    /// Binary operation
81    pub fn binary_op(lhs: Value, rhs: Value, op: &str) -> ValueResult {
82        match op {
83            "+" => lhs + rhs,
84            "-" => lhs - rhs,
85            "*" => lhs * rhs,
86            "/" => lhs / rhs,
87            "^" => lhs.pow(&rhs),
88            "&" => lhs & rhs,
89            "|" => lhs | rhs,
90            ">" => Ok(Value::Bool(lhs > rhs)),
91            "<" => Ok(Value::Bool(lhs < rhs)),
92            "≤" => Ok(Value::Bool(lhs <= rhs)),
93            "≥" => Ok(Value::Bool(lhs >= rhs)),
94            "~" => todo!("implement near ~="),
95            "==" => Ok(Value::Bool(lhs == rhs)),
96            "!=" => Ok(Value::Bool(lhs != rhs)),
97            _ => unimplemented!("{op:?}"),
98        }
99    }
100
101    /// Unary operation.
102    pub fn unary_op(self, op: &str) -> ValueResult {
103        match op {
104            "-" => -self,
105            _ => unimplemented!(),
106        }
107    }
108
109    /// Try to get boolean value.
110    ///
111    /// A `Value::None` will return false.
112    pub fn try_bool(&self) -> Result<bool, ValueError> {
113        match self {
114            Value::Bool(b) => Ok(*b),
115            Value::None => Ok(false),
116            value => Err(ValueError::CannotConvertToBool(value.clone())),
117        }
118    }
119
120    /// Try to convert to [`String`].
121    pub fn try_string(&self) -> Result<String, ValueError> {
122        match self {
123            Value::String(s) => return Ok(s.clone()),
124            Value::Integer(i) => return Ok(i.to_string()),
125            _ => {}
126        }
127
128        Err(ValueError::CannotConvert(self.clone(), "String".into()))
129    }
130
131    /// Try to convert to [`Scalar`].
132    pub fn try_scalar(&self) -> Result<Scalar, ValueError> {
133        match self {
134            Value::Quantity(q) => return Ok(q.value),
135            Value::Integer(i) => return Ok((*i) as f64),
136            _ => {}
137        }
138
139        Err(ValueError::CannotConvert(self.clone(), "Scalar".into()))
140    }
141}
142
143impl PartialOrd for Value {
144    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
145        match (self, other) {
146            // integer type
147            (Value::Integer(lhs), Value::Integer(rhs)) => lhs.partial_cmp(rhs),
148            (Value::Quantity(lhs), Value::Quantity(rhs)) => lhs.partial_cmp(rhs),
149            (
150                Value::Quantity(Quantity {
151                    value,
152                    quantity_type: QuantityType::Scalar,
153                }),
154                Value::Integer(rhs),
155            ) => value.partial_cmp(&(*rhs as Scalar)),
156            _ => {
157                log::warn!("unhandled type mismatch between {self} and {other}");
158                None
159            }
160        }
161    }
162}
163
164impl crate::ty::Ty for Value {
165    fn ty(&self) -> Type {
166        match self {
167            Value::None => Type::Invalid,
168            Value::Integer(_) => Type::Integer,
169            Value::Quantity(q) => q.ty(),
170            Value::Bool(_) => Type::Bool,
171            Value::String(_) => Type::String,
172            Value::Array(list) => list.ty(),
173            Value::Tuple(tuple) => tuple.ty(),
174            Value::Matrix(matrix) => matrix.ty(),
175            Value::Model(_) => Type::Models,
176            Value::Return(r) => r.ty(),
177        }
178    }
179}
180
181impl std::ops::Neg for Value {
182    type Output = ValueResult;
183
184    fn neg(self) -> Self::Output {
185        match self {
186            Value::Integer(n) => Ok(Value::Integer(-n)),
187            Value::Quantity(q) => Ok(Value::Quantity(q.neg())),
188            Value::Array(a) => -a,
189            Value::Tuple(t) => -t.as_ref().clone(),
190            _ => Err(ValueError::InvalidOperator("-".into())),
191        }
192    }
193}
194
195/// Rules for operator `+`.
196impl std::ops::Add for Value {
197    type Output = ValueResult;
198
199    fn add(self, rhs: Self) -> Self::Output {
200        match (self, rhs) {
201            // Add two integers
202            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs + rhs)),
203            // Add a quantity to an integer
204            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
205            // Add an integer to a quantity
206            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
207            // Add two scalars
208            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
209            // Concatenate two strings
210            (Value::String(lhs), Value::String(rhs)) => Ok(Value::String(lhs + &rhs)),
211            // Concatenate two lists
212            (Value::Array(lhs), Value::Array(rhs)) => {
213                if lhs.ty() != rhs.ty() {
214                    return Err(ValueError::CannotCombineVecOfDifferentType(
215                        lhs.ty(),
216                        rhs.ty(),
217                    ));
218                }
219
220                Ok(Value::Array(Array::new(
221                    lhs.iter().chain(rhs.iter()).cloned().collect(),
222                    lhs.ty(),
223                )))
224            }
225            // Add a value to an array.
226            (Value::Array(lhs), rhs) => Ok((lhs + rhs)?),
227            // Add two tuples of the same type: (x = 1., y = 2.) + (x = 3., y = 4.)
228            (Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs + *rhs)?.into()),
229            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} + {rhs}"))),
230        }
231    }
232}
233
234/// Rules for operator `-`.
235impl std::ops::Sub for Value {
236    type Output = ValueResult;
237
238    fn sub(self, rhs: Self) -> Self::Output {
239        match (self, rhs) {
240            // Subtract two integers
241            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs - rhs)),
242            // Subtract an scalar and an integer
243            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
244            // Subtract an integer and a scalar
245            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
246            // Subtract two numbers
247            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
248            // Subtract value to an array: `[1,2,3] - 1 = [0,1,2]`.
249            (Value::Array(lhs), rhs) => Ok((lhs - rhs)?),
250            // Subtract two tuples of the same type: (x = 1., y = 2.) - (x = 3., y = 4.)
251            (Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs - *rhs)?.into()),
252
253            // Boolean difference operator for models
254            (Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
255                lhs.boolean_op(microcad_core::BooleanOp::Subtract, rhs),
256            )),
257            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} - {rhs}"))),
258        }
259    }
260}
261
262/// Rules for operator `*`.
263impl std::ops::Mul for Value {
264    type Output = ValueResult;
265
266    fn mul(self, rhs: Self) -> Self::Output {
267        match (self, rhs) {
268            // Multiply two integers
269            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs * rhs)),
270            // Multiply an integer and a scalar, result is scalar
271            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
272            // Multiply a scalar and an integer, result is scalar
273            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
274            // Multiply two scalars
275            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
276            (Value::Array(array), value) | (value, Value::Array(array)) => Ok((array * value)?),
277            (Value::Tuple(tuple), value) | (value, Value::Tuple(tuple)) => {
278                Ok((tuple.as_ref().clone() * value)?.into())
279            }
280            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} * {rhs}"))),
281        }
282    }
283}
284
285/// Multiply a Unit with a value. Used for unit bundling: `[1,2,3]mm`.
286///
287/// `[1,2,3]mm` is a shortcut for `[1,2,3] * 1mm`.
288impl std::ops::Mul<Unit> for Value {
289    type Output = ValueResult;
290
291    fn mul(self, unit: Unit) -> Self::Output {
292        match (self, unit.ty()) {
293            (value, Type::Quantity(QuantityType::Scalar)) | (value, Type::Integer) => Ok(value),
294            (Value::Integer(i), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
295                Quantity::new(unit.normalize(i as Scalar), quantity_type),
296            )),
297            (Value::Quantity(quantity), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
298                (quantity * Quantity::new(unit.normalize(1.0), quantity_type))?,
299            )),
300            (Value::Array(array), Type::Quantity(quantity_type)) => {
301                Ok((array * Value::Quantity(Quantity::new(unit.normalize(1.0), quantity_type)))?)
302            }
303            (value, _) => Err(ValueError::CannotAddUnitToValueWithUnit(value.clone())),
304        }
305    }
306}
307
308/// Rules for operator `/`.
309impl std::ops::Div for Value {
310    type Output = ValueResult;
311
312    fn div(self, rhs: Self) -> Self::Output {
313        match (self, rhs) {
314            // Division with scalar result
315            (Value::Integer(lhs), Value::Integer(rhs)) => {
316                Ok(Value::Quantity((lhs as Scalar / rhs as Scalar).into()))
317            }
318            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
319            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
320            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
321            (Value::Array(array), value) => Ok((array / value)?),
322            (Value::Tuple(tuple), value) => Ok((tuple.as_ref().clone() / value)?.into()),
323            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} / {rhs}"))),
324        }
325    }
326}
327
328/// Rules for operator `|`` (union).
329impl std::ops::BitOr for Value {
330    type Output = ValueResult;
331
332    fn bitor(self, rhs: Self) -> Self::Output {
333        match (self, rhs) {
334            (Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
335                lhs.boolean_op(microcad_core::BooleanOp::Union, rhs),
336            )),
337            (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)),
338            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} | {rhs}"))),
339        }
340    }
341}
342
343/// Rules for operator `&` (intersection).
344impl std::ops::BitAnd for Value {
345    type Output = ValueResult;
346
347    fn bitand(self, rhs: Self) -> Self::Output {
348        match (self, rhs) {
349            (Value::Model(lhs), Value::Model(rhs)) => {
350                Ok(Value::Model(lhs.boolean_op(BooleanOp::Intersect, rhs)))
351            }
352            (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)),
353            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} & {rhs}"))),
354        }
355    }
356}
357
358impl std::fmt::Display for Value {
359    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
360        match self {
361            Value::None => write!(f, crate::invalid_no_ansi!(VALUE)),
362            Value::Integer(n) => write!(f, "{n}"),
363            Value::Quantity(q) => write!(f, "{q}"),
364            Value::Bool(b) => write!(f, "{b}"),
365            Value::String(s) => write!(f, "{s}"),
366            Value::Array(l) => write!(f, "{l}"),
367            Value::Tuple(t) => write!(f, "{t}"),
368            Value::Matrix(m) => write!(f, "{m}"),
369            Value::Model(n) => write!(f, "{n}"),
370            Value::Return(r) => write!(f, "{r}"),
371        }
372    }
373}
374
375impl std::fmt::Debug for Value {
376    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377        match self {
378            Value::None => write!(f, crate::invalid!(VALUE)),
379            Value::Integer(n) => write!(f, "{n}"),
380            Value::Quantity(q) => write!(f, "{q}"),
381            Value::Bool(b) => write!(f, "{b}"),
382            Value::String(s) => write!(f, "\"{s}\""),
383            Value::Array(l) => write!(f, "{l}"),
384            Value::Tuple(t) => write!(f, "{t}"),
385            Value::Matrix(m) => write!(f, "{m}"),
386            Value::Model(n) => write!(f, "Models:\n {n}"),
387            Value::Return(r) => write!(f, "Return: {r}"),
388        }
389    }
390}
391
392macro_rules! impl_try_from {
393    ($($variant:ident),+ => $ty:ty ) => {
394        impl TryFrom<Value> for $ty {
395            type Error = ValueError;
396
397            fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
398                match value {
399                    $(Value::$variant(v) => Ok(v),)*
400                    value => Err(ValueError::CannotConvert(value, stringify!($ty).into())),
401                }
402            }
403        }
404
405        impl TryFrom<&Value> for $ty {
406            type Error = ValueError;
407
408            fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
409                match value {
410                    $(Value::$variant(v) => Ok(v.clone().into()),)*
411                    value => Err(ValueError::CannotConvert(value.clone(), stringify!($ty).into())),
412                }
413            }
414        }
415    };
416}
417
418impl_try_from!(Integer => i64);
419impl_try_from!(Bool => bool);
420impl_try_from!(String => String);
421
422impl TryFrom<&Value> for Scalar {
423    type Error = ValueError;
424
425    fn try_from(value: &Value) -> Result<Self, Self::Error> {
426        match value {
427            Value::Integer(i) => Ok(*i as Scalar),
428            Value::Quantity(Quantity {
429                value,
430                quantity_type: QuantityType::Scalar,
431            }) => Ok(*value),
432            _ => Err(ValueError::CannotConvert(value.clone(), "Scalar".into())),
433        }
434    }
435}
436
437impl TryFrom<Value> for Scalar {
438    type Error = ValueError;
439
440    fn try_from(value: Value) -> Result<Self, Self::Error> {
441        match value {
442            Value::Integer(i) => Ok(i as Scalar),
443            Value::Quantity(Quantity {
444                value,
445                quantity_type: QuantityType::Scalar,
446            }) => Ok(value),
447            _ => Err(ValueError::CannotConvert(value.clone(), "Scalar".into())),
448        }
449    }
450}
451
452impl TryFrom<&Value> for Size2 {
453    type Error = ValueError;
454
455    fn try_from(value: &Value) -> Result<Self, Self::Error> {
456        match value {
457            Value::Tuple(tuple) => Ok(tuple.as_ref().try_into()?),
458            _ => Err(ValueError::CannotConvert(value.clone(), "Size2".into())),
459        }
460    }
461}
462
463impl TryFrom<&Value> for Mat3 {
464    type Error = ValueError;
465
466    fn try_from(value: &Value) -> Result<Self, Self::Error> {
467        if let Value::Matrix(m) = value {
468            if let Matrix::Matrix3(matrix3) = m.as_ref() {
469                return Ok(*matrix3);
470            }
471        }
472
473        Err(ValueError::CannotConvert(value.clone(), "Matrix3".into()))
474    }
475}
476
477impl From<f32> for Value {
478    fn from(f: f32) -> Self {
479        Value::Quantity((f as Scalar).into())
480    }
481}
482
483impl From<Scalar> for Value {
484    fn from(scalar: Scalar) -> Self {
485        Value::Quantity(scalar.into())
486    }
487}
488
489impl From<Size2> for Value {
490    fn from(value: Size2) -> Self {
491        Value::Tuple(Box::new(value.into()))
492    }
493}
494
495impl From<Quantity> for Value {
496    fn from(qty: Quantity) -> Self {
497        Value::Quantity(qty)
498    }
499}
500
501impl From<String> for Value {
502    fn from(value: String) -> Self {
503        Value::String(value)
504    }
505}
506
507impl FromIterator<Value> for Value {
508    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
509        Self::Array(iter.into_iter().collect())
510    }
511}
512
513impl From<Model> for Value {
514    fn from(model: Model) -> Self {
515        Self::Model(model)
516    }
517}
518
519impl AttributesAccess for Value {
520    fn get_attributes_by_id(&self, id: &Identifier) -> Vec<crate::model::Attribute> {
521        match self {
522            Value::Model(model) => model.get_attributes_by_id(id),
523            _ => Vec::default(),
524        }
525    }
526}
527
528#[cfg(test)]
529fn integer(value: i64) -> Value {
530    Value::Integer(value)
531}
532
533#[cfg(test)]
534fn scalar(value: f64) -> Value {
535    Value::Quantity(Quantity::new(value, QuantityType::Scalar))
536}
537
538#[cfg(test)]
539fn check(result: ValueResult, value: Value) {
540    let result = result.expect("error result");
541    assert_eq!(result, value);
542}
543
544#[test]
545fn test_value_integer() {
546    let u = || integer(2);
547    let v = || integer(5);
548    let w = || scalar(5.0);
549
550    // symmetric operations
551    check(u() + v(), integer(2 + 5));
552    check(u() - v(), integer(2 - 5));
553    check(u() * v(), integer(2 * 5));
554    check(u() / v(), scalar(2.0 / 5.0));
555    check(-u(), integer(-2));
556
557    // asymmetric operations
558    check(u() + w(), scalar(2 as Scalar + 5.0));
559    check(u() - w(), scalar(2 as Scalar - 5.0));
560    check(u() * w(), scalar(2 as Scalar * 5.0));
561    check(u() / w(), scalar(2.0 / 5.0));
562}
563
564#[test]
565fn test_value_scalar() {
566    let u = || scalar(2.0);
567    let v = || scalar(5.0);
568    let w = || integer(5);
569
570    // symmetric operations
571    check(u() + v(), scalar(2.0 + 5.0));
572    check(u() - v(), scalar(2.0 - 5.0));
573    check(u() * v(), scalar(2.0 * 5.0));
574    check(u() / v(), scalar(2.0 / 5.0));
575    check(-u(), scalar(-2.0));
576
577    // asymmetric operations
578    check(u() + w(), scalar(2.0 + 5.0));
579    check(u() - w(), scalar(2.0 - 5.0));
580    check(u() * w(), scalar(2.0 * 5.0));
581    check(u() / w(), scalar(2.0 / 5.0));
582}