microcad_lang/ty/
type.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad Type
5
6use crate::{syntax::*, ty::*};
7
8/// µcad Basic Types
9#[derive(Clone, PartialEq, Eq, Hash)]
10pub enum Type {
11    /// Invalid type (used for error handling)
12    Invalid,
13    /// A 64-bit integer number: `Interger: 10`.
14    Integer,
15    /// A quantity type: `Scalar: 1.0`, `Length: 1.0mm`
16    Quantity(QuantityType),
17    /// A string.
18    String,
19    /// A boolean: `true`, `false`.
20    Bool,
21    /// An array of elements of the same type: `[Scalar]`.
22    Array(Box<Type>),
23    /// A named tuple of elements: `(x: Scalar, y: String)`.
24    Tuple(Box<TupleType>),
25    /// Matrix type: `Matrix3x3`.
26    Matrix(MatrixType),
27    /// Models.
28    Models,
29    /// A custom type in the syntax tree
30    Custom(QualifiedName),
31}
32
33impl Type {
34    /// Shortcut to create a scalar type.
35    pub fn scalar() -> Self {
36        Self::Quantity(QuantityType::Scalar)
37    }
38
39    /// Shortcut to create a length type.
40    pub fn length() -> Self {
41        Self::Quantity(QuantityType::Length)
42    }
43
44    /// Check if the type is an array of the given type `ty`
45    pub fn is_array_of(&self, ty: &Type) -> bool {
46        match self {
47            Self::Array(array_type) => array_type.as_ref() == ty,
48            _ => false,
49        }
50    }
51
52    /// Check if types are add compatible.
53    pub fn is_add_compatible_to(&self, rhs: &Self) -> bool {
54        rhs == self
55            || (*self == Type::Integer && *rhs == Type::scalar())
56            || (*rhs == Type::Integer && *self == Type::scalar())
57    }
58}
59
60impl std::ops::Mul for Type {
61    type Output = Type;
62
63    fn mul(self, rhs: Self) -> Self::Output {
64        if self == Self::Invalid || rhs == Self::Invalid {
65            return Self::Invalid;
66        }
67
68        match (self, rhs) {
69            (Type::Integer, ty) | (ty, Type::Integer) => ty,
70            (Type::Quantity(lhs), Type::Quantity(rhs)) => Type::Quantity(lhs * rhs),
71            (ty, Type::Array(array_type)) | (Type::Array(array_type), ty) => *array_type * ty,
72            (Type::Tuple(_), _) | (_, Type::Tuple(_)) => todo!(),
73            (Type::Matrix(_), _) | (_, Type::Matrix(_)) => todo!(),
74            (lhs, rhs) => unimplemented!("Multiplication for {lhs} * {rhs}"),
75        }
76    }
77}
78
79impl std::ops::Div for Type {
80    type Output = Type;
81
82    fn div(self, rhs: Self) -> Self::Output {
83        if self == Self::Invalid || rhs == Self::Invalid {
84            return Self::Invalid;
85        }
86
87        match (self, rhs) {
88            (Type::Integer, ty) | (ty, Type::Integer) => ty,
89            (Type::Quantity(lhs), Type::Quantity(rhs)) => Type::Quantity(lhs / rhs),
90            (Type::Array(array_type), ty) => *array_type / ty,
91            (Type::Tuple(_), _) => todo!(),
92            (Type::Matrix(_), _) | (_, Type::Matrix(_)) => todo!(),
93            (lhs, rhs) => unimplemented!("Division for {lhs} * {rhs}"),
94        }
95    }
96}
97
98impl From<QuantityType> for Type {
99    fn from(value: QuantityType) -> Self {
100        Type::Quantity(value)
101    }
102}
103
104impl std::fmt::Display for Type {
105    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
106        match self {
107            Self::Invalid => write!(f, crate::invalid_no_ansi!(TYPE)),
108            Self::Integer => write!(f, "Integer"),
109            Self::Quantity(quantity) => write!(f, "{quantity}"),
110            Self::String => write!(f, "String"),
111            Self::Bool => write!(f, "Bool"),
112            Self::Array(t) => write!(f, "[{t}]"),
113            Self::Tuple(t) => write!(f, "{t}"),
114            Self::Matrix(t) => write!(f, "{t}"),
115            Self::Models => write!(f, "Models"),
116            Self::Custom(n) => write!(f, "Custom({n})"),
117        }
118    }
119}
120
121impl std::fmt::Debug for Type {
122    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
123        match self {
124            Self::Invalid => write!(f, crate::invalid!(TYPE)),
125            Self::Integer => write!(f, "Integer"),
126            Self::Quantity(quantity) => write!(f, "{quantity}"),
127            Self::String => write!(f, "String"),
128            Self::Bool => write!(f, "Bool"),
129            Self::Array(t) => write!(f, "[{t}]"),
130            Self::Tuple(t) => write!(f, "{t}"),
131            Self::Matrix(t) => write!(f, "{t}"),
132            Self::Models => write!(f, "Models"),
133            Self::Custom(n) => write!(f, "Custom({n})"),
134        }
135    }
136}
137
138#[test]
139fn builtin_type() {
140    use crate::parser::*;
141
142    let ty = Parser::parse_rule::<TypeAnnotation>(Rule::r#type, "Integer", 0).expect("test error");
143    assert_eq!(ty.0.to_string(), "Integer");
144    assert_eq!(ty.0.value, Type::Integer);
145}