From 12cdb095598e667cd51db797ff56e4b0357d52f5 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Wed, 3 May 2023 15:14:26 +0900 Subject: [PATCH] feat: support `Callable/Iterable` --- Cargo.lock | 16 ++++++------ Cargo.toml | 6 ++--- README.md | 23 ++++++++++------- crates/py2erg/convert.rs | 56 +++++++++++++++++++++++++++++++++++++--- tests/test.rs | 2 +- tests/typespec.py | 12 ++++++++- 6 files changed, 90 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0353802..0ae2f09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,9 +252,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "els" -version = "0.1.24-nightly.4" +version = "0.1.24-nightly.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74cdce8d05df8b4a6090cc892e34a30ab6880f5ed8786e9346fe4d4fb4c47d5" +checksum = "3db3f0318812d7401d51a3232f256faaa15963f30f4bf7bc07dc8bc73b849353" dependencies = [ "erg_common", "erg_compiler", @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.6.12-nightly.4" +version = "0.6.12-nightly.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cc57d02fe5520b0f0a42d30be2604edafa71a35074a35f579f2ca91533d965" +checksum = "8c34723b8e8f53e11205330924f36425d52b2ebae66a5d45e889fb83a01be4fb" dependencies = [ "backtrace-on-stack-overflow", "hermit-abi", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.6.12-nightly.4" +version = "0.6.12-nightly.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "101a854704b5bda829d4fa6005d590b2f426ce6624b8361118b4949c7d90c859" +checksum = "beef1255c0fe34d84892d6b3eecd4506a77ba71d24a8b143795f1c5b2e4f6877" dependencies = [ "erg_common", "erg_parser", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.6.12-nightly.4" +version = "0.6.12-nightly.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fe0229517462ba79374a8eb1b16a183f0854744af6d4ebfcd4b2ff05539121" +checksum = "a16c93aca8cc69dd920dd3a6fb0c6ab9054bdfe3e46ada0ea89dce8896617296" dependencies = [ "erg_common", "unicode-xid 0.2.4", diff --git a/Cargo.toml b/Cargo.toml index 3fd4c8b..1b3b5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,9 @@ edition = "2021" repository = "https://github.com/mtshiba/pylyzer" [workspace.dependencies] -erg_common = { version = "0.6.12-nightly.4", features = ["py_compat", "els"] } -erg_compiler = { version = "0.6.12-nightly.4", features = ["py_compat", "els"] } -els = { version = "0.1.24-nightly.4", features = ["py_compat"] } +erg_common = { version = "0.6.12-nightly.6", features = ["py_compat", "els"] } +erg_compiler = { version = "0.6.12-nightly.6", features = ["py_compat", "els"] } +els = { version = "0.1.24-nightly.6", features = ["py_compat"] } rustpython-parser = "0.1.2" # erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compat", "els"] } # erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compat", "els"] } diff --git a/README.md b/README.md index 2716807..d277dbf 100644 --- a/README.md +++ b/README.md @@ -99,23 +99,28 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker. * [x] builtin modules resolving (partially) * [x] local scripts resolving * [ ] local packages resolving -* [ ] compound type checking - * [x] `Union` - * [x] `Optional` +* [x] collection types * [x] `list` * [x] `dict` * [x] `tuple` +* [ ] `typing` + * [x] `Union` + * [x] `Optional` * [x] `Literal` + * [x] `Callable` * [ ] `TypedDict` + * [ ] type variable (`TypeVar`, `Generic`) + * [ ] `Protocol` + * [ ] `Final` + * [ ] `Annotated` + * [ ] `TypeAlias` + * [ ] type guard (`TypeGuard`) + * [ ] others +* `collections.abc` + * [x] `Iterable` * [ ] others -* [ ] type variable (`TypeVar`, `Generic`) -* [ ] `Protocol` -* [ ] `Final` -* [ ] `Annotated` -* [ ] `TypeAlias` * [x] type assertion (`typing.cast`) * [x] type narrowing (`is`, `isinstance`) -* [ ] type guard (`TypeGuard`) --- diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 72507fb..6ff0bd6 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -14,7 +14,7 @@ use erg_compiler::erg_parser::ast::{ NormalTuple, ParamPattern, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, Signature, SubrSignature, Tuple, TypeAscription, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, - VarSignature, VisModifierSpec, ConstPosArg, ConstDict, ConstKeyValue, TupleTypeSpec, + VarSignature, VisModifierSpec, ConstPosArg, ConstDict, ConstKeyValue, TupleTypeSpec, SubrTypeSpec, ParamTySpec, ConstAttribute, }; use erg_compiler::erg_parser::desugar::Desugarer; use erg_compiler::erg_parser::token::{Token, TokenKind, AS, DOT, EQUAL}; @@ -29,6 +29,8 @@ use crate::ast_util::length; use crate::clone::clone_loc_expr; use crate::error::*; +pub const ARROW: Token = Token::dummy(TokenKind::FuncArrow, "->"); + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum NameKind { Variable, @@ -550,6 +552,50 @@ impl ASTConverter { let elems = ConstArgs::new(elems, None, vec![], None); TypeSpec::Enum(elems) } + // TODO: distinguish from collections.abc.Callable + "Callable" => { + let ExpressionType::Tuple { mut elements } = args.node else { + return Self::gen_dummy_type_spec(args.location); + }; + let params = elements.remove(0); + let mut non_defaults = vec![]; + match params.node { + ExpressionType::List { elements } => { + for param in elements.into_iter() { + let t_spec = self.convert_type_spec(param); + non_defaults.push(ParamTySpec::anonymous(t_spec)); + } + } + other => { + let err = CompileError::syntax_error( + self.cfg.input.clone(), + line!() as usize, + pyloc_to_ergloc(params.location, length(&other)), + self.cur_namespace(), + "Expected a list of parameters".into(), + None, + ); + self.errs.push(err); + } + } + let ret = self.convert_type_spec(elements.remove(0)); + TypeSpec::Subr(SubrTypeSpec::new(TypeBoundSpecs::empty(), None, non_defaults, None, vec![], ARROW, ret)) + } + "Iterable" => { + let elem_t = self.convert_expr(args); + let elem_t = match Parser::validate_const_expr(elem_t) { + Ok(elem_t) => elem_t, + Err(err) => { + let err = CompileError::new(err.into(), self.cfg.input.clone(), self.cur_namespace()); + self.errs.push(err); + ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into()))) + } + }; + let elem_t = ConstPosArg::new(elem_t); + let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into()))); + let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Iterable".into()))); + TypeSpec::poly(acc, ConstArgs::pos_only(vec![elem_t], None)) + } "list" => { let len = ConstExpr::Accessor(ConstAccessor::Local( self.convert_ident("_".into(), args.location), @@ -565,7 +611,9 @@ impl ASTConverter { }; let elem_t = ConstPosArg::new(elem_t); let len = ConstPosArg::new(len); - TypeSpec::poly(Identifier::private("Array!".into()), ConstArgs::new(vec![elem_t, len], None, vec![], None)) + let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into()))); + let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Array!".into()))); + TypeSpec::poly(acc, ConstArgs::new(vec![elem_t, len], None, vec![], None)) } "dict" => { let ExpressionType::Tuple { mut elements } = args.node else { @@ -591,7 +639,9 @@ impl ASTConverter { } }; let dict = ConstPosArg::new(ConstExpr::Dict(ConstDict::new(l_brace, r_brace, vec![ConstKeyValue::new(key_t, val_t)]))); - TypeSpec::poly(Identifier::private("Dict!".into()), ConstArgs::new(vec![dict], None, vec![], None)) + let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into()))); + let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Dict!".into()))); + TypeSpec::poly(acc, ConstArgs::new(vec![dict], None, vec![], None)) } "tuple" => { let ExpressionType::Tuple { elements } = args.node else { diff --git a/tests/test.rs b/tests/test.rs index cb64722..b5a5fe9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -76,7 +76,7 @@ fn exec_warns() { #[test] fn exec_typespec() { - expect("tests/typespec.py", 0, 4); + expect("tests/typespec.py", 0, 6); } #[test] diff --git a/tests/typespec.py b/tests/typespec.py index deea20e..f73d4a0 100644 --- a/tests/typespec.py +++ b/tests/typespec.py @@ -1,4 +1,5 @@ -from typing import Union, Optional, Literal +from typing import Union, Optional, Literal, Callable +from collections.abc import Iterable i: Union[int, str] = 1 # OK j: Union[int, str] = "aa" # OK @@ -14,3 +15,12 @@ def f(x: Union[int, str]) -> None: f(1) # OK f(None) # ERR + +def g(x: int) -> int: + return x + +_: Callable[[Union[int, str]], None] = f # OK +_: Callable[[Union[int, str]], None] = g # ERR + +_: Iterable[int] = [1] # OK +_: Iterable[int] = ["a"] # ERR