From a350da5142d2dc7a801abc517483d83c247c70fa Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sat, 17 Dec 2022 12:23:40 +0900 Subject: [PATCH] support `Class.__call__` --- Cargo.lock | 6 +++--- README.md | 14 ++++++++++++++ crates/py2erg/convert.rs | 28 ++++++++++++++++++++++------ tests/class.py | 9 +++++++++ 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 tests/class.py diff --git a/Cargo.lock b/Cargo.lock index eaf0ee4..74fbc92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,7 +227,7 @@ dependencies = [ [[package]] name = "erg_common" version = "0.6.0-beta.2" -source = "git+https://github.com/erg-lang/erg?branch=main#d4f8f17e4182bb3f22a3f98a2ca579000bd61a22" +source = "git+https://github.com/erg-lang/erg?branch=main#e7d82ecf4ac1b4338e56e44acbc287e28210499c" dependencies = [ "hermit-abi", "libc", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "erg_compiler" version = "0.6.0-beta.2" -source = "git+https://github.com/erg-lang/erg?branch=main#d4f8f17e4182bb3f22a3f98a2ca579000bd61a22" +source = "git+https://github.com/erg-lang/erg?branch=main#e7d82ecf4ac1b4338e56e44acbc287e28210499c" dependencies = [ "erg_common", "erg_parser", @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "erg_parser" version = "0.6.0-beta.2" -source = "git+https://github.com/erg-lang/erg?branch=main#d4f8f17e4182bb3f22a3f98a2ca579000bd61a22" +source = "git+https://github.com/erg-lang/erg?branch=main#e7d82ecf4ac1b4338e56e44acbc287e28210499c" dependencies = [ "erg_common", "unicode-xid 0.2.4", diff --git a/README.md b/README.md index e81914c..2308339 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,24 @@ This language is a transpiled language that targets Python, and has a static typ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker. It then displays the results with appropriate modifications. +## Limitation + +* pylyzer's type inspector only assumes (potentially) statically typed code, so you cannot use `exec`, etc. + +* Type checking of compound types such as Union types is not supported yet (will be implemented soon). + ## TODOs * [x] type checking + * [x] variable + * [x] operator + * [x] function/method + * [x] class * [x] type inferring + * [x] variable + * [x] operator + * [x] function/method + * [x] class * [x] builtin modules resolving (partially) * [x] local scripts resolving * [ ] local packages resolving diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 19adc76..1f278bf 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -382,12 +382,13 @@ impl ASTConverter { // self.x = x // self.y = y // ↓ - // {x: Int, y: Int}, .new(x: Int, y: Int) -> Self - fn extract_init(&self, def: Def) -> Expr { + // {x: Int, y: Int}, .__call__(x: Int, y: Int): Self = .unreachable() + fn extract_init(&self, def: Def) -> (Expr, Def) { let l_brace = Token::new(TokenKind::LBrace, "{", def.ln_begin().unwrap(), def.col_begin().unwrap()); let r_brace = Token::new(TokenKind::RBrace, "}", def.ln_end().unwrap(), def.col_end().unwrap()); let Signature::Subr(sig) = def.sig else { unreachable!() }; let mut fields = vec![]; + let mut params = vec![]; for chunk in def.body.block { #[allow(clippy::single_match)] match chunk { @@ -401,8 +402,11 @@ impl ASTConverter { } else { "Never".to_string() }; - let typ = Identifier::public_with_line(DOT, typ_name.into(), attr.obj.ln_begin().unwrap()); - let typ = Expr::Accessor(Accessor::Ident(typ)); + let typ_ident = Identifier::public_with_line(DOT, typ_name.into(), attr.obj.ln_begin().unwrap()); + let typ_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(typ_ident.clone(), ConstArgs::empty()))); + let typ_spec = TypeSpecWithOp::new(COLON, typ_spec); + params.push(NonDefaultParamSignature::new(ParamPattern::VarName(attr.ident.name.clone()), Some(typ_spec))); + let typ = Expr::Accessor(Accessor::Ident(typ_ident)); let sig = Signature::Var(VarSignature::new(VarPattern::Ident(attr.ident), None)); let body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0)); let field_type_def = Def::new(sig, body); @@ -412,7 +416,17 @@ impl ASTConverter { _ => {} } } - Expr::Record(Record::Normal(NormalRecord::new(l_brace, r_brace, RecordAttrs::new(fields)))) + let record = Record::Normal(NormalRecord::new(l_brace, r_brace, RecordAttrs::new(fields))); + let call_ident = Identifier::new(Some(DOT), VarName::from_static("__call__")); + let params = Params::new(params, None, vec![], None); + let class_ident = Identifier::public_with_line(DOT, self.namespace.last().unwrap().into(), sig.ln_begin().unwrap()); + let class_spec = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(class_ident, ConstArgs::empty()))); + let sig = Signature::Subr(SubrSignature::new(HashSet::new(), call_ident, TypeBoundSpecs::empty(), params, Some(class_spec))); + let unreachable_acc = Identifier::new(Some(DOT), VarName::from_static("exit")); + let body = Expr::Accessor(Accessor::Ident(unreachable_acc)).call_expr(Args::empty()); + let body = DefBody::new(EQUAL, Block::new(vec![body]), DefId(0)); + let def = Def::new(sig, body); + (Expr::Record(record), def) } fn extract_method(&mut self, body: Vec>) -> (Expr, ClassAttrs) { @@ -423,7 +437,9 @@ impl ASTConverter { match chunk { Expr::Def(def) => { if def.is_subr() && &def.sig.ident().unwrap().inspect()[..] == "__init__" { - base_type = self.extract_init(def); + let (base_t, call_def) = self.extract_init(def); + base_type = base_t; + attrs.push(ClassAttr::Def(call_def)); } else { attrs.push(ClassAttr::Def(def)); } diff --git a/tests/class.py b/tests/class.py new file mode 100644 index 0000000..39cc9eb --- /dev/null +++ b/tests/class.py @@ -0,0 +1,9 @@ +class C: + def __init__(x: int): + self.x = x + def method(self): + return self.x + +a = C(1).method() # OK +_: int = a + 1 +b = C("a").method() # ERR