yog_script_analyze/typeck/
model.rs1use std::fmt;
5
6use super::builtin;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum Type {
11 I8,
12 I16,
13 I32,
14 I64,
15 U8,
16 U16,
17 U32,
18 U64,
19 F32,
20 F64,
21 Decimal,
22 Char,
23 Bool,
24 String,
25 Object,
27 Dynamic,
29 Void,
30 Null,
32 Unit(String),
34 Content(String),
36 Tag(String),
39 Own(String, Vec<Type>),
42 Param(String),
45 Static(String, Vec<Type>),
50 Game(String),
52 Yog(String),
55 Namespace(String),
57 Array(Box<Type>, usize),
60 Tuple(Vec<Type>, Vec<Option<String>>),
63 Fn(Vec<Type>, Box<Type>),
65 Nullable(Box<Type>),
66 Unknown,
69}
70
71pub use yog_script_ids::units::{BLOCKS, SECONDS, TICKS};
73
74impl Type {
75 pub fn own(name: impl Into<String>) -> Type {
77 Type::Own(name.into(), Vec::new())
78 }
79
80 pub fn nullable(self) -> Type {
81 match self {
82 Type::Nullable(_) | Type::Null | Type::Unknown => self,
83 other => Type::Nullable(Box::new(other)),
84 }
85 }
86
87 pub fn non_null(&self) -> &Type {
89 match self {
90 Type::Nullable(t) => t,
91 t => t,
92 }
93 }
94
95 pub fn is_nullable(&self) -> bool {
96 matches!(self, Type::Nullable(_) | Type::Null)
97 }
98
99 pub fn known(&self) -> bool {
100 !matches!(self, Type::Unknown)
101 }
102
103 pub fn is_numeric(&self) -> bool {
104 builtin::is_number(self) || matches!(self, Type::Unit(_))
105 }
106
107 pub fn is_integer(&self) -> bool {
108 builtin::is_integer(self)
109 }
110
111 pub fn converts_to(&self, to: &Type) -> bool {
113 if self == to || !self.known() || !to.known() {
114 return true;
115 }
116 if matches!(self, Type::Dynamic) || matches!(to, Type::Dynamic | Type::Object) {
117 return true;
118 }
119 match (self, to) {
120 (Type::Null, Type::Nullable(_)) => true,
121 (Type::Nullable(a), Type::Nullable(b)) => a.converts_to(b),
122 (a, Type::Nullable(b)) => a.converts_to(b),
123 (Type::Unit(_), Type::Unit(_)) => false,
127 (n, Type::Unit(_)) => builtin::is_number(n),
130 (Type::Unit(_), n) => builtin::widens(&Type::I32, n),
131 (Type::Array(a, ra), Type::Array(b, rb)) => ra == rb && a == b,
132 (a, b) => builtin::widens(a, b),
133 }
134 }
135
136 pub fn integer_width(&self) -> Option<(u32, bool)> {
139 builtin::integer_width(self)
140 }
141
142 pub fn wider(a: &Type, b: &Type) -> Type {
143 match (a, b) {
144 (Type::Unit(_), _) => a.clone(),
145 (_, Type::Unit(_)) => b.clone(),
146 _ if builtin::widens(a, b) => b.clone(),
147 _ => a.clone(),
148 }
149 }
150}
151
152impl fmt::Display for Type {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 match self {
155 Type::I8
156 | Type::I16
157 | Type::I32
158 | Type::I64
159 | Type::U8
160 | Type::U16
161 | Type::U32
162 | Type::U64
163 | Type::F32
164 | Type::F64
165 | Type::Decimal
166 | Type::Char
167 | Type::Bool
168 | Type::String
169 | Type::Object
170 | Type::Dynamic
171 | Type::Void => write!(f, "{}", builtin::word(self).unwrap_or_default()),
172 Type::Null => write!(f, "{}", crate::words::null::WORD.text),
173 Type::Own(name, args) if !args.is_empty() => {
174 let args: Vec<_> = args.iter().map(ToString::to_string).collect();
175 write!(f, "{name}<{}>", args.join(", "))
176 }
177 Type::Unit(u)
178 | Type::Own(u, _)
179 | Type::Static(u, _)
180 | Type::Param(u)
181 | Type::Yog(u)
182 | Type::Namespace(u) => {
183 write!(f, "{u}")
184 }
185 Type::Content(kind) => write!(f, "{}", super::content_ids::content_id_name(kind)),
186 Type::Tag(kind) => write!(f, "{}", super::content_ids::tag_id_name(kind)),
187 Type::Game(name) => write!(f, "{}", crate::index::simple_name(name)),
188 Type::Array(..) => {
191 let mut item = self;
192 let mut ranks = String::new();
193 while let Type::Array(inner, rank) = item {
194 ranks.push_str(&format!("[{}]", ",".repeat(rank - 1)));
195 item = inner;
196 }
197 write!(f, "{item}{ranks}")
198 }
199 Type::Tuple(items, names) => {
200 let items: Vec<String> = items
201 .iter()
202 .zip(names.iter().chain(std::iter::repeat(&None)))
203 .map(|(t, n)| match n {
204 Some(n) => format!("{t} {n}"),
205 None => t.to_string(),
206 })
207 .collect();
208 write!(f, "({})", items.join(", "))
209 }
210 Type::Fn(params, ret) => {
211 let mut all: Vec<String> = params.iter().map(ToString::to_string).collect();
212 let word = if **ret == Type::Void {
213 yog_script_ids::system::ACTION
214 } else {
215 all.push(ret.to_string());
216 yog_script_ids::system::FUNC
217 };
218 write!(f, "{word}<{}>", all.join(", "))
219 }
220 Type::Nullable(t) => write!(f, "{t}?"),
221 Type::Unknown => write!(f, "?"),
222 }
223 }
224}
225
226#[cfg(test)]
227mod tests;