mod array;
mod matrix;
mod quantity;
mod tuple;
mod value_access;
mod value_error;
mod value_list;
pub use array::*;
use derive_more::From;
pub use matrix::*;
pub use quantity::*;
pub use tuple::*;
pub use value_access::*;
pub use value_error::*;
pub use value_list::*;
use crate::{lower::ir, model::*, ty::*};
use microcad_core::*;
use microcad_lang_base::SrcRef;
pub(crate) type ValueResult<Type = Value> = std::result::Result<Type, ValueError>;
#[derive(Clone, Debug, Default, PartialEq, From)]
pub enum Value {
#[default]
None,
Quantity(Quantity),
Bool(bool),
Integer(Integer),
String(String),
Array(Array),
Tuple(Box<Tuple>),
Matrix(Box<Matrix>),
Model(Model),
Return(Box<Value>),
}
impl Value {
pub fn is_invalid(&self) -> bool {
matches!(self, Value::None)
}
pub fn pow(&self, rhs: &Value) -> ValueResult {
match (&self, rhs) {
(Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity(lhs.pow(rhs))),
(Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity(lhs.pow_int(rhs))),
(Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs.pow(*rhs as u32))),
_ => Err(ValueError::InvalidOperator("^".to_string())),
}
}
pub fn binary_op(lhs: Value, rhs: Value, op: &str) -> ValueResult {
match op {
"+" => lhs + rhs,
"-" => lhs - rhs,
"*" => lhs * rhs,
"/" => lhs / rhs,
"^" => lhs.pow(&rhs),
"&" | "and" => lhs & rhs,
"|" | "or" => lhs | rhs,
">" => Ok(Value::Bool(lhs > rhs)),
"<" => Ok(Value::Bool(lhs < rhs)),
"≤" | "<=" => Ok(Value::Bool(lhs <= rhs)),
"≥" | ">=" => Ok(Value::Bool(lhs >= rhs)),
"~" => todo!("implement near ~="),
"==" => Ok(Value::Bool(lhs == rhs)),
"!=" => Ok(Value::Bool(lhs != rhs)),
_ => unimplemented!("{op:?}"),
}
}
pub fn unary_op(self, op: &str) -> ValueResult {
match op {
"-" => -self,
"!" => !self,
_ => Err(ValueError::InvalidOperator(op.to_string())),
}
}
pub fn try_string(&self) -> Result<String, ValueError> {
match self {
Value::String(s) => return Ok(s.clone()),
Value::Integer(i) => return Ok(i.to_string()),
_ => {}
}
Err(ValueError::CannotConvert(self.to_string(), "String".into()))
}
pub fn try_scalar(&self) -> Result<Scalar, ValueError> {
match self {
Value::Quantity(q) => return Ok(q.value),
Value::Integer(i) => return Ok((*i) as f64),
_ => {}
}
Err(ValueError::CannotConvert(self.to_string(), "Scalar".into()))
}
pub fn un_return(&self) -> Value {
match self {
Value::Return(value) => value.as_ref().clone(),
value => value.clone(),
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Value::Integer(lhs), Value::Integer(rhs)) => lhs.partial_cmp(rhs),
(Value::Quantity(lhs), Value::Quantity(rhs)) => lhs.partial_cmp(rhs),
(
Value::Quantity(Quantity {
value,
quantity_type: QuantityType::Scalar,
..
}),
Value::Integer(rhs),
) => value.partial_cmp(&(*rhs as Scalar)),
_ => {
log::warn!("unhandled type mismatch between {self} and {other}");
None
}
}
}
}
impl crate::ty::Ty for Value {
fn ty(&self) -> Type {
match self {
Value::None => Type::Invalid,
Value::Integer(_) => Type::Integer,
Value::Quantity(q) => q.ty(),
Value::Bool(_) => Type::Bool,
Value::String(_) => Type::String,
Value::Array(list) => list.ty(),
Value::Tuple(tuple) => tuple.ty(),
Value::Matrix(matrix) => matrix.ty(),
Value::Model(_) => Type::Model,
Value::Return(r) => r.ty(),
}
}
}
impl std::ops::Neg for Value {
type Output = ValueResult;
fn neg(self) -> Self::Output {
match self {
Value::Integer(n) => Ok(Value::Integer(-n)),
Value::Quantity(q) => Ok(Value::Quantity(q.neg())),
Value::Array(a) => -a,
Value::Tuple(t) => -t.as_ref().clone(),
_ => Err(ValueError::InvalidOperator("-".into())),
}
}
}
impl std::ops::Not for Value {
type Output = ValueResult;
fn not(self) -> Self::Output {
match self {
Value::Bool(b) => Ok(Value::Bool(!b)),
Value::Array(a) => !a,
Value::Tuple(t) => !t.as_ref().clone(),
_ => Err(ValueError::InvalidOperator("!".into())),
}
}
}
impl std::ops::Add for Value {
type Output = ValueResult;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs + rhs)),
(Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
(Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
(Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
(Value::String(lhs), Value::String(rhs)) => Ok(Value::String(lhs + &rhs)),
(Value::Array(lhs), Value::Array(rhs)) => {
if lhs.ty() != rhs.ty() {
return Err(ValueError::CannotCombineVecOfDifferentType(
lhs.ty(),
rhs.ty(),
));
}
Ok(Value::Array(Array::from_values(
lhs.iter().chain(rhs.iter()).cloned().collect(),
lhs.ty(),
)))
}
(Value::Array(lhs), rhs) => Ok((lhs + rhs)?),
(Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs + *rhs)?.into()),
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} + {rhs}"))),
}
}
}
impl std::ops::Sub for Value {
type Output = ValueResult;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs - rhs)),
(Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
(Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
(Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
(Value::Array(lhs), rhs) => Ok((lhs - rhs)?),
(Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs - *rhs)?.into()),
(Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
lhs.boolean_op(microcad_core::BooleanOp::Subtract, rhs),
)),
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} - {rhs}"))),
}
}
}
impl std::ops::Mul for Value {
type Output = ValueResult;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Integer(lhs), Value::Model(rhs)) => Ok(Value::Model(
Models::from(rhs.multiply(lhs)).to_multiplicity(SrcRef::none()),
)),
(Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs * rhs)),
(Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
(Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
(Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
(Value::Array(array), value) | (value, Value::Array(array)) => Ok((array * value)?),
(Value::Tuple(tuple), value) | (value, Value::Tuple(tuple)) => {
Ok((tuple.as_ref().clone() * value)?.into())
}
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} * {rhs}"))),
}
}
}
impl std::ops::Mul<Unit> for Value {
type Output = ValueResult;
fn mul(self, unit: Unit) -> Self::Output {
match (self, unit.ty()) {
(value, Type::Quantity(QuantityType::Scalar)) | (value, Type::Integer) => Ok(value),
(Value::Integer(i), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
Quantity::new(unit.normalize(i as Scalar), quantity_type),
)),
(Value::Quantity(quantity), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
(quantity * Quantity::new(unit.normalize(1.0), quantity_type))?,
)),
(Value::Array(array), Type::Quantity(quantity_type)) => {
Ok((array * Value::Quantity(Quantity::new(unit.normalize(1.0), quantity_type)))?)
}
(value, _) => Err(ValueError::CannotAddUnitToValueWithUnit(value.to_string())),
}
}
}
impl std::ops::Div for Value {
type Output = ValueResult;
fn div(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Integer(lhs), Value::Integer(rhs)) => {
Ok(Value::Quantity((lhs as Scalar / rhs as Scalar).into()))
}
(Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
(Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
(Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
(Value::Array(array), value) => Ok((array / value)?),
(Value::Tuple(tuple), value) => Ok((tuple.as_ref().clone() / value)?.into()),
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} / {rhs}"))),
}
}
}
impl std::ops::BitOr for Value {
type Output = ValueResult;
fn bitor(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
lhs.boolean_op(microcad_core::BooleanOp::Union, rhs),
)),
(Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)),
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} | {rhs}"))),
}
}
}
impl std::ops::BitAnd for Value {
type Output = ValueResult;
fn bitand(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Value::Model(lhs), Value::Model(rhs)) => {
Ok(Value::Model(lhs.boolean_op(BooleanOp::Intersect, rhs)))
}
(Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)),
(lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} & {rhs}"))),
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Value::None => write!(f, "<NO VALUE>"),
Value::Integer(n) => write!(f, "{n}"),
Value::Quantity(q) => write!(f, "{q}"),
Value::Bool(b) => write!(f, "{b}"),
Value::String(s) => write!(f, "{s}"),
Value::Array(l) => write!(f, "{l}"),
Value::Tuple(t) => write!(f, "{t}"),
Value::Matrix(m) => write!(f, "{m}"),
Value::Model(n) => write!(f, "{n}"),
Value::Return(r) => write!(f, "{r}"),
}
}
}
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Value::None => std::mem::discriminant(&Value::None).hash(state),
Value::Quantity(quantity) => quantity.hash(state),
Value::Bool(b) => b.hash(state),
Value::Integer(i) => i.hash(state),
Value::String(s) => s.hash(state),
Value::Array(array) => array.hash(state),
Value::Tuple(tuple) => tuple.hash(state),
Value::Matrix(matrix) => matrix.hash(state),
Value::Model(model) => model.hash(state),
Value::Return(value) => value.hash(state),
}
}
}
macro_rules! impl_try_from {
($($variant:ident),+ => $ty:ty ) => {
impl TryFrom<Value> for $ty {
type Error = ValueError;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
match value {
$(Value::$variant(v) => Ok(v),)*
value => Err(ValueError::CannotConvert(value.to_string(), stringify!($ty).into())),
}
}
}
impl TryFrom<&Value> for $ty {
type Error = ValueError;
fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
match value {
$(Value::$variant(v) => Ok(v.clone().into()),)*
value => Err(ValueError::CannotConvert(value.to_string(), stringify!($ty).into())),
}
}
}
};
}
impl_try_from!(Integer => i64);
impl_try_from!(Bool => bool);
impl_try_from!(String => String);
impl TryFrom<&Value> for Scalar {
type Error = ValueError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Integer(i) => Ok(*i as Scalar),
Value::Quantity(Quantity {
value,
quantity_type: QuantityType::Scalar,
..
}) => Ok(*value),
_ => Err(ValueError::CannotConvert(
value.to_string(),
"Scalar".into(),
)),
}
}
}
impl TryFrom<Value> for Scalar {
type Error = ValueError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Integer(i) => Ok(i as Scalar),
Value::Quantity(Quantity {
value,
quantity_type: QuantityType::Scalar,
..
}) => Ok(value),
_ => Err(ValueError::CannotConvert(
value.to_string(),
"Scalar".into(),
)),
}
}
}
impl TryFrom<&Value> for Angle {
type Error = ValueError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Quantity(Quantity {
value,
quantity_type: QuantityType::Angle,
..
}) => Ok(cgmath::Rad(*value)),
_ => Err(ValueError::CannotConvert(value.to_string(), "Angle".into())),
}
}
}
impl TryFrom<&Value> for Length {
type Error = ValueError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Quantity(Quantity {
value,
quantity_type: QuantityType::Length,
..
}) => Ok(Length(*value)),
_ => Err(ValueError::CannotConvert(
value.to_string(),
"Length".into(),
)),
}
}
}
impl TryFrom<&Value> for Size2 {
type Error = ValueError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Tuple(tuple) => Ok(tuple.as_ref().try_into()?),
_ => Err(ValueError::CannotConvert(value.to_string(), "Size2".into())),
}
}
}
impl TryFrom<&Value> for Mat3 {
type Error = ValueError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
if let Value::Matrix(m) = value {
if let Matrix::Matrix3(matrix3) = m.as_ref() {
return Ok(*matrix3);
}
}
Err(ValueError::CannotConvert(
value.to_string(),
"Matrix3".into(),
))
}
}
impl From<usize> for Value {
fn from(value: usize) -> Self {
Value::Integer(value as Integer)
}
}
impl From<f32> for Value {
fn from(f: f32) -> Self {
Value::Quantity((f as Scalar).into())
}
}
impl From<Scalar> for Value {
fn from(scalar: Scalar) -> Self {
Value::Quantity(scalar.into())
}
}
impl From<Length> for Value {
fn from(length: Length) -> Self {
Value::Quantity(length.into())
}
}
impl From<Size2> for Value {
fn from(value: Size2) -> Self {
Self::Tuple(Box::new(value.into()))
}
}
impl From<Color> for Value {
fn from(color: Color) -> Self {
Self::Tuple(Box::new(color.into()))
}
}
impl From<Vec3> for Value {
fn from(v: Vec3) -> Self {
Self::Tuple(Box::new(v.into()))
}
}
impl FromIterator<Value> for Value {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
Self::Array(iter.into_iter().collect())
}
}
impl AttributesAccess for Value {
fn get_attributes_by_id(&self, id: &ir::Identifier) -> Vec<crate::model::Attribute> {
match self {
Value::Model(model) => model.get_attributes_by_id(id),
_ => Vec::default(),
}
}
}
#[cfg(test)]
fn integer(value: i64) -> Value {
Value::Integer(value)
}
#[cfg(test)]
fn scalar(value: f64) -> Value {
Value::Quantity(Quantity::new(value, QuantityType::Scalar))
}
#[cfg(test)]
fn check(result: ValueResult, value: Value) {
let result = result.expect("error result");
assert_eq!(result, value);
}
#[test]
fn test_value_integer() {
let u = || integer(2);
let v = || integer(5);
let w = || scalar(5.0);
check(u() + v(), integer(2 + 5));
check(u() - v(), integer(2 - 5));
check(u() * v(), integer(2 * 5));
check(u() / v(), scalar(2.0 / 5.0));
check(-u(), integer(-2));
check(u() + w(), scalar(2 as Scalar + 5.0));
check(u() - w(), scalar(2 as Scalar - 5.0));
check(u() * w(), scalar(2 as Scalar * 5.0));
check(u() / w(), scalar(2.0 / 5.0));
}
#[test]
fn test_value_scalar() {
let u = || scalar(2.0);
let v = || scalar(5.0);
let w = || integer(5);
check(u() + v(), scalar(2.0 + 5.0));
check(u() - v(), scalar(2.0 - 5.0));
check(u() * v(), scalar(2.0 * 5.0));
check(u() / v(), scalar(2.0 / 5.0));
check(-u(), scalar(-2.0));
check(u() + w(), scalar(2.0 + 5.0));
check(u() - w(), scalar(2.0 - 5.0));
check(u() * w(), scalar(2.0 * 5.0));
check(u() / w(), scalar(2.0 / 5.0));
}