y_lang/typechecker/
variabletype.rs

1use std::{fmt::Display, str::FromStr};
2
3use crate::loader::Module;
4
5#[derive(Default, Debug, Clone, PartialEq, Eq)]
6pub enum VariableType {
7    #[default]
8    Void,
9    Bool,
10    Str,
11    Int,
12    Char,
13    // TODO: Maybe just dont use
14    Any,
15    Unknown,
16    Func {
17        params: Vec<VariableType>,
18        return_type: Box<VariableType>,
19        source: Option<Module<()>>,
20    },
21    ArraySlice(Box<VariableType>),
22    TupleArray {
23        item_type: Box<VariableType>,
24        size: usize,
25    },
26}
27
28pub struct VariableParseError(String);
29
30impl FromStr for VariableType {
31    type Err = VariableParseError;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        match s {
35            "void" => Ok(Self::Void),
36            "bool" => Ok(Self::Bool),
37            "str" => Ok(Self::Str),
38            "int" => Ok(Self::Int),
39            "any" => Ok(Self::Any),
40            "char" => Ok(Self::Char),
41            "unknown" => Ok(Self::Unknown),
42            _ => Err(VariableParseError(format!("Invalid type '{s}'"))),
43        }
44    }
45}
46
47impl Display for VariableType {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        use VariableType::*;
50
51        let value = &match self {
52            Void => "void".to_owned(),
53            Bool => "bool".to_owned(),
54            Int => "int".to_owned(),
55            Str => "str".to_owned(),
56            Any => "any".to_owned(),
57            Char => "char".to_owned(),
58            Unknown => "unknown".to_owned(),
59            Func {
60                params,
61                return_type: return_value,
62                ..
63            } => format!("{params:?} -> {return_value:?}"),
64            ArraySlice(item_type) => format!("&[{item_type}]"),
65            TupleArray { item_type, size } => format!("[{item_type}; {size}]"),
66        };
67
68        f.write_str(value)
69    }
70}
71
72#[derive(Debug, PartialEq)]
73pub struct VariableConversionError;
74
75impl VariableType {
76    pub fn size(&self) -> usize {
77        match self {
78            VariableType::Void => 0,
79            VariableType::Bool => 1,
80            VariableType::Str => 8,
81            VariableType::Int => 8,
82            VariableType::Char => 1,
83            VariableType::Any => 8,
84            VariableType::Unknown => 8,
85            VariableType::Func { .. } => 8,
86            VariableType::ArraySlice(_) => 8,
87            VariableType::TupleArray { .. } => 8,
88        }
89    }
90
91    pub fn set_source(self, source: Module<()>) -> Self {
92        match self {
93            VariableType::Func {
94                params,
95                return_type: return_value,
96                ..
97            } => VariableType::Func {
98                params,
99                return_type: return_value,
100                source: Some(source),
101            },
102            _ => self,
103        }
104    }
105
106    pub fn get_source(&self) -> Option<Module<()>> {
107        match self {
108            VariableType::Func { source, .. } => source.clone(),
109            _ => None,
110        }
111    }
112
113    /// Try to convert this variable type to another. If the conversion is successful, it returns
114    /// the new variable type. If it is not successful, it returns Err(VariableConversionError).
115    ///
116    /// Note the rules:
117    ///     - `unknown` can be converted to anything
118    ///     - nothing can be converted to `unknown` (except `unknown` itself)
119    ///     - everything can be converted to `any`
120    ///     - `any` can not be converted to anything else
121    ///     - every basic type can be converted to itself
122    pub fn convert_to(&self, to_convert_to: &Self) -> Result<Self, VariableConversionError> {
123        use VariableType::*;
124        match (self, to_convert_to) {
125            (Unknown, other) => Ok(other.clone()),
126            (_, Any) => Ok(Any),
127            (TupleArray { item_type, .. }, ArraySlice(other_item_type)) => {
128                Ok(ArraySlice(Box::new(item_type.convert_to(other_item_type)?)))
129            }
130            (Str, ArraySlice(other_item_type)) => {
131                if *other_item_type == Box::new(Char) {
132                    Ok(ArraySlice(Box::new(Char)))
133                } else {
134                    Err(VariableConversionError)
135                }
136            }
137            (Char, Int) => Ok(Int),
138            (Int, Char) => Ok(Char),
139            (TupleArray { item_type, .. }, Str) => {
140                if *item_type == Box::new(Char) {
141                    Ok(Str)
142                } else {
143                    Err(VariableConversionError)
144                }
145            }
146            // TODO: Allow conversion of same-sized strings to tuple arrays
147            // (Str, TupleArray { size, .. }) => todo!(),
148            (left, right) => {
149                if left == right {
150                    Ok(right.clone())
151                } else {
152                    Err(VariableConversionError)
153                }
154            }
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::{VariableConversionError, VariableType::*};
162
163    #[test]
164    fn test_convert_to_any() {
165        assert_eq!(Void.convert_to(&Any), Ok(Any));
166        assert_eq!(Int.convert_to(&Any), Ok(Any));
167        assert_eq!(Any.convert_to(&Any), Ok(Any));
168    }
169
170    #[test]
171    fn test_convert_from_any() {
172        assert_eq!(Any.convert_to(&Void), Err(VariableConversionError));
173        assert_eq!(Any.convert_to(&Int), Err(VariableConversionError));
174        assert_eq!(Any.convert_to(&Str), Err(VariableConversionError));
175    }
176
177    #[test]
178    fn test_convert_from_unknown() {
179        assert_eq!(Unknown.convert_to(&Int), Ok(Int));
180        assert_eq!(Unknown.convert_to(&Any), Ok(Any));
181        assert_eq!(Unknown.convert_to(&Unknown), Ok(Unknown));
182    }
183
184    #[test]
185    fn test_conver_to_unknown() {
186        assert_eq!(Int.convert_to(&Unknown), Err(VariableConversionError));
187        assert_eq!(Any.convert_to(&Unknown), Err(VariableConversionError));
188        assert_eq!(Void.convert_to(&Unknown), Err(VariableConversionError));
189    }
190}