mamba/check/context/clss/
mod.rs1use std::collections::{HashMap, HashSet};
2use std::convert::TryFrom;
3use std::fmt;
4use std::fmt::{Display, Formatter};
5use std::hash::{Hash, Hasher};
6
7use itertools::{EitherOrBoth, enumerate, Itertools};
8
9use crate::check::context::{Context, LookupClass};
10use crate::check::context::arg::FunctionArg;
11use crate::check::context::arg::generic::GenericFunctionArg;
12use crate::check::context::clss::generic::GenericClass;
13use crate::check::context::field::Field;
14use crate::check::context::field::generic::GenericField;
15use crate::check::context::function::Function;
16use crate::check::context::function::generic::GenericFunction;
17use crate::check::context::parent::generic::GenericParent;
18use crate::check::name::{Any, Empty, Name, Substitute};
19use crate::check::name::string_name::StringName;
20use crate::check::name::true_name::TrueName;
21use crate::check::result::{TypeErr, TypeResult};
22use crate::common::position::Position;
23
24pub const INT: &str = "Int";
25pub const FLOAT: &str = "Float";
26pub const STRING: &str = "Str";
27pub const BOOL: &str = "Bool";
28pub const ENUM: &str = "Enum";
29pub const COMPLEX: &str = "Complex";
30
31pub const COLLECTION: &str = "Collection";
32pub const RANGE: &str = "Range";
33pub const SLICE: &str = "Slice";
34pub const SET: &str = "Set";
35pub const LIST: &str = "List";
36pub const DICT: &str = "Dict";
37
38pub const TUPLE: &str = "Tuple";
39pub const CALLABLE: &str = "Callable";
40pub const UNION: &str = "Union";
41
42pub const ANY: &str = "Any";
43pub const NONE: &str = "None";
44pub const EXCEPTION: &str = "Exception";
45
46pub mod generic;
47pub mod python;
48
49#[derive(Debug, Clone, Eq)]
58pub struct Class {
59 pub is_py_type: bool,
60 pub name: StringName,
61 pub concrete: bool,
62 pub args: Vec<FunctionArg>,
63 pub fields: HashSet<Field>,
64 pub parents: HashSet<TrueName>,
65 pub functions: HashSet<Function>,
66}
67
68pub trait HasParent<T> {
69 fn has_parent(&self, name: T, ctx: &Context, pos: Position) -> TypeResult<bool>;
73}
74
75pub trait GetField<T> {
76 fn field(&self, name: &str, ctx: &Context, pos: Position) -> TypeResult<T>;
77}
78
79pub trait GetFun<T> {
80 fn fun(&self, name: &StringName, ctx: &Context, pos: Position) -> TypeResult<T>;
81}
82
83impl HasParent<&StringName> for Class {
84 fn has_parent(&self, other: &StringName, ctx: &Context, pos: Position) -> TypeResult<bool> {
85 if self.name == *other || other.name.as_str() == ANY {
86 return Ok(true);
87 } else if (self.name.name == TUPLE && (other.name == TUPLE || other.name == COLLECTION)) ||
88 (self.name.name == *other.name && self.name.generics.len() == other.generics.len()) {
89
90 let mut all_generic_super = true;
93 for (s_name, o_name) in self.name.generics.iter().zip(&other.generics) {
94 for s_name in &s_name.names {
95 all_generic_super &= ctx.class(s_name, pos)?.has_parent(o_name, ctx, pos)?;
96 }
97 }
98 if all_generic_super { return Ok(all_generic_super); }
99 }
100
101 Ok(self
102 .parents
103 .iter()
104 .map(|p| ctx.class(p, pos)?.has_parent(other, ctx, pos))
105 .collect::<Result<Vec<bool>, _>>()?
106 .iter()
107 .any(|b| *b))
108 }
109}
110
111impl HasParent<&TrueName> for Class {
112 fn has_parent(&self, name: &TrueName, ctx: &Context, pos: Position) -> TypeResult<bool> {
113 self.has_parent(&name.variant, ctx, pos)
114 }
115}
116
117impl HasParent<&Name> for Class {
118 fn has_parent(&self, name: &Name, ctx: &Context, pos: Position) -> TypeResult<bool> {
119 if name.contains(&TrueName::from(&self.name)) || name == &Name::any() {
120 return Ok(true);
121 }
122
123 let names = name.as_direct();
124 let parent_names: Vec<StringName> = self.parents.iter().map(StringName::from).collect();
125 let parent_classes: Vec<Class> =
126 parent_names.iter().map(|name| ctx.class(name, pos)).collect::<Result<_, _>>()?;
127
128 for name in names {
129 let res = parent_classes
130 .iter()
131 .map(|pc| pc.has_parent(&name, ctx, pos))
132 .collect::<Result<Vec<bool>, _>>()?;
133 if res.iter().any(|a| *a) {
134 return Ok(true);
135 }
136 }
137
138 Ok(false)
139 }
140}
141
142impl LookupClass<&Name, HashSet<Class>> for Context {
143 fn class(&self, class_name: &Name, pos: Position) -> TypeResult<HashSet<Class>> {
144 if class_name.is_empty() {
145 let msg = format!("Tried to get class for {class_name}");
146 return Err(vec![TypeErr::new(pos, &msg)]);
147 }
148 class_name.names.iter().map(|name| self.class(name, pos)).collect::<TypeResult<_>>()
149 }
150}
151
152impl LookupClass<&TrueName, Class> for Context {
153 fn class(&self, class: &TrueName, pos: Position) -> TypeResult<Class> {
154 self.class(&StringName::from(class), pos)
155 }
156}
157
158impl LookupClass<&StringName, Class> for Context {
159 fn class(&self, class: &StringName, pos: Position) -> TypeResult<Class> {
163 if let Some(generic_class) = self.classes.iter().find(|c| c.name.name == class.name) {
164 let mut generics = HashMap::new();
165 if class.name == TUPLE || class.name == COLLECTION {
166 let mut generic_keys: Vec<Name> = vec![];
168 for (i, gen) in enumerate(&class.generics) {
169 generic_keys.push(Name::from(format!("G{i}").as_str()));
170 generics.insert(Name::from(format!("G{i}").as_str()), gen.clone());
171 }
172
173 let name = StringName::new(class.name.as_str(), &generic_keys);
174 let generic_class = GenericClass { name, ..generic_class.clone() };
175 let class = Class::try_from((&generic_class, &generics, pos));
176 return class;
177 }
178
179 let placeholders = generic_class.name.clone();
180
181 for name in placeholders.generics.iter().zip_longest(class.generics.iter()) {
182 match name {
183 EitherOrBoth::Both(placeholder, name) => {
184 generics.insert(placeholder.clone(), name.clone());
185 }
186 EitherOrBoth::Left(placeholder) => {
187 let msg = format!("No argument for generic {placeholder} in {class}");
188 return Err(vec![TypeErr::new(pos, &msg)]);
189 }
190 EitherOrBoth::Right(placeholder) => {
191 let msg = format!("Gave unexpected generic {placeholder} to {class}");
192 return Err(vec![TypeErr::new(pos, &msg)]);
193 }
194 }
195 }
196
197 Class::try_from((generic_class, &generics, pos))
198 } else {
199 let msg = format!("Type '{class}' is undefined.");
200 Err(vec![TypeErr::new(pos, &msg)])
201 }
202 }
203}
204
205impl Hash for Class {
206 fn hash<H: Hasher>(&self, state: &mut H) {
207 self.name.hash(state)
208 }
209}
210
211impl PartialEq for Class {
212 fn eq(&self, other: &Self) -> bool {
213 self.name == other.name
214 }
215}
216
217impl Display for Class {
218 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
219 write!(f, "{}", self.name)
220 }
221}
222
223impl TryFrom<(&GenericClass, &HashMap<Name, Name>, Position)> for Class {
224 type Error = Vec<TypeErr>;
225
226 fn try_from(
227 (generic, generics, pos): (&GenericClass, &HashMap<Name, Name>, Position),
228 ) -> Result<Self, Self::Error> {
229 let try_arg = |a: &GenericFunctionArg| FunctionArg::try_from((a, generics, pos));
230 let try_field = |field: &GenericField| Field::try_from((field, generics, pos));
231 let try_function = |fun: &GenericFunction| Function::try_from((fun, generics, pos));
232 let try_parent = |parent: &GenericParent| TrueName::try_from((parent, generics, pos));
233
234 Ok(Class {
235 is_py_type: generic.is_py_type,
236 name: generic.name.substitute(generics, pos)?,
237 concrete: generic.concrete,
238 args: generic.args.iter().map(try_arg).collect::<Result<_, _>>()?,
239 parents: generic.parents.iter().map(try_parent).collect::<Result<_, _>>()?,
240 fields: generic.fields.iter().map(try_field).collect::<Result<_, _>>()?,
241 functions: generic.functions.iter().map(try_function).collect::<Result<_, _>>()?,
242 })
243 }
244}
245
246impl Class {
247 pub fn constructor(&self, without_self: bool) -> Function {
248 Function {
249 is_py_type: false,
250 name: self.name.clone(),
251 self_mutable: None,
252 pure: false,
253 arguments: if without_self && !self.args.is_empty() {
254 self.args.iter().skip(1).cloned().collect()
255 } else {
256 self.args.clone()
257 },
258 raises: Name::empty(),
259 in_class: None,
260 ret_ty: Name::from(&self.name),
261 }
262 }
263}
264
265impl GetField<Field> for Class {
266 fn field(&self, name: &str, ctx: &Context, pos: Position) -> TypeResult<Field> {
267 if let Some(field) = self.fields.iter().find(|f| f.name == name) {
268 return Ok(field.clone());
269 }
270
271 for parent in &self.parents {
272 if let Ok(ok) = ctx.class(parent, pos)?.field(name, ctx, pos) {
273 return Ok(ok);
274 }
275 }
276 Err(vec![TypeErr::new(pos, &format!("'{self}' does not define '{name}'"))])
277 }
278}
279
280impl GetFun<Function> for Class {
281 fn fun(&self, name: &StringName, ctx: &Context, pos: Position) -> TypeResult<Function> {
286 if let Some(function) = self.functions.iter().find(|f| &f.name == name) {
287 return Ok(function.clone());
288 }
289
290 for parent in &self.parents {
291 if let Ok(function) = ctx.class(parent, pos)?.fun(name, ctx, pos) {
292 return Ok(function);
293 }
294 }
295 Err(vec![TypeErr::new(pos, &format!("'{self}' does not define '{name}'"))])
296 }
297}
298
299pub fn concrete_to_python(name: &str) -> String {
300 match name {
301 INT => String::from(python::INT_PRIMITIVE),
302 FLOAT => String::from(python::FLOAT_PRIMITIVE),
303 STRING => String::from(python::STRING_PRIMITIVE),
304 BOOL => String::from(python::BOOL_PRIMITIVE),
305 ENUM => String::from(python::ENUM_PRIMITIVE),
306 COMPLEX => String::from(python::COMPLEX_PRIMITIVE),
307
308 COLLECTION => String::from(python::COLLECTION),
309 RANGE => String::from(python::RANGE),
310 SLICE => String::from(python::SLICE),
311 SET => String::from(python::SET),
312 LIST => String::from(python::LIST),
313 TUPLE => String::from(python::TUPLE),
314 DICT => String::from(python::DICT),
315
316 CALLABLE => String::from(python::CALLABLE),
317 NONE => String::from(python::NONE),
318 EXCEPTION => String::from(python::EXCEPTION),
319 UNION => String::from(python::UNION),
320 ANY => String::from(python::ANY),
321
322 other => String::from(other),
323 }
324}