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