1use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq)]
6pub enum Ty {
7 Unknown,
9 Never,
11 Void,
13 Null,
15 Bool,
17 Int,
19 Float,
21 Str,
23 Array,
25 Callable,
27 Object(String),
29 Union(Vec<Ty>),
31 Intersection(Vec<Ty>),
33}
34
35impl Ty {
36 pub fn from_str(s: &str) -> Self {
38 let s = s.trim();
39 if s.contains('|') {
40 let parts: Vec<Ty> = s.split('|').map(Ty::from_str).collect();
41 return Ty::Union(parts);
42 }
43 if s.contains('&') {
44 let parts: Vec<Ty> = s.split('&').map(Ty::from_str).collect();
45 return Ty::Intersection(parts);
46 }
47 if let Some(inner) = s.strip_prefix('?') {
48 return Ty::Union(vec![Ty::from_str(inner), Ty::Null]);
49 }
50 match s {
51 "null" | "NULL" => Ty::Null,
52 "bool" | "boolean" => Ty::Bool,
53 "int" | "integer" => Ty::Int,
54 "float" | "double" => Ty::Float,
55 "string" => Ty::Str,
56 "array" => Ty::Array,
57 "callable" => Ty::Callable,
58 "void" => Ty::Void,
59 "never" => Ty::Never,
60 "mixed" | "" => Ty::Unknown,
61 name => Ty::Object(name.to_string()),
62 }
63 }
64
65 pub fn class_name(&self) -> Option<&str> {
67 match self {
68 Ty::Object(n) => Some(n),
69 _ => None,
70 }
71 }
72
73 pub fn is_nullable(&self) -> bool {
75 match self {
76 Ty::Null | Ty::Unknown => true,
77 Ty::Union(ts) => ts.iter().any(|t| matches!(t, Ty::Null)),
78 _ => false,
79 }
80 }
81}
82
83#[derive(Debug, Default, Clone)]
85pub struct TypeEnv(pub HashMap<String, Ty>);
86
87impl TypeEnv {
88 pub fn get(&self, var: &str) -> &Ty {
89 self.0.get(var).unwrap_or(&Ty::Unknown)
90 }
91
92 pub fn class_name(&self, var: &str) -> Option<&str> {
94 self.0.get(var)?.class_name()
95 }
96
97 pub fn insert(&mut self, var: String, ty: Ty) {
98 self.0.insert(var, ty);
99 }
100}