Skip to main content

microcad_lang/ty/
type.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad Type
5
6use crate::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: `Integer: 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    /// Model.
28    Model,
29}
30
31impl Type {
32    /// Shortcut to create a scalar type.
33    pub fn scalar() -> Self {
34        Self::Quantity(QuantityType::Scalar)
35    }
36
37    /// Shortcut to create a length type.
38    pub fn length() -> Self {
39        Self::Quantity(QuantityType::Length)
40    }
41
42    /// Check if the type is an array of the given type `ty`
43    pub fn is_array_of(&self, ty: &Type) -> bool {
44        match self {
45            Self::Array(array_type) => array_type.as_ref() == ty,
46            _ => false,
47        }
48    }
49
50    /// Check if types are compatible.
51    pub fn is_compatible_to(&self, rhs: &Self) -> bool {
52        rhs == self
53            || (*self == Type::Integer && *rhs == Type::scalar())
54            || (*rhs == Type::Integer && *self == Type::scalar())
55    }
56
57    /// Returns if the given type or it's inner type matches the given parameter type.
58    pub fn is_matching(&self, param_type: &Type) -> bool {
59        match (self, param_type) {
60            (_, Type::Quantity(QuantityType::Scalar)) => {
61                self == &Type::scalar()
62                    || self == &Type::Integer
63                    || self.is_array_of(&Type::scalar())
64                    || self.is_array_of(&Type::Integer)
65            }
66            (Type::Tuple(ty_s), Type::Tuple(ty_p)) => ty_s.is_matching(ty_p),
67            _ => self == param_type || self.is_array_of(param_type),
68        }
69    }
70}
71
72impl std::ops::Mul for Type {
73    type Output = Type;
74
75    fn mul(self, rhs: Self) -> Self::Output {
76        if self == Self::Invalid || rhs == Self::Invalid {
77            return Self::Invalid;
78        }
79
80        match (self, rhs) {
81            (Type::Integer, ty) | (ty, Type::Integer) => ty,
82            (Type::Quantity(lhs), Type::Quantity(rhs)) => Type::Quantity(lhs * rhs),
83            (ty, Type::Array(array_type)) | (Type::Array(array_type), ty) => *array_type * ty,
84            (Type::Tuple(_), _) | (_, Type::Tuple(_)) => todo!(),
85            (Type::Matrix(_), _) | (_, Type::Matrix(_)) => todo!(),
86            (lhs, rhs) => unimplemented!("Multiplication for {lhs} * {rhs}"),
87        }
88    }
89}
90
91impl std::ops::Div for Type {
92    type Output = Type;
93
94    fn div(self, rhs: Self) -> Self::Output {
95        if self == Self::Invalid || rhs == Self::Invalid {
96            return Self::Invalid;
97        }
98
99        match (self, rhs) {
100            (Type::Integer, ty) | (ty, Type::Integer) => ty,
101            (Type::Quantity(lhs), Type::Quantity(rhs)) => Type::Quantity(lhs / rhs),
102            (Type::Array(array_type), ty) => *array_type / ty,
103            (Type::Tuple(_), _) => todo!(),
104            (Type::Matrix(_), _) | (_, Type::Matrix(_)) => todo!(),
105            (lhs, rhs) => unimplemented!("Division for {lhs} * {rhs}"),
106        }
107    }
108}
109
110impl From<QuantityType> for Type {
111    fn from(value: QuantityType) -> Self {
112        Type::Quantity(value)
113    }
114}
115
116impl std::fmt::Display for Type {
117    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
118        match self {
119            Self::Invalid => write!(f, microcad_lang_base::invalid_no_ansi!(TYPE)),
120            Self::Integer => write!(f, "Integer"),
121            Self::Quantity(quantity) => write!(f, "{quantity}"),
122            Self::String => write!(f, "String"),
123            Self::Bool => write!(f, "Bool"),
124            Self::Array(t) => write!(f, "[{t}]"),
125            Self::Tuple(t) => write!(f, "{t}"),
126            Self::Matrix(t) => write!(f, "{t}"),
127            Self::Model => write!(f, "Model"),
128        }
129    }
130}
131
132impl std::fmt::Debug for Type {
133    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
134        match self {
135            Self::Invalid => write!(f, microcad_lang_base::invalid!(TYPE)),
136            Self::Integer => write!(f, "Integer"),
137            Self::Quantity(quantity) => write!(f, "{quantity}"),
138            Self::String => write!(f, "String"),
139            Self::Bool => write!(f, "Bool"),
140            Self::Array(t) => write!(f, "[{t}]"),
141            Self::Tuple(t) => write!(f, "{t}"),
142            Self::Matrix(t) => write!(f, "{t}"),
143            Self::Model => write!(f, "Models"),
144        }
145    }
146}
147
148#[test]
149fn type_matching() {
150    assert!(Type::scalar().is_matching(&Type::scalar()));
151    assert!(!Type::scalar().is_matching(&Type::Integer));
152    assert!(Type::Integer.is_matching(&Type::scalar()));
153    assert!(!Type::scalar().is_matching(&Type::String));
154    assert!(!Type::String.is_matching(&Type::scalar()));
155}