1use microcad_core::hash::HashMap;
7
8use microcad_lang_base::{Identifier, SrcReferrer};
9use microcad_lang_proc_macros::SrcReferrer;
10
11use crate::{ty::*, value::*};
12
13#[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#[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#[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 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 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 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 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 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 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 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 pub fn multiplicity<P: FnMut(Tuple)>(&self, mut ids: ir::IdentifierList, mut p: P) {
183 log::trace!("combining: {ids:?}:");
184
185 ids.sort();
187
188 let mut combinations = 1;
190 let mut counts: HashMap<Identifier, (_, _)> = ids
191 .into_iter()
192 .map(|id| {
193 let counter = if let Some(Value::Array(array)) = &self.named.get(&id) {
194 let len = array.len();
195 combinations *= len;
196 (0, len)
197 } else {
198 panic!("{id:?} found in tuple but no list:\n{self:#?}");
199 };
200 (id, counter)
201 })
202 .collect();
203
204 log::trace!("multiplicity: {combinations} combinations:");
205
206 for _ in 0..combinations {
208 let mut counted = false;
209
210 let mut named: Vec<_> = self.named.iter().collect();
212 named.sort_by(|lhs, rhs| lhs.0.cmp(rhs.0));
213
214 let tuple = named
215 .into_iter()
216 .map(|(id, v)| match v {
217 Value::Array(array) => {
218 if let Some((count, len)) = counts.get_mut(id) {
219 let item = (
220 id.clone(),
221 array.get(*count).expect("array index not found").clone(),
222 );
223 if !counted {
224 *count += 1;
225 if *count == *len {
226 *count = 0
227 } else {
228 counted = true;
229 }
230 }
231 item
232 } else {
233 panic!("{id:?} found in tuple but no list");
234 }
235 }
236 _ => (id.clone(), v.clone()),
237 })
238 .collect();
239 p(tuple);
240 }
241 }
242}
243
244impl ValueAccess for Tuple {
245 fn by_id(&self, id: &Identifier) -> Option<&Value> {
246 self.named.get(id)
247 }
248
249 fn by_ty(&self, ty: &Type) -> Option<&Value> {
250 self.unnamed.get(ty)
251 }
252}
253
254impl<T> From<std::slice::Iter<'_, (&'static str, T)>> for Tuple
256where
257 T: Into<Value> + Clone + std::fmt::Debug,
258{
259 fn from(iter: std::slice::Iter<'_, (&'static str, T)>) -> Self {
260 let (unnamed, named): (Vec<_>, _) = iter
261 .map(|(k, v)| (Identifier::no_ref(k), (*v).clone().into()))
262 .partition(|(k, _)| k.is_empty());
263 Self {
264 src_ref: SrcRef::none(),
265 named: named.into_iter().collect(),
266 unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
267 }
268 }
269}
270
271impl FromIterator<(Identifier, Value)> for Tuple {
272 fn from_iter<T: IntoIterator<Item = (Identifier, Value)>>(iter: T) -> Self {
273 let (unnamed, named): (Vec<_>, _) = iter
274 .into_iter()
275 .map(|(k, v)| (k, v.clone()))
276 .partition(|(k, _)| k.is_empty());
277 Self {
278 src_ref: SrcRef::merge_all(
279 named
280 .iter()
281 .map(|(id, _)| id.src_ref())
282 .chain(unnamed.iter().map(|(id, _)| id.src_ref())),
283 ),
284 named: named.into_iter().collect(),
285 unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
286 }
287 }
288}
289
290impl From<Vec2> for Tuple {
291 fn from(v: Vec2) -> Self {
292 create_tuple!(x = v.x, y = v.y)
293 }
294}
295
296impl From<Vec3> for Tuple {
297 fn from(v: Vec3) -> Self {
298 create_tuple!(x = v.x, y = v.y, z = v.z)
299 }
300}
301
302impl From<Color> for Tuple {
303 fn from(color: Color) -> Self {
304 create_tuple!(r = color.r, g = color.g, b = color.b, a = color.a)
305 }
306}
307
308impl From<Size2> for Tuple {
309 fn from(size: Size2) -> Self {
310 create_tuple!(
311 width = Value::from(Quantity::length(size.width)),
312 height = Value::from(Quantity::length(size.height))
313 )
314 }
315}
316
317impl From<Tuple> for Value {
318 fn from(tuple: Tuple) -> Self {
319 Value::Tuple(Box::new(tuple))
320 }
321}
322
323impl FromIterator<Tuple> for Tuple {
324 fn from_iter<T: IntoIterator<Item = Tuple>>(iter: T) -> Self {
325 let tuples: Vec<_> = iter.into_iter().collect();
326 Self {
327 src_ref: SrcRef::merge_all(tuples.iter().map(|t| t.src_ref())),
328 named: Default::default(),
329 unnamed: tuples
330 .into_iter()
331 .map(|t| (Type::Tuple(t.tuple_type().into()), Value::Tuple(t.into())))
332 .collect(),
333 }
334 }
335}
336
337impl IntoIterator for Tuple {
338 type Item = (Identifier, Value);
339 type IntoIter = std::collections::hash_map::IntoIter<Identifier, Value>;
340
341 fn into_iter(self) -> Self::IntoIter {
342 if !self.unnamed.is_empty() {
343 log::warn!("trying to iterate Tuple with unnamed items");
344 }
345 self.named.into_iter()
346 }
347}
348
349impl<'a> TryFrom<&'a Value> for &'a Tuple {
350 type Error = ValueError;
351
352 fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
353 match value {
354 Value::Tuple(tuple) => Ok(tuple),
355 _ => Err(ValueError::CannotConvert(
356 value.to_string(),
357 "Tuple".to_string(),
358 )),
359 }
360 }
361}
362
363impl TryFrom<&Tuple> for Color {
364 type Error = ValueError;
365
366 fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
367 let (r, g, b, a) = (
368 tuple.by_id(&Identifier::no_ref("r")),
369 tuple.by_id(&Identifier::no_ref("g")),
370 tuple.by_id(&Identifier::no_ref("b")),
371 tuple
372 .by_id(&Identifier::no_ref("a"))
373 .unwrap_or(&Value::Quantity(Quantity::new(1.0, QuantityType::Scalar)))
374 .clone(),
375 );
376
377 match (r, g, b, a) {
378 (
379 Some(Value::Quantity(Quantity {
380 value: r,
381 quantity_type: QuantityType::Scalar,
382 ..
383 })),
384 Some(Value::Quantity(Quantity {
385 value: g,
386 quantity_type: QuantityType::Scalar,
387 ..
388 })),
389 Some(Value::Quantity(Quantity {
390 value: b,
391 quantity_type: QuantityType::Scalar,
392 ..
393 })),
394 Value::Quantity(Quantity {
395 value: a,
396 quantity_type: QuantityType::Scalar,
397 ..
398 }),
399 ) => Ok(Color::new(*r as f32, *g as f32, *b as f32, a as f32)),
400 _ => Err(ValueError::CannotConvertToColor(tuple.to_string())),
401 }
402 }
403}
404
405impl TryFrom<&Tuple> for Size2 {
406 type Error = ValueError;
407
408 fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
409 let (width, height) = (
410 tuple.by_id(&Identifier::no_ref("width")),
411 tuple.by_id(&Identifier::no_ref("height")),
412 );
413
414 match (width, height) {
415 (
416 Some(Value::Quantity(Quantity {
417 value: width,
418 quantity_type: QuantityType::Length,
419 ..
420 })),
421 Some(Value::Quantity(Quantity {
422 value: height,
423 quantity_type: QuantityType::Length,
424 ..
425 })),
426 ) => Ok(Size2 {
427 width: *width,
428 height: *height,
429 }),
430 _ => Err(ValueError::CannotConvert(tuple.to_string(), "Size2".into())),
431 }
432 }
433}
434
435impl std::fmt::Display for Tuple {
436 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
437 write!(
438 f,
439 "({items})",
440 items = {
441 let mut items = self
442 .named
443 .iter()
444 .map(|(id, v)| format!("{id}={v}"))
445 .chain(self.unnamed.values().map(|v| format!("{v}")))
446 .collect::<Vec<String>>();
447 items.sort();
448 items.join(", ")
449 }
450 )
451 }
452}
453
454impl std::ops::Add<Tuple> for Tuple {
455 type Output = ValueResult<Tuple>;
456
457 fn add(self, rhs: Tuple) -> Self::Output {
458 self.combine(rhs, |lhs, rhs| lhs.clone() + rhs.clone())
459 }
460}
461
462impl std::ops::Sub<Tuple> for Tuple {
463 type Output = ValueResult<Tuple>;
464
465 fn sub(self, rhs: Tuple) -> Self::Output {
466 self.combine(rhs, |lhs, rhs| lhs.clone() - rhs.clone())
467 }
468}
469
470impl std::ops::Mul<Value> for Tuple {
471 type Output = ValueResult<Tuple>;
472
473 fn mul(self, rhs: Value) -> Self::Output {
474 self.apply(rhs, |lhs, rhs| lhs * rhs)
475 }
476}
477
478impl std::ops::Div<Value> for Tuple {
479 type Output = ValueResult<Tuple>;
480
481 fn div(self, rhs: Value) -> Self::Output {
482 self.apply(rhs, |lhs, rhs| lhs / rhs)
483 }
484}
485
486impl std::ops::Neg for Tuple {
487 type Output = ValueResult;
488
489 fn neg(self) -> Self::Output {
490 Ok(Value::Tuple(Box::new(self.transform(|value| -value)?)))
491 }
492}
493
494impl std::ops::Not for Tuple {
495 type Output = ValueResult;
496
497 fn not(self) -> Self::Output {
498 Ok(Value::Tuple(Box::new(self.transform(|value| !value)?)))
499 }
500}
501
502impl std::hash::Hash for Tuple {
503 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
504 self.unnamed.iter().for_each(|(ty, value)| {
505 ty.hash(state);
506 value.hash(state);
507 });
508 self.named.iter().for_each(|(id, value)| {
509 id.hash(state);
510 value.hash(state);
511 });
512 }
513}
514
515impl Ty for Tuple {
516 fn ty(&self) -> Type {
517 Type::Tuple(Box::new(self.tuple_type()))
518 }
519}