use microcad_core::hash::HashMap;
use microcad_lang_base::{Identifier, SrcReferrer};
use microcad_lang_proc_macros::SrcReferrer;
use crate::{ty::*, value::*};
#[derive(Clone, Debug, Default, PartialEq, SrcReferrer)]
pub struct Tuple {
pub(crate) named: HashMap<ir::Identifier, Value>,
pub(crate) unnamed: HashMap<Type, Value>,
pub(crate) src_ref: SrcRef,
}
#[macro_export]
macro_rules! create_tuple_value {
($($key:ident = $value:expr),*) => {
Value::Tuple(Box::new($crate::create_tuple!($( $key = $value ),*)))
};
}
#[macro_export]
macro_rules! create_tuple {
($($key:ident = $value:expr),*) => {
[$( (stringify!($key), $crate::value::Value::try_from($value).expect("Valid value")) ),* ]
.iter()
.into()
};
}
impl Tuple {
pub fn new_named(
named: microcad_core::hash::HashMap<ir::Identifier, Value>,
src_ref: SrcRef,
) -> Self {
Self {
named,
unnamed: HashMap::default(),
src_ref,
}
}
pub fn insert(&mut self, id: ir::Identifier, value: Value) {
if id.is_empty() {
self.unnamed.insert(value.ty(), value);
} else {
self.named.insert(id, value);
}
}
pub fn named_iter(&self) -> std::collections::hash_map::Iter<'_, ir::Identifier, Value> {
if !self.unnamed.is_empty() {
log::warn!("using named_iter() on a tuple which has unnamed items too")
}
self.named.iter()
}
pub fn tuple_type(&self) -> TupleType {
TupleType {
named: self
.named
.iter()
.map(|(id, v)| (id.clone(), v.ty()))
.collect(),
unnamed: self.unnamed.values().map(|v| v.ty()).collect(),
}
}
pub fn combine(
self,
rhs: Tuple,
op: impl Fn(Value, Value) -> ValueResult,
) -> ValueResult<Self> {
if self.ty() == rhs.ty() {
let mut named = self.named;
for (key, rhs_val) in rhs.named {
named
.entry(key)
.and_modify(|lhs_val| {
*lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
})
.or_insert(rhs_val);
}
let mut unnamed = self.unnamed;
for (key, rhs_val) in rhs.unnamed {
unnamed
.entry(key)
.and_modify(|lhs_val| {
*lhs_val = op(lhs_val.clone(), rhs_val.clone()).unwrap_or_default()
})
.or_insert(rhs_val);
}
Ok(Tuple {
named,
unnamed,
src_ref: self.src_ref,
})
} else {
Err(ValueError::TupleTypeMismatch {
lhs: self.ty(),
rhs: rhs.ty(),
})
}
}
pub fn apply(
self,
value: Value,
op: impl Fn(Value, Value) -> ValueResult,
) -> ValueResult<Self> {
let mut named = HashMap::default();
for (key, lhs_val) in self.named {
named.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
}
let mut unnamed = HashMap::default();
for (key, lhs_val) in self.unnamed {
unnamed.insert(key, op(lhs_val, value.clone()).unwrap_or_default());
}
Ok(Tuple {
named,
unnamed,
src_ref: self.src_ref,
})
}
pub fn transform(self, op: impl Fn(Value) -> ValueResult) -> ValueResult<Self> {
let mut named = HashMap::default();
for (key, value) in self.named {
named.insert(key, op(value).unwrap_or_default());
}
let mut unnamed = HashMap::default();
for (key, value) in self.unnamed {
unnamed.insert(key, op(value).unwrap_or_default());
}
Ok(Tuple {
named,
unnamed,
src_ref: self.src_ref,
})
}
pub fn ray(&mut self) {
self.unnamed.retain(|_, value| {
if let Value::Tuple(tuple) = value {
tuple.ray();
tuple.named.drain().for_each(|(k, v)| {
self.named.insert(k, v);
});
false
} else {
true
}
});
}
pub fn multiplicity<P: FnMut(Tuple)>(&self, mut ids: ir::IdentifierList, mut p: P) {
log::trace!("combining: {ids:?}:");
ids.sort();
let mut combinations = 1;
let mut counts: HashMap<Identifier, (_, _)> = ids
.into_iter()
.map(|id| {
let counter = if let Some(Value::Array(array)) = &self.named.get(&id) {
let len = array.len();
combinations *= len;
(0, len)
} else {
panic!("{id:?} found in tuple but no list:\n{self:#?}");
};
(id, counter)
})
.collect();
log::trace!("multiplicity: {combinations} combinations:");
for _ in 0..combinations {
let mut counted = false;
let mut named: Vec<_> = self.named.iter().collect();
named.sort_by(|lhs, rhs| lhs.0.cmp(rhs.0));
let tuple = named
.into_iter()
.map(|(id, v)| match v {
Value::Array(array) => {
if let Some((count, len)) = counts.get_mut(id) {
let item = (
id.clone(),
array.get(*count).expect("array index not found").clone(),
);
if !counted {
*count += 1;
if *count == *len {
*count = 0
} else {
counted = true;
}
}
item
} else {
panic!("{id:?} found in tuple but no list");
}
}
_ => (id.clone(), v.clone()),
})
.collect();
p(tuple);
}
}
}
impl ValueAccess for Tuple {
fn by_id(&self, id: &Identifier) -> Option<&Value> {
self.named.get(id)
}
fn by_ty(&self, ty: &Type) -> Option<&Value> {
self.unnamed.get(ty)
}
}
impl<T> From<std::slice::Iter<'_, (&'static str, T)>> for Tuple
where
T: Into<Value> + Clone + std::fmt::Debug,
{
fn from(iter: std::slice::Iter<'_, (&'static str, T)>) -> Self {
let (unnamed, named): (Vec<_>, _) = iter
.map(|(k, v)| (Identifier::no_ref(k), (*v).clone().into()))
.partition(|(k, _)| k.is_empty());
Self {
src_ref: SrcRef::none(),
named: named.into_iter().collect(),
unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
}
}
}
impl FromIterator<(Identifier, Value)> for Tuple {
fn from_iter<T: IntoIterator<Item = (Identifier, Value)>>(iter: T) -> Self {
let (unnamed, named): (Vec<_>, _) = iter
.into_iter()
.map(|(k, v)| (k, v.clone()))
.partition(|(k, _)| k.is_empty());
Self {
src_ref: SrcRef::merge_all(
named
.iter()
.map(|(id, _)| id.src_ref())
.chain(unnamed.iter().map(|(id, _)| id.src_ref())),
),
named: named.into_iter().collect(),
unnamed: unnamed.into_iter().map(|(_, v)| (v.ty(), v)).collect(),
}
}
}
impl From<Vec2> for Tuple {
fn from(v: Vec2) -> Self {
create_tuple!(x = v.x, y = v.y)
}
}
impl From<Vec3> for Tuple {
fn from(v: Vec3) -> Self {
create_tuple!(x = v.x, y = v.y, z = v.z)
}
}
impl From<Color> for Tuple {
fn from(color: Color) -> Self {
create_tuple!(r = color.r, g = color.g, b = color.b, a = color.a)
}
}
impl From<Size2> for Tuple {
fn from(size: Size2) -> Self {
create_tuple!(
width = Value::from(Quantity::length(size.width)),
height = Value::from(Quantity::length(size.height))
)
}
}
impl From<Tuple> for Value {
fn from(tuple: Tuple) -> Self {
Value::Tuple(Box::new(tuple))
}
}
impl FromIterator<Tuple> for Tuple {
fn from_iter<T: IntoIterator<Item = Tuple>>(iter: T) -> Self {
let tuples: Vec<_> = iter.into_iter().collect();
Self {
src_ref: SrcRef::merge_all(tuples.iter().map(|t| t.src_ref())),
named: Default::default(),
unnamed: tuples
.into_iter()
.map(|t| (Type::Tuple(t.tuple_type().into()), Value::Tuple(t.into())))
.collect(),
}
}
}
impl IntoIterator for Tuple {
type Item = (Identifier, Value);
type IntoIter = std::collections::hash_map::IntoIter<Identifier, Value>;
fn into_iter(self) -> Self::IntoIter {
if !self.unnamed.is_empty() {
log::warn!("trying to iterate Tuple with unnamed items");
}
self.named.into_iter()
}
}
impl<'a> TryFrom<&'a Value> for &'a Tuple {
type Error = ValueError;
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Tuple(tuple) => Ok(tuple),
_ => Err(ValueError::CannotConvert(
value.to_string(),
"Tuple".to_string(),
)),
}
}
}
impl TryFrom<&Tuple> for Color {
type Error = ValueError;
fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
let (r, g, b, a) = (
tuple.by_id(&Identifier::no_ref("r")),
tuple.by_id(&Identifier::no_ref("g")),
tuple.by_id(&Identifier::no_ref("b")),
tuple
.by_id(&Identifier::no_ref("a"))
.unwrap_or(&Value::Quantity(Quantity::new(1.0, QuantityType::Scalar)))
.clone(),
);
match (r, g, b, a) {
(
Some(Value::Quantity(Quantity {
value: r,
quantity_type: QuantityType::Scalar,
..
})),
Some(Value::Quantity(Quantity {
value: g,
quantity_type: QuantityType::Scalar,
..
})),
Some(Value::Quantity(Quantity {
value: b,
quantity_type: QuantityType::Scalar,
..
})),
Value::Quantity(Quantity {
value: a,
quantity_type: QuantityType::Scalar,
..
}),
) => Ok(Color::new(*r as f32, *g as f32, *b as f32, a as f32)),
_ => Err(ValueError::CannotConvertToColor(tuple.to_string())),
}
}
}
impl TryFrom<&Tuple> for Size2 {
type Error = ValueError;
fn try_from(tuple: &Tuple) -> Result<Self, Self::Error> {
let (width, height) = (
tuple.by_id(&Identifier::no_ref("width")),
tuple.by_id(&Identifier::no_ref("height")),
);
match (width, height) {
(
Some(Value::Quantity(Quantity {
value: width,
quantity_type: QuantityType::Length,
..
})),
Some(Value::Quantity(Quantity {
value: height,
quantity_type: QuantityType::Length,
..
})),
) => Ok(Size2 {
width: *width,
height: *height,
}),
_ => Err(ValueError::CannotConvert(tuple.to_string(), "Size2".into())),
}
}
}
impl std::fmt::Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"({items})",
items = {
let mut items = self
.named
.iter()
.map(|(id, v)| format!("{id}={v}"))
.chain(self.unnamed.values().map(|v| format!("{v}")))
.collect::<Vec<String>>();
items.sort();
items.join(", ")
}
)
}
}
impl std::ops::Add<Tuple> for Tuple {
type Output = ValueResult<Tuple>;
fn add(self, rhs: Tuple) -> Self::Output {
self.combine(rhs, |lhs, rhs| lhs.clone() + rhs.clone())
}
}
impl std::ops::Sub<Tuple> for Tuple {
type Output = ValueResult<Tuple>;
fn sub(self, rhs: Tuple) -> Self::Output {
self.combine(rhs, |lhs, rhs| lhs.clone() - rhs.clone())
}
}
impl std::ops::Mul<Value> for Tuple {
type Output = ValueResult<Tuple>;
fn mul(self, rhs: Value) -> Self::Output {
self.apply(rhs, |lhs, rhs| lhs * rhs)
}
}
impl std::ops::Div<Value> for Tuple {
type Output = ValueResult<Tuple>;
fn div(self, rhs: Value) -> Self::Output {
self.apply(rhs, |lhs, rhs| lhs / rhs)
}
}
impl std::ops::Neg for Tuple {
type Output = ValueResult;
fn neg(self) -> Self::Output {
Ok(Value::Tuple(Box::new(self.transform(|value| -value)?)))
}
}
impl std::ops::Not for Tuple {
type Output = ValueResult;
fn not(self) -> Self::Output {
Ok(Value::Tuple(Box::new(self.transform(|value| !value)?)))
}
}
impl std::hash::Hash for Tuple {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.unnamed.iter().for_each(|(ty, value)| {
ty.hash(state);
value.hash(state);
});
self.named.iter().for_each(|(id, value)| {
id.hash(state);
value.hash(state);
});
}
}
impl Ty for Tuple {
fn ty(&self) -> Type {
Type::Tuple(Box::new(self.tuple_type()))
}
}