diff --git a/src/check/context/clss/generic.rs b/src/check/context/clss/generic.rs index 3068533a..96f8a234 100644 --- a/src/check/context/clss/generic.rs +++ b/src/check/context/clss/generic.rs @@ -41,6 +41,24 @@ impl Hash for GenericClass { } impl GenericClass { + pub fn try_from_id(id: &AST) -> TypeResult { + match &id.node { + Node::Id { lit } => { + Ok(GenericClass { + is_py_type: false, + name: StringName::from(lit.as_str()), + pos: id.pos, + concrete: true, // assume concrete for now + args: vec![], + fields: Default::default(), + functions: Default::default(), + parents: Default::default(), + }) + } + _ => Err(vec![TypeErr::new(id.pos, "Expected class name")]), + } + } + pub fn all_pure(self, pure: bool) -> TypeResult { let functions = self.functions.iter().map(|f| f.clone().pure(pure)).collect(); Ok(GenericClass { functions, ..self }) diff --git a/src/check/context/generic.rs b/src/check/context/generic.rs index 95e420a5..1102a1fc 100644 --- a/src/check/context/generic.rs +++ b/src/check/context/generic.rs @@ -1,11 +1,14 @@ use std::collections::HashSet; use std::convert::TryFrom; +use itertools::EitherOrBoth::{Both, Left, Right}; +use itertools::Itertools; + use crate::check::context::clss::generic::GenericClass; use crate::check::context::field::generic::{GenericField, GenericFields}; use crate::check::context::function::generic::GenericFunction; use crate::check::result::{TypeErr, TypeResult}; -use crate::parse::ast::{AST, Node}; +use crate::parse::ast::{AST, Node, OptAST}; pub fn generics( files: &[AST], @@ -30,6 +33,10 @@ pub fn generics( fields.insert(ty.clone()); }); } + Node::Import { from, import, alias } => + from_import(from, import, alias)?.into_iter().for_each(|t| { + types.insert(t); + }), _ => {} } } @@ -40,3 +47,23 @@ pub fn generics( Ok((types, fields, functions)) } + +/// From import. +/// +/// A more elaborate import system will extract the signature of the class. +fn from_import(_from: &OptAST, import: &[AST], alias: &[AST]) -> TypeResult> { + let (mut classes, mut errs) = (vec![], vec![]); + for pair in import.iter().zip_longest(alias) { + match pair { + Left(import) => classes.push(GenericClass::try_from_id(import)?), + Both(_, alias) => classes.push(GenericClass::try_from_id(alias)?), + Right(alias) => { + let msg = format!("alias with no matching import: {}", alias.node); + errs.push(TypeErr::new(alias.pos, &msg)); + } + } + } + + if !errs.is_empty() { return Err(errs); } + Ok(classes) +} diff --git a/src/check/context/mod.rs b/src/check/context/mod.rs index 5b8a7844..a7c521b8 100644 --- a/src/check/context/mod.rs +++ b/src/check/context/mod.rs @@ -84,6 +84,7 @@ mod tests { use crate::check::name::string_name::StringName; use crate::check::result::TypeResult; use crate::common::position::Position; + use crate::parse::parse; #[test] pub fn lookup_custom_list_type() -> TypeResult<()> { @@ -156,4 +157,42 @@ mod tests { context.class(&StringName::from("None"), Position::default()).unwrap(); context.class(&StringName::from("Exception"), Position::default()).unwrap(); } + + #[test] + pub fn test_import_parse() { + let file = parse("import IPv4Address").unwrap(); + let context = Context::try_from(vec![*file.clone()].as_slice()).unwrap(); + + context.class(&StringName::from("IPv4Address"), Position::default()).unwrap(); + } + + #[test] + pub fn test_import_as_parse() { + let file = parse("import IPv4Address as Other").unwrap(); + let context = Context::try_from(vec![*file.clone()].as_slice()).unwrap(); + + context.class(&StringName::from("Other"), Position::default()).unwrap(); + } + + #[test] + pub fn test_import_as_too_many_parse() { + let file = parse("import IPv4Address as Other, Other2").unwrap(); + Context::try_from(vec![*file.clone()].as_slice()).unwrap_err(); + } + + #[test] + pub fn test_from_import_parse() { + let file = parse("from ipaddress import IPv4Address").unwrap(); + let context = Context::try_from(vec![*file.clone()].as_slice()).unwrap(); + + context.class(&StringName::from("IPv4Address"), Position::default()).unwrap(); + } + + #[test] + pub fn test_from_import_as_parse() { + let file = parse("from ipaddress import IPv4Address as Other").unwrap(); + let context = Context::try_from(vec![*file.clone()].as_slice()).unwrap(); + + context.class(&StringName::from("Other"), Position::default()).unwrap(); + } }