support `Class.__call__`

This commit is contained in:
Shunsuke Shibayama 2022-12-17 12:23:40 +09:00
parent be2bb81d81
commit a350da5142
4 changed files with 48 additions and 9 deletions

6
Cargo.lock generated
View File

@ -227,7 +227,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.0-beta.2" 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 = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@ -237,7 +237,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.0-beta.2" 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 = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -246,7 +246,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.0-beta.2" 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 = [ dependencies = [
"erg_common", "erg_common",
"unicode-xid 0.2.4", "unicode-xid 0.2.4",

View File

@ -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. 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 ## TODOs
* [x] type checking * [x] type checking
* [x] variable
* [x] operator
* [x] function/method
* [x] class
* [x] type inferring * [x] type inferring
* [x] variable
* [x] operator
* [x] function/method
* [x] class
* [x] builtin modules resolving (partially) * [x] builtin modules resolving (partially)
* [x] local scripts resolving * [x] local scripts resolving
* [ ] local packages resolving * [ ] local packages resolving

View File

@ -382,12 +382,13 @@ impl ASTConverter {
// self.x = x // self.x = x
// self.y = y // self.y = y
// ↓ // ↓
// {x: Int, y: Int}, .new(x: Int, y: Int) -> Self // {x: Int, y: Int}, .__call__(x: Int, y: Int): Self = .unreachable()
fn extract_init(&self, def: Def) -> Expr { fn extract_init(&self, def: Def) -> (Expr, Def) {
let l_brace = Token::new(TokenKind::LBrace, "{", def.ln_begin().unwrap(), def.col_begin().unwrap()); 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 r_brace = Token::new(TokenKind::RBrace, "}", def.ln_end().unwrap(), def.col_end().unwrap());
let Signature::Subr(sig) = def.sig else { unreachable!() }; let Signature::Subr(sig) = def.sig else { unreachable!() };
let mut fields = vec![]; let mut fields = vec![];
let mut params = vec![];
for chunk in def.body.block { for chunk in def.body.block {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match chunk { match chunk {
@ -401,8 +402,11 @@ impl ASTConverter {
} else { } else {
"Never".to_string() "Never".to_string()
}; };
let typ = Identifier::public_with_line(DOT, typ_name.into(), attr.obj.ln_begin().unwrap()); let typ_ident = Identifier::public_with_line(DOT, typ_name.into(), attr.obj.ln_begin().unwrap());
let typ = Expr::Accessor(Accessor::Ident(typ)); 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 sig = Signature::Var(VarSignature::new(VarPattern::Ident(attr.ident), None));
let body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0)); let body = DefBody::new(EQUAL, Block::new(vec![typ]), DefId(0));
let field_type_def = Def::new(sig, body); 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<Located<StatementType>>) -> (Expr, ClassAttrs) { fn extract_method(&mut self, body: Vec<Located<StatementType>>) -> (Expr, ClassAttrs) {
@ -423,7 +437,9 @@ impl ASTConverter {
match chunk { match chunk {
Expr::Def(def) => { Expr::Def(def) => {
if def.is_subr() && &def.sig.ident().unwrap().inspect()[..] == "__init__" { 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 { } else {
attrs.push(ClassAttr::Def(def)); attrs.push(ClassAttr::Def(def));
} }

9
tests/class.py Normal file
View File

@ -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