1use std::collections::HashMap;
7
8use microcad_lang_base::SrcReferrer;
9
10use crate::{ty::*, value::*};
11
12#[derive(Clone, Default, PartialEq)]
16pub struct Tuple {
17 pub(crate) named: std::collections::HashMap<Identifier, Value>,
18 pub(crate) unnamed: std::collections::HashMap<Type, Value>,
19 pub(crate) src_ref: SrcRef,
20}
21
22#[cfg(test)]
24#[macro_export]
25macro_rules! tuple {
26 ($code:expr) => {{
27 use $crate::eval::*;
28 match $crate::tuple_expression!($code)
29 .eval(&mut Default::default())
30 .expect("evaluation error")
31 {
32 Value::Tuple(tuple) => *tuple,
33 _ => panic!(),
34 }
35 }};
36}
37
38#[macro_export]
40macro_rules! create_tuple_value {
41 ($($key:ident = $value:expr),*) => {
42 Value::Tuple(Box::new($crate::create_tuple!($( $key = $value ),*)))
43 };
44}
45
46#[macro_export]
48macro_rules! create_tuple {
49 ($($key:ident = $value:expr),*) => {
50 [$( (stringify!($key), $crate::value::Value::try_from($value).expect("Valid value")) ),* ]
51 .iter()
52 .into()
53 };
54}
55
56impl Tuple {
57 pub fn new_named(named: std::collections::HashMap<Identifier, Value>, src_ref: SrcRef) -> Self {
59 Self {
60 named,
61 unnamed: HashMap::default(),
62 src_ref,
63 }
64 }
65
66 pub fn insert(&mut self, id: Identifier, value: Value) {
68 if id.is_empty() {
69 self.unnamed.insert(value.ty(), value);
70 } else {
71 self.named.insert(id, value);
72 }
73 }
74
75 pub fn named_iter(&self) -> std::collections::hash_map::Iter<'_, Identifier, Value> {
77 if !self.unnamed.is_empty() {
78 log::warn!("using named_iter() on a tuple which has unnamed items too")
79 }
80 self.named.iter()
81 }
82
83 pub fn tuple_type(&self) -> TupleType {
85 TupleType {
86 named: self
87 .named
88 .iter()
89 .map(|(id, v)| (id.clone(), v.ty()))
90 .collect(),
91 unnamed: self.unnamed.values().map(|v| v.ty()).collect(),
92 }
93 }
94
95 pub fn combine(
99 self,
100 rhs: Tuple,
101 op: impl Fn(Value, Value) -> ValueResult,
102 ) -> ValueResult<Self> {
103 if self.ty() == rhs.ty() {
104 let mut named = self.named;
105
106 for (key, rhs_val) in rhs.named {
107 named
108 .entry(key)
109 .and_modify(|lhs_val| {
110 *lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
111 })
112 .or_insert(rhs_val);
113 }
114
115 let mut unnamed = self.unnamed;
116
117 for (key, rhs_val) in rhs.unnamed {
118 unnamed
119 .entry(key)
120 .and_modify(|lhs_val| {
121 *lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
122 })
123 .or_insert(rhs_val);
124 }
125
126 Ok(Tuple {
127 named,
128 unnamed,
129 src_ref: self.src_ref,
130 })
131 } else {
132 Err(ValueError::TupleTypeMismatch {
133 lhs: self.ty(),
134 rhs: rhs.ty(),
135 })
136 }
137 }
138
139 pub fn apply(
143 self,
144 value: Value,
145 op: impl Fn(Value, Value) -> ValueResult,
146 ) -> ValueResult<Self> {
147 let mut named = HashMap::new();
148 for (key, lhs_val) in self.named {
149 named.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
150 }
151
152 let mut unnamed = HashMap::new();
153 for (key, lhs_val) in self.unnamed {
154 unnamed.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
155 }
156
157 Ok(Tuple {
158 named,
159 unnamed,
160 src_ref: self.src_ref,
161 })
162 }
163
164 pub fn transform(self, op: impl Fn(Value) -> ValueResult) -> ValueResult<Self> {
166 let mut named = HashMap::new();
167 for (key, value) in self.named {
168 named.insert(key, op(value).unwrap_or_default());
169 }
170
171 let mut unnamed = HashMap::new();
172 for (key, value) in self.unnamed {
173 unnamed.insert(key, op(value).unwrap_or_default());
174 }
175
176 Ok(Tuple {
177 named,
178 unnamed,
179 src_ref: self.src_ref,
180 })
181 }
182
183 pub fn ray(&mut self) {
192 self.unnamed.retain(|_, value| {
193 if let Value::Tuple(tuple) = value {
194 tuple.ray();
195 tuple.named.drain().for_each(|(k, v)| {
196 self.named.insert(k, v);
197 });
198 false
199 } else {
200 true
201 }
202 });
203 }
204
205 pub fn multiplicity<P: FnMut(Tuple)>(&self, mut ids: IdentifierList, mut p: P) {
217 log::trace!("combining: {ids:?}:");
218
219 ids.sort();
221
222 let mut combinations = 1;
224 let mut counts: HashMap<Identifier, (_, _)> = ids
225 .into_iter()
226 .map(|id| {
227 let counter = if let Some(Value::Array(array)) = &self.named.get(&id) {
228 let len = array.len();
229 combinations *= len;
230 (0, len)
231 } else {
232 panic!("{id:?} found in tuple but no list:\n{self:#?}");
233 };
234 (id, counter)
235 })
236 .collect();
237
238 log::trace!("multiplicity: {combinations} combinations:");
239
240 for _ in 0..combinations {
242 let mut counted = false;
243
244 let mut named: Vec<_> = self.named.iter().collect();
246 named.sort_by(|lhs, rhs| lhs.0.cmp(rhs.0));
247
248 let tuple = named
249 .into_iter()
250 .map(|(id, v)| match v {
251 Value::Array(array) => {
252 if let Some((count, len)) = counts.get_mut(id) {
253 let item = (
254 id.clone(),
255 array.get(*count).expect("array index not found").clone(),
256 );
257 if !counted {
258 *count += 1;
259 if *count == *len {
260 *count = 0
261 } else {
262 counted = true;
263 }
264 }
265 item
266 } else {
267 panic!("{id:?} found in tuple but no list");
268 }
269 }
270 _ => (id.clone(), v.clone()),
271 })
272 .collect();
273 p(tuple);
274 }
275 }
276}
277
278impl ValueAccess for Tuple {
279 fn by_id(&self, id: &Identifier) -> Option<&Value> {
280 self.named.get(id)
281 }
282
283 fn by_ty(&self, ty: &Type) -> Option<&Value> {
284 self.unnamed.get(ty)
285 }
286}
287
288impl SrcReferrer for Tuple {
289 fn src_ref(&self) -> SrcRef {
290 self.src_ref.clone()
291 }
292}
293
294impl<T> From<std::slice::Iter<'_, (&'static str, T)>> for Tuple
296where
297 T: Into<Value> + Clone + std::fmt::Debug,
298{
299 fn from(iter: std::slice::Iter<'_, (&'static str, T)>) -> Self {
300 let (unnamed, named): (Vec<_>, _) = iter
301 .map(|(k, v)| (Identifier::no_ref(k), (*v).clone().into()))
302 .partition(|(k, _)| k.is_empty());
303 Self {
304 src_ref: SrcRef(None),
305 named: named.into_iter().collect(),
306 unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
307 }
308 }
309}
310
311impl FromIterator<(Identifier, Value)> for Tuple {
312 fn from_iter<T: IntoIterator<Item = (Identifier, Value)>>(iter: T) -> Self {
313 let (unnamed, named): (Vec<_>, _) = iter
314 .into_iter()
315 .map(|(k, v)| (k, v.clone()))
316 .partition(|(k, _)| k.is_empty());
317 Self {
318 src_ref: SrcRef::merge_all(
319 named
320 .iter()
321 .map(|(id, _)| id.src_ref())
322 .chain(unnamed.iter().map(|(id, _)| id.src_ref())),
323 ),
324 named: named.into_iter().collect(),
325 unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
326 }
327 }
328}
329
330impl From<Vec2> for Tuple {
331 fn from(v: Vec2) -> Self {
332 create_tuple!(x = v.x, y = v.y)
333 }
334}
335
336impl From<Vec3> for Tuple {
337 fn from(v: Vec3) -> Self {
338 create_tuple!(x = v.x, y = v.y, z = v.z)
339 }
340}
341
342impl From<Color> for Tuple {
343 fn from(color: Color) -> Self {
344 create_tuple!(r = color.r, g = color.g, b = color.b, a = color.a)
345 }
346}
347
348impl From<Size2> for Tuple {
349 fn from(size: Size2) -> Self {
350 create_tuple!(
351 width = Value::from(Quantity::length(size.width)),
352 height = Value::from(Quantity::length(size.height))
353 )
354 }
355}
356
357impl From<Tuple> for Value {
358 fn from(tuple: Tuple) -> Self {
359 Value::Tuple(Box::new(tuple))
360 }
361}
362
363impl FromIterator<Tuple> for Tuple {
364 fn from_iter<T: IntoIterator<Item = Tuple>>(iter: T) -> Self {
365 let tuples: Vec<_> = iter.into_iter().collect();
366 Self {
367 src_ref: SrcRef::merge_all(tuples.iter().map(|t| t.src_ref())),
368 named: Default::default(),
369 unnamed: tuples
370 .into_iter()
371 .map(|t| (Type::Tuple(t.tuple_type().into()), Value::Tuple(t.into())))
372 .collect(),
373 }
374 }
375}
376
377impl IntoIterator for Tuple {
378 type Item = (Identifier, Value);
379 type IntoIter = std::collections::hash_map::IntoIter<Identifier, Value>;
380
381 fn into_iter(self) -> Self::IntoIter {
382 if !self.unnamed.is_empty() {
383 log::warn!("trying to iterate Tuple with unnamed items");
384 }
385 self.named.into_iter()
386 }
387}
388
389impl<'a> TryFrom<&'a Value> for &'a Tuple {
390 type Error = ValueError;
391
392 fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
393 match value {
394 Value::Tuple(tuple) => Ok(tuple),
395 _ => Err(ValueError::CannotConvert(
396 value.to_string(),
397 "Tuple".to_string(),
398 )),
399 }
400 }
401}
402
403impl TryFrom<&Tuple> for Color {
404 type Error = ValueError;
405
406 fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
407 let (r, g, b, a) = (
408 tuple.by_id(&Identifier::no_ref("r")),
409 tuple.by_id(&Identifier::no_ref("g")),
410 tuple.by_id(&Identifier::no_ref("b")),
411 tuple
412 .by_id(&Identifier::no_ref("a"))
413 .unwrap_or(&Value::Quantity(Quantity::new(1.0, QuantityType::Scalar)))
414 .clone(),
415 );
416
417 match (r, g, b, a) {
418 (
419 Some(Value::Quantity(Quantity {
420 value: r,
421 quantity_type: QuantityType::Scalar,
422 })),
423 Some(Value::Quantity(Quantity {
424 value: g,
425 quantity_type: QuantityType::Scalar,
426 })),
427 Some(Value::Quantity(Quantity {
428 value: b,
429 quantity_type: QuantityType::Scalar,
430 })),
431 Value::Quantity(Quantity {
432 value: a,
433 quantity_type: QuantityType::Scalar,
434 }),
435 ) => Ok(Color::new(*r as f32, *g as f32, *b as f32, a as f32)),
436 _ => Err(ValueError::CannotConvertToColor(tuple.to_string())),
437 }
438 }
439}
440
441impl TryFrom<&Tuple> for Size2 {
442 type Error = ValueError;
443
444 fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
445 let (width, height) = (
446 tuple.by_id(&Identifier::no_ref("width")),
447 tuple.by_id(&Identifier::no_ref("height")),
448 );
449
450 match (width, height) {
451 (
452 Some(Value::Quantity(Quantity {
453 value: width,
454 quantity_type: QuantityType::Length,
455 })),
456 Some(Value::Quantity(Quantity {
457 value: height,
458 quantity_type: QuantityType::Length,
459 })),
460 ) => Ok(Size2 {
461 width: *width,
462 height: *height,
463 }),
464 _ => Err(ValueError::CannotConvert(tuple.to_string(), "Size2".into())),
465 }
466 }
467}
468
469impl std::fmt::Display for Tuple {
470 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
471 write!(
472 f,
473 "({items})",
474 items = {
475 let mut items = self
476 .named
477 .iter()
478 .map(|(id, v)| format!("{id}={v}"))
479 .chain(self.unnamed.values().map(|v| format!("{v}")))
480 .collect::<Vec<String>>();
481 items.sort();
482 items.join(", ")
483 }
484 )
485 }
486}
487
488impl std::fmt::Debug for Tuple {
489 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
490 write!(
491 f,
492 "({items})",
493 items = {
494 let mut items = self
495 .named
496 .iter()
497 .map(|(id, v)| format!("{id:?}={v:?}"))
498 .chain(self.unnamed.values().map(|v| format!("{v:?}")))
499 .collect::<Vec<String>>();
500 items.sort();
501 items.join(", ")
502 }
503 )
504 }
505}
506
507impl std::ops::Add<Tuple> for Tuple {
508 type Output = ValueResult<Tuple>;
509
510 fn add(self, rhs: Tuple) -> Self::Output {
511 self.combine(rhs, |lhs, rhs| lhs.clone() + rhs.clone())
512 }
513}
514
515impl std::ops::Sub<Tuple> for Tuple {
516 type Output = ValueResult<Tuple>;
517
518 fn sub(self, rhs: Tuple) -> Self::Output {
519 self.combine(rhs, |lhs, rhs| lhs.clone() - rhs.clone())
520 }
521}
522
523impl std::ops::Mul<Value> for Tuple {
524 type Output = ValueResult<Tuple>;
525
526 fn mul(self, rhs: Value) -> Self::Output {
527 self.apply(rhs, |lhs, rhs| lhs * rhs)
528 }
529}
530
531impl std::ops::Div<Value> for Tuple {
532 type Output = ValueResult<Tuple>;
533
534 fn div(self, rhs: Value) -> Self::Output {
535 self.apply(rhs, |lhs, rhs| lhs / rhs)
536 }
537}
538
539impl std::ops::Neg for Tuple {
540 type Output = ValueResult;
541
542 fn neg(self) -> Self::Output {
543 Ok(Value::Tuple(Box::new(self.transform(|value| -value)?)))
544 }
545}
546
547impl std::ops::Not for Tuple {
548 type Output = ValueResult;
549
550 fn not(self) -> Self::Output {
551 Ok(Value::Tuple(Box::new(self.transform(|value| !value)?)))
552 }
553}
554
555impl std::hash::Hash for Tuple {
556 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
557 self.unnamed.iter().for_each(|(ty, value)| {
558 ty.hash(state);
559 value.hash(state);
560 });
561 self.named.iter().for_each(|(id, value)| {
562 id.hash(state);
563 value.hash(state);
564 });
565 }
566}
567
568impl Ty for Tuple {
569 fn ty(&self) -> Type {
570 Type::Tuple(Box::new(self.tuple_type()))
571 }
572}
573
574#[test]
575fn tuple_equal() {
576 assert_eq!(
577 tuple!("(v=1.0m³, l=1.0m, a=1.0m²)"),
578 tuple!("(l=1.0m, a=1.0m², v=1.0m³)")
579 );
580}
581
582#[test]
583fn tuple_not_equal() {
584 assert_ne!(
585 tuple!("(d=1.0g/mm³, l=1.0m, a=1.0m²)"),
586 tuple!("(l=1.0m, a=1.0m², v=1.0m³)")
587 );
588 assert_ne!(
589 tuple!("(l=1.0m, a=1.0m²)"),
590 tuple!("(l=1.0m, a=1.0m², v=1.0m³)")
591 );
592}
593
594#[test]
595fn multiplicity_check() {
596 let tuple = tuple!("(x = [1, 2, 3], y = [1, 2], z = 1)");
597
598 let ids: IdentifierList = ["x".into(), "y".into()].into_iter().collect();
599 tuple.multiplicity(ids, |tuple| println!("{tuple}"));
600}