diff --git a/Cargo.lock b/Cargo.lock index 3a81399..18418e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,8 +253,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "els" version = "0.1.19-nightly.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc21e1a7815740ad6debfd616bd9f5551dc4b10fa1b4e0e2cec42ddf57dbc7d8" +source = "git+https://github.com/erg-lang/erg?branch=main#0f0d04202014c002fbbafa88ff9d42067d57f201" dependencies = [ "erg_common", "erg_compiler", @@ -275,8 +274,7 @@ dependencies = [ [[package]] name = "erg_common" version = "0.6.7-nightly.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d2357638900c5d5f2ee751b4fd2254932fda6a1c50939ffbc8a7acd5903ce9" +source = "git+https://github.com/erg-lang/erg?branch=main#0f0d04202014c002fbbafa88ff9d42067d57f201" dependencies = [ "backtrace-on-stack-overflow", "hermit-abi", @@ -287,8 +285,7 @@ dependencies = [ [[package]] name = "erg_compiler" version = "0.6.7-nightly.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "682faa374e190771be607591426293f6fb56f22f8ed08c3d424a27615bb261cd" +source = "git+https://github.com/erg-lang/erg?branch=main#0f0d04202014c002fbbafa88ff9d42067d57f201" dependencies = [ "erg_common", "erg_parser", @@ -297,8 +294,7 @@ dependencies = [ [[package]] name = "erg_parser" version = "0.6.7-nightly.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e3ee6c10e35b83fe0c6c675ab1697aa72d175e4d10cbcf9a8057485192a604" +source = "git+https://github.com/erg-lang/erg?branch=main#0f0d04202014c002fbbafa88ff9d42067d57f201" dependencies = [ "erg_common", "unicode-xid 0.2.4", @@ -920,9 +916,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.108" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 626319e..d743794 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ edition = "2021" repository = "https://github.com/mtshiba/pylyzer" [workspace.dependencies] -erg_common = { version = "0.6.7-nightly.0", features = ["py_compatible", "els"] } -erg_compiler = { version = "0.6.7-nightly.0", features = ["py_compatible", "els"] } -els = { version = "0.1.19-nightly.0", features = ["py_compatible"] } +# erg_common = { version = "0.6.7-nightly.0", features = ["py_compatible", "els"] } +# erg_compiler = { version = "0.6.7-nightly.0", features = ["py_compatible", "els"] } +# els = { version = "0.1.19-nightly.0", features = ["py_compatible"] } rustpython-parser = "0.1.2" -# erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } -# erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } -# els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] } +erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } +erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } +els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] } [features] debug = ["erg_compiler/debug", "erg_common/debug", "py2erg/debug"] diff --git a/README.md b/README.md index 95ec148..3c9357e 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,11 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker. * [x] local scripts resolving * [ ] local packages resolving * [ ] compound type checking -* [ ] type assertion + * [x] `Union` + * [x] `Optional` + * [x] `list` + * [ ] others +* [ ] type assertion (`typing.cast`) --- diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 940d970..7f5a2a3 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -6,12 +6,13 @@ use erg_common::traits::{Locational, Stream}; use erg_common::{log, set}; use erg_compiler::artifact::IncompleteArtifact; use erg_compiler::erg_parser::ast::{ - Accessor, Args, Array, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstArgs, Decorator, - Def, DefBody, DefId, DefaultParamSignature, Dict, Dummy, Expr, Identifier, KeyValue, Lambda, - LambdaSignature, Literal, Methods, Module, NonDefaultParamSignature, NormalArray, NormalDict, - NormalRecord, NormalSet, NormalTuple, ParamPattern, Params, PosArg, PreDeclTypeSpec, ReDef, - Record, RecordAttrs, Set, Signature, SimpleTypeSpec, SubrSignature, Tuple, TypeAscription, - TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarSignature, + Accessor, Args, Array, ArrayTypeSpec, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, + ConstAccessor, ConstArgs, ConstExpr, Decorator, Def, DefBody, DefId, DefaultParamSignature, + Dict, Dummy, Expr, Identifier, KeyValue, Lambda, LambdaSignature, Literal, Methods, Module, + NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple, + ParamPattern, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, Signature, + SimpleTypeSpec, SubrSignature, Tuple, TypeAscription, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, + UnaryOp, VarName, VarPattern, VarSignature, }; use erg_compiler::erg_parser::token::{Token, TokenKind, COLON, DOT, EQUAL}; use erg_compiler::error::CompileErrors; @@ -502,17 +503,89 @@ impl ASTConverter { Lambda::new(sig, op, Block::new(body), DefId(0)) } + fn convert_ident_type_spec(&mut self, name: String, loc: PyLocation) -> SimpleTypeSpec { + SimpleTypeSpec::new(self.convert_ident(name, loc), ConstArgs::empty()) + } + + fn gen_dummy_type_spec(loc: PyLocation) -> TypeSpec { + TypeSpec::Infer(Token::new( + TokenKind::UBar, + "_", + loc.row() as u32, + loc.column() as u32 - 1, + )) + } + + // TODO: + fn convert_compound_type_spec( + &mut self, + name: String, + args: Located, + ) -> TypeSpec { + match &name[..] { + "Union" => { + let ExpressionType::Tuple { mut elements } = args.node else { + return Self::gen_dummy_type_spec(args.location); + }; + let lhs = self.convert_type_spec(elements.remove(0)); + let rhs = self.convert_type_spec(elements.remove(0)); + TypeSpec::or(lhs, rhs) + } + "Optional" => { + let loc = args.location; + let t = self.convert_type_spec(args); + let ident = Identifier::private_with_line("NoneType".into(), loc.row() as u32); + let none = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new( + ident, + ConstArgs::empty(), + ))); + TypeSpec::or(t, none) + } + "list" => { + let len = ConstExpr::Accessor(ConstAccessor::Local( + self.convert_ident("_".into(), args.location), + )); + let elem_t = self.convert_type_spec(args); + TypeSpec::Array(ArrayTypeSpec::new(elem_t, len)) + } + _ => Self::gen_dummy_type_spec(args.location), + } + } + fn convert_type_spec(&mut self, expr: Located) -> TypeSpec { + #[allow(clippy::collapsible_match)] match expr.node { ExpressionType::Identifier { name } => TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple( - SimpleTypeSpec::new(self.convert_ident(name, expr.location), ConstArgs::empty()), - )), - _other => TypeSpec::Infer(Token::new( - TokenKind::UBar, - "_", - expr.location.row() as u32, - expr.location.column() as u32 - 1, + self.convert_ident_type_spec(name, expr.location), )), + ExpressionType::Attribute { value, name } => { + let namespace = Box::new(self.convert_expr(*value)); + let t = self.convert_ident_type_spec(name, expr.location); + let predecl = PreDeclTypeSpec::Attr { namespace, t }; + TypeSpec::PreDeclTy(predecl) + } + ExpressionType::Subscript { a, b } => match a.node { + ExpressionType::Identifier { name } => self.convert_compound_type_spec(name, *b), + other => { + log!(err "unknown: {other:?}"); + Self::gen_dummy_type_spec(a.location) + } + }, + ExpressionType::Binop { a, op, b } => { + match op { + // A | B + Operator::BitOr => { + let lhs = self.convert_type_spec(*a); + let rhs = self.convert_type_spec(*b); + TypeSpec::or(lhs, rhs) + } + _ => Self::gen_dummy_type_spec(expr.location), + } + } + other => { + log!(err "unknown: {other:?}"); + Self::gen_dummy_type_spec(expr.location) + } } } diff --git a/tests/test.rs b/tests/test.rs index 0bd8e7e..339c9f7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -70,3 +70,8 @@ fn exec_errors() { fn exec_warns() { expect("tests/warns.py", 2, 0); } + +#[test] +fn exec_typespec() { + expect("tests/typespec.py", 0, 2); +} diff --git a/tests/typespec.py b/tests/typespec.py new file mode 100644 index 0000000..f445b7c --- /dev/null +++ b/tests/typespec.py @@ -0,0 +1,8 @@ +from typing import Union, Optional + +i: Union[int, str] = 1 # OK +j: Union[int, str] = "aa" # OK +k: Union[list[int], str] = 1 # ERR +l: Union[list[int], str] = [1] # OK +o: Optional[int] = None # OK +p: Optional[int] = "a" # ERR