Skip to main content

microcad_lang/value/
tuple.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Named tuple evaluation entity
5
6use microcad_core::hash::HashMap;
7
8use microcad_lang_base::{Identifier, SrcReferrer};
9use microcad_lang_proc_macros::SrcReferrer;
10
11use crate::{ty::*, value::*};
12
13/// Tuple with named values
14///
15/// Names are optional, which means Identifiers can be empty.
16#[derive(Clone, Debug, Default, PartialEq, SrcReferrer)]
17pub struct Tuple {
18    pub(crate) named: HashMap<ir::Identifier, Value>,
19    pub(crate) unnamed: HashMap<Type, Value>,
20    pub(crate) src_ref: SrcRef,
21}
22
23/// Create a Value::Tuple from items
24#[macro_export]
25macro_rules! create_tuple_value {
26    ($($key:ident = $value:expr),*) => {
27        Value::Tuple(Box::new($crate::create_tuple!($( $key = $value ),*)))
28    };
29}
30
31/// Create a Tuple from items
32#[macro_export]
33macro_rules! create_tuple {
34        ($($key:ident = $value:expr),*) => {
35                [$( (stringify!($key), $crate::value::Value::try_from($value).expect("Valid value")) ),* ]
36                    .iter()
37                    .into()
38    };
39}
40
41impl Tuple {
42    /// Create new named tuple.
43    pub fn new_named(
44        named: microcad_core::hash::HashMap<ir::Identifier, Value>,
45        src_ref: SrcRef,
46    ) -> Self {
47        Self {
48            named,
49            unnamed: HashMap::default(),
50            src_ref,
51        }
52    }
53
54    /// Insert new (or overwrite existing) value into tuple
55    pub fn insert(&mut self, id: ir::Identifier, value: Value) {
56        if id.is_empty() {
57            self.unnamed.insert(value.ty(), value);
58        } else {
59            self.named.insert(id, value);
60        }
61    }
62
63    /// Return an iterator over all named values
64    pub fn named_iter(&self) -> std::collections::hash_map::Iter<'_, ir::Identifier, Value> {
65        if !self.unnamed.is_empty() {
66            log::warn!("using named_iter() on a tuple which has unnamed items too")
67        }
68        self.named.iter()
69    }
70
71    /// Return the tuple type.
72    pub fn tuple_type(&self) -> TupleType {
73        TupleType {
74            named: self
75                .named
76                .iter()
77                .map(|(id, v)| (id.clone(), v.ty()))
78                .collect(),
79            unnamed: self.unnamed.values().map(|v| v.ty()).collect(),
80        }
81    }
82
83    /// Combine two tuples of the same type with an operation.
84    ///
85    /// This function is used for `+` and `-` builtin operators.
86    pub fn combine(
87        self,
88        rhs: Tuple,
89        op: impl Fn(Value, Value) -> ValueResult,
90    ) -> ValueResult<Self> {
91        if self.ty() == rhs.ty() {
92            let mut named = self.named;
93
94            for (key, rhs_val) in rhs.named {
95                named
96                    .entry(key)
97                    .and_modify(|lhs_val| {
98                        *lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
99                    })
100                    .or_insert(rhs_val);
101            }
102
103            let mut unnamed = self.unnamed;
104
105            for (key, rhs_val) in rhs.unnamed {
106                unnamed
107                    .entry(key)
108                    .and_modify(|lhs_val| {
109                        *lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
110                    })
111                    .or_insert(rhs_val);
112            }
113
114            Ok(Tuple {
115                named,
116                unnamed,
117                src_ref: self.src_ref,
118            })
119        } else {
120            Err(ValueError::TupleTypeMismatch {
121                lhs: self.ty(),
122                rhs: rhs.ty(),
123            })
124        }
125    }
126
127    /// Apply value with an operation to a tuple.
128    ///
129    /// This function is used for `*` and `/` builtin operators.
130    pub fn apply(
131        self,
132        value: Value,
133        op: impl Fn(Value, Value) -> ValueResult,
134    ) -> ValueResult<Self> {
135        let mut named = HashMap::default();
136        for (key, lhs_val) in self.named {
137            named.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
138        }
139
140        let mut unnamed = HashMap::default();
141        for (key, lhs_val) in self.unnamed {
142            unnamed.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
143        }
144
145        Ok(Tuple {
146            named,
147            unnamed,
148            src_ref: self.src_ref,
149        })
150    }
151
152    /// Transform each value in the tuple.
153    pub fn transform(self, op: impl Fn(Value) -> ValueResult) -> ValueResult<Self> {
154        let mut named = HashMap::default();
155        for (key, value) in self.named {
156            named.insert(key, op(value).unwrap_or_default());
157        }
158
159        let mut unnamed = HashMap::default();
160        for (key, value) in self.unnamed {
161            unnamed.insert(key, op(value).unwrap_or_default());
162        }
163
164        Ok(Tuple {
165            named,
166            unnamed,
167            src_ref: self.src_ref,
168        })
169    }
170
171    /// Dissolve unnamed them.
172    ///
173    /// Transparent tuples are unnamed tuple items of a tuple.
174    ///
175    /// ```,ucad
176    /// assert_eq!( (x=0, (y=0, z=0)), (x=0, y=0, z=0) );
177    /// ///               ^ unnamed tuple
178    /// ```
179    pub fn ray(&mut self) {
180        self.unnamed.retain(|_, value| {
181            if let Value::Tuple(tuple) = value {
182                tuple.ray();
183                tuple.named.drain().for_each(|(k, v)| {
184                    self.named.insert(k, v);
185                });
186                false
187            } else {
188                true
189            }
190        });
191    }
192
193    /// Call a predicate for each tuple multiplicity.
194    ///
195    /// - `ids`: Items to multiply.
196    /// - `p`: Predicate to call for each resulting tuple.
197    ///
198    /// # Example
199    ///
200    /// | Input           | Predicate's Parameters |
201    /// |-----------------|------------------------|
202    /// | `([x₀, x₁], y)` | `(x₀, y)`, `(x₁, y)`   |
203    ///
204    pub fn multiplicity<P: FnMut(Tuple)>(&self, mut ids: ir::IdentifierList, mut p: P) {
205        log::trace!("combining: {ids:?}:");
206
207        // sort ids for persistent order
208        ids.sort();
209
210        // count array indexes for items which shall be multiplied and number of overall combinations
211        let mut combinations = 1;
212        let mut counts: HashMap<Identifier, (_, _)> = ids
213            .into_iter()
214            .map(|id| {
215                let counter = if let Some(Value::Array(array)) = &self.named.get(&id) {
216                    let len = array.len();
217                    combinations *= len;
218                    (0, len)
219                } else {
220                    panic!("{id:?} found in tuple but no list:\n{self:#?}");
221                };
222                (id, counter)
223            })
224            .collect();
225
226        log::trace!("multiplicity: {combinations} combinations:");
227
228        // call predicate for each version of the tuple
229        for _ in 0..combinations {
230            let mut counted = false;
231
232            // sort multiplier ids for persistent order
233            let mut named: Vec<_> = self.named.iter().collect();
234            named.sort_by(|lhs, rhs| lhs.0.cmp(rhs.0));
235
236            let tuple = named
237                .into_iter()
238                .map(|(id, v)| match v {
239                    Value::Array(array) => {
240                        if let Some((count, len)) = counts.get_mut(id) {
241                            let item = (
242                                id.clone(),
243                                array.get(*count).expect("array index not found").clone(),
244                            );
245                            if !counted {
246                                *count += 1;
247                                if *count == *len {
248                                    *count = 0
249                                } else {
250                                    counted = true;
251                                }
252                            }
253                            item
254                        } else {
255                            panic!("{id:?} found in tuple but no list");
256                        }
257                    }
258                    _ => (id.clone(), v.clone()),
259                })
260                .collect();
261            p(tuple);
262        }
263    }
264}
265
266impl ValueAccess for Tuple {
267    fn by_id(&self, id: &Identifier) -> Option<&Value> {
268        self.named.get(id)
269    }
270
271    fn by_ty(&self, ty: &Type) -> Option<&Value> {
272        self.unnamed.get(ty)
273    }
274}
275
276// TODO impl FromIterator instead
277impl<T> From<std::slice::Iter<'_, (&'static str, T)>> for Tuple
278where
279    T: Into<Value> + Clone + std::fmt::Debug,
280{
281    fn from(iter: std::slice::Iter<'_, (&'static str, T)>) -> Self {
282        let (unnamed, named): (Vec<_>, _) = iter
283            .map(|(k, v)| (Identifier::no_ref(k), (*v).clone().into()))
284            .partition(|(k, _)| k.is_empty());
285        Self {
286            src_ref: SrcRef::none(),
287            named: named.into_iter().collect(),
288            unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
289        }
290    }
291}
292
293impl FromIterator<(Identifier, Value)> for Tuple {
294    fn from_iter<T: IntoIterator<Item = (Identifier, Value)>>(iter: T) -> Self {
295        let (unnamed, named): (Vec<_>, _) = iter
296            .into_iter()
297            .map(|(k, v)| (k, v.clone()))
298            .partition(|(k, _)| k.is_empty());
299        Self {
300            src_ref: SrcRef::merge_all(
301                named
302                    .iter()
303                    .map(|(id, _)| id.src_ref())
304                    .chain(unnamed.iter().map(|(id, _)| id.src_ref())),
305            ),
306            named: named.into_iter().collect(),
307            unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
308        }
309    }
310}
311
312impl From<Vec2> for Tuple {
313    fn from(v: Vec2) -> Self {
314        create_tuple!(x = v.x, y = v.y)
315    }
316}
317
318impl From<Vec3> for Tuple {
319    fn from(v: Vec3) -> Self {
320        create_tuple!(x = v.x, y = v.y, z = v.z)
321    }
322}
323
324impl From<Color> for Tuple {
325    fn from(color: Color) -> Self {
326        create_tuple!(r = color.r, g = color.g, b = color.b, a = color.a)
327    }
328}
329
330impl From<Size2> for Tuple {
331    fn from(size: Size2) -> Self {
332        create_tuple!(
333            width = Value::from(Quantity::length(size.width)),
334            height = Value::from(Quantity::length(size.height))
335        )
336    }
337}
338
339impl From<Tuple> for Value {
340    fn from(tuple: Tuple) -> Self {
341        Value::Tuple(Box::new(tuple))
342    }
343}
344
345impl FromIterator<Tuple> for Tuple {
346    fn from_iter<T: IntoIterator<Item = Tuple>>(iter: T) -> Self {
347        let tuples: Vec<_> = iter.into_iter().collect();
348        Self {
349            src_ref: SrcRef::merge_all(tuples.iter().map(|t| t.src_ref())),
350            named: Default::default(),
351            unnamed: tuples
352                .into_iter()
353                .map(|t| (Type::Tuple(t.tuple_type().into()), Value::Tuple(t.into())))
354                .collect(),
355        }
356    }
357}
358
359impl IntoIterator for Tuple {
360    type Item = (Identifier, Value);
361    type IntoIter = std::collections::hash_map::IntoIter<Identifier, Value>;
362
363    fn into_iter(self) -> Self::IntoIter {
364        if !self.unnamed.is_empty() {
365            log::warn!("trying to iterate Tuple with unnamed items");
366        }
367        self.named.into_iter()
368    }
369}
370
371impl<'a> TryFrom<&'a Value> for &'a Tuple {
372    type Error = ValueError;
373
374    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
375        match value {
376            Value::Tuple(tuple) => Ok(tuple),
377            _ => Err(ValueError::CannotConvert(
378                value.to_string(),
379                "Tuple".to_string(),
380            )),
381        }
382    }
383}
384
385impl TryFrom<&Tuple> for Color {
386    type Error = ValueError;
387
388    fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
389        let (r, g, b, a) = (
390            tuple.by_id(&Identifier::no_ref("r")),
391            tuple.by_id(&Identifier::no_ref("g")),
392            tuple.by_id(&Identifier::no_ref("b")),
393            tuple
394                .by_id(&Identifier::no_ref("a"))
395                .unwrap_or(&Value::Quantity(Quantity::new(1.0, QuantityType::Scalar)))
396                .clone(),
397        );
398
399        match (r, g, b, a) {
400            (
401                Some(Value::Quantity(Quantity {
402                    value: r,
403                    quantity_type: QuantityType::Scalar,
404                    ..
405                })),
406                Some(Value::Quantity(Quantity {
407                    value: g,
408                    quantity_type: QuantityType::Scalar,
409                    ..
410                })),
411                Some(Value::Quantity(Quantity {
412                    value: b,
413                    quantity_type: QuantityType::Scalar,
414                    ..
415                })),
416                Value::Quantity(Quantity {
417                    value: a,
418                    quantity_type: QuantityType::Scalar,
419                    ..
420                }),
421            ) => Ok(Color::new(*r as f32, *g as f32, *b as f32, a as f32)),
422            _ => Err(ValueError::CannotConvertToColor(tuple.to_string())),
423        }
424    }
425}
426
427impl TryFrom<&Tuple> for Size2 {
428    type Error = ValueError;
429
430    fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
431        let (width, height) = (
432            tuple.by_id(&Identifier::no_ref("width")),
433            tuple.by_id(&Identifier::no_ref("height")),
434        );
435
436        match (width, height) {
437            (
438                Some(Value::Quantity(Quantity {
439                    value: width,
440                    quantity_type: QuantityType::Length,
441                    ..
442                })),
443                Some(Value::Quantity(Quantity {
444                    value: height,
445                    quantity_type: QuantityType::Length,
446                    ..
447                })),
448            ) => Ok(Size2 {
449                width: *width,
450                height: *height,
451            }),
452            _ => Err(ValueError::CannotConvert(tuple.to_string(), "Size2".into())),
453        }
454    }
455}
456
457impl std::fmt::Display for Tuple {
458    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
459        write!(
460            f,
461            "({items})",
462            items = {
463                let mut items = self
464                    .named
465                    .iter()
466                    .map(|(id, v)| format!("{id}={v}"))
467                    .chain(self.unnamed.values().map(|v| format!("{v}")))
468                    .collect::<Vec<String>>();
469                items.sort();
470                items.join(", ")
471            }
472        )
473    }
474}
475
476impl std::ops::Add<Tuple> for Tuple {
477    type Output = ValueResult<Tuple>;
478
479    fn add(self, rhs: Tuple) -> Self::Output {
480        self.combine(rhs, |lhs, rhs| lhs.clone() + rhs.clone())
481    }
482}
483
484impl std::ops::Sub<Tuple> for Tuple {
485    type Output = ValueResult<Tuple>;
486
487    fn sub(self, rhs: Tuple) -> Self::Output {
488        self.combine(rhs, |lhs, rhs| lhs.clone() - rhs.clone())
489    }
490}
491
492impl std::ops::Mul<Value> for Tuple {
493    type Output = ValueResult<Tuple>;
494
495    fn mul(self, rhs: Value) -> Self::Output {
496        self.apply(rhs, |lhs, rhs| lhs * rhs)
497    }
498}
499
500impl std::ops::Div<Value> for Tuple {
501    type Output = ValueResult<Tuple>;
502
503    fn div(self, rhs: Value) -> Self::Output {
504        self.apply(rhs, |lhs, rhs| lhs / rhs)
505    }
506}
507
508impl std::ops::Neg for Tuple {
509    type Output = ValueResult;
510
511    fn neg(self) -> Self::Output {
512        Ok(Value::Tuple(Box::new(self.transform(|value| -value)?)))
513    }
514}
515
516impl std::ops::Not for Tuple {
517    type Output = ValueResult;
518
519    fn not(self) -> Self::Output {
520        Ok(Value::Tuple(Box::new(self.transform(|value| !value)?)))
521    }
522}
523
524impl std::hash::Hash for Tuple {
525    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
526        self.unnamed.iter().for_each(|(ty, value)| {
527            ty.hash(state);
528            value.hash(state);
529        });
530        self.named.iter().for_each(|(id, value)| {
531            id.hash(state);
532            value.hash(state);
533        });
534    }
535}
536
537impl Ty for Tuple {
538    fn ty(&self) -> Type {
539        Type::Tuple(Box::new(self.tuple_type()))
540    }
541}