Skip to main content

microcad_lang/value/
mod.rs

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