feat: support inheritance

This commit is contained in:
Shunsuke Shibayama 2023-03-07 22:58:21 +09:00
parent bd90b57c58
commit 0d2bfb294f
5 changed files with 84 additions and 41 deletions

20
Cargo.lock generated
View File

@ -252,8 +252,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]] [[package]]
name = "els" name = "els"
version = "0.1.19-nightly.2" version = "0.1.19"
source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94694dcfb56e12207fb27ff73158b9fb759497485790c71de98aa4b0136c72f6"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_compiler", "erg_compiler",
@ -273,8 +274,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.7-nightly.2" version = "0.6.7"
source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c9d058b405901b41dbc282f9927fd5295dc46c50fa2f8fc75f2c3d7783ac77"
dependencies = [ dependencies = [
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"hermit-abi", "hermit-abi",
@ -284,8 +286,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.7-nightly.2" version = "0.6.7"
source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb25a8403805eb864bb3f56c920dfafadca30bf2ff8be5c733d3449e16ae224"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -293,8 +296,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.7-nightly.2" version = "0.6.7"
source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea28285bc487cc9807beafd63664c0903bf8bca86c5ac692dd8a3b783c03bdb7"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"unicode-xid 0.2.4", "unicode-xid 0.2.4",

View File

@ -22,13 +22,13 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer" repository = "https://github.com/mtshiba/pylyzer"
[workspace.dependencies] [workspace.dependencies]
# erg_common = { version = "0.6.7-nightly.2", features = ["py_compatible", "els"] } erg_common = { version = "0.6.7", features = ["py_compatible", "els"] }
# erg_compiler = { version = "0.6.7-nightly.2", features = ["py_compatible", "els"] } erg_compiler = { version = "0.6.7", features = ["py_compatible", "els"] }
# els = { version = "0.1.19-nightly.2", features = ["py_compatible"] } els = { version = "0.1.19", features = ["py_compatible"] }
rustpython-parser = "0.1.2" rustpython-parser = "0.1.2"
erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } # 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"] } # 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"] } # els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] }
[features] [features]
debug = ["erg_compiler/debug", "erg_common/debug", "py2erg/debug"] debug = ["erg_compiler/debug", "erg_common/debug", "py2erg/debug"]

View File

@ -4,7 +4,7 @@
<a href="https://github.com/mtshiba/pylyzer/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/mtshiba/pylyzer.svg"></a> <a href="https://github.com/mtshiba/pylyzer/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/mtshiba/pylyzer.svg"></a>
<a href="https://github.com/mtshiba/pylyzer/actions/workflows/rust.yml"><img alt="Build status" src="https://github.com/mtshiba/pylyzer/actions/workflows/rust.yml/badge.svg"></a> <a href="https://github.com/mtshiba/pylyzer/actions/workflows/rust.yml"><img alt="Build status" src="https://github.com/mtshiba/pylyzer/actions/workflows/rust.yml/badge.svg"></a>
`pylyzer` is a static code analyzer / language server for Python written in Rust. `pylyzer` is a static code analyzer / language server for Python, written in Rust.
## Installation ## Installation
@ -20,8 +20,7 @@ cargo install pylyzer
pip install pylyzer pip install pylyzer
``` ```
If installed this way, type resolution for the standard libraries is not available. If installed this way, you also need to [install Erg](https://github.com/mtshiba/ergup).
If you want to use it, you need to [install Erg](https://github.com/mtshiba/ergup).
```bash ```bash
curl -L https://github.com/mtshiba/ergup/raw/main/ergup.py | python3 curl -L https://github.com/mtshiba/ergup/raw/main/ergup.py | python3

View File

@ -1019,7 +1019,7 @@ impl ASTConverter {
ConstArgs::empty(), ConstArgs::empty(),
))); )));
let sig = Signature::Subr(SubrSignature::new( let sig = Signature::Subr(SubrSignature::new(
HashSet::new(), set! { Decorator(Expr::static_local("Override")) },
call_ident, call_ident,
TypeBoundSpecs::empty(), TypeBoundSpecs::empty(),
params, params,
@ -1046,7 +1046,7 @@ impl ASTConverter {
ConstArgs::empty(), ConstArgs::empty(),
))); )));
let sig = Signature::Subr(SubrSignature::new( let sig = Signature::Subr(SubrSignature::new(
HashSet::new(), set! { Decorator(Expr::static_local("Override")) },
call_ident, call_ident,
TypeBoundSpecs::empty(), TypeBoundSpecs::empty(),
params, params,
@ -1059,13 +1059,23 @@ impl ASTConverter {
Def::new(sig, body) Def::new(sig, body)
} }
fn extract_method(&mut self, body: Vec<Located<StatementType>>) -> (Option<Expr>, ClassAttrs) { fn extract_method(
&mut self,
body: Vec<Located<StatementType>>,
inherit: bool,
) -> (Option<Expr>, ClassAttrs) {
let mut base_type = None; let mut base_type = None;
let mut attrs = vec![]; let mut attrs = vec![];
let mut init_is_defined = false; let mut init_is_defined = false;
for stmt in body { for stmt in body {
match self.convert_statement(stmt, true) { match self.convert_statement(stmt, true) {
Expr::Def(def) => { Expr::Def(mut def) => {
if inherit {
if let Signature::Subr(subr) = &mut def.sig {
subr.decorators
.insert(Decorator(Expr::static_local("Override")));
}
}
if def if def
.sig .sig
.ident() .ident()
@ -1124,7 +1134,7 @@ impl ASTConverter {
_other => {} // TODO: _other => {} // TODO:
} }
} }
if !init_is_defined { if !init_is_defined && !inherit {
attrs.insert(0, ClassAttr::Def(self.gen_default_init(0))); attrs.insert(0, ClassAttr::Def(self.gen_default_init(0)));
} }
(base_type, ClassAttrs::new(attrs)) (base_type, ClassAttrs::new(attrs))
@ -1134,13 +1144,14 @@ impl ASTConverter {
&mut self, &mut self,
ident: Identifier, ident: Identifier,
body: Vec<Located<StatementType>>, body: Vec<Located<StatementType>>,
inherit: bool,
) -> (Option<Expr>, Vec<Methods>) { ) -> (Option<Expr>, Vec<Methods>) {
let class = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new( let class = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(
ident.clone(), ident.clone(),
ConstArgs::empty(), ConstArgs::empty(),
))); )));
let class_as_expr = Expr::Accessor(Accessor::Ident(ident)); let class_as_expr = Expr::Accessor(Accessor::Ident(ident));
let (base_type, attrs) = self.extract_method(body); let (base_type, attrs) = self.extract_method(body, inherit);
let methods = Methods::new(class, class_as_expr, VisModifierSpec::Public(DOT), attrs); let methods = Methods::new(class, class_as_expr, VisModifierSpec::Public(DOT), attrs);
(base_type, vec![methods]) (base_type, vec![methods])
} }
@ -1205,6 +1216,13 @@ impl ASTConverter {
/// ```erg /// ```erg
/// Foo = Inheritable Class() /// Foo = Inheritable Class()
/// ``` /// ```
/// ```python
/// class Foo(Bar): pass
/// ```
/// ↓
/// ```erg
/// Foo = Inherit Bar
/// ```
fn convert_classdef( fn convert_classdef(
&mut self, &mut self,
name: String, name: String,
@ -1217,34 +1235,48 @@ impl ASTConverter {
.into_iter() .into_iter()
.map(|deco| self.convert_expr(deco)) .map(|deco| self.convert_expr(deco))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let _bases = bases let mut bases = bases
.into_iter() .into_iter()
.map(|base| self.convert_expr(base)) .map(|base| self.convert_expr(base))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let inherit = !bases.is_empty();
self.register_name_info(&name, NameKind::Class); self.register_name_info(&name, NameKind::Class);
let class_name_loc = PyLocation::new(loc.row(), loc.column() + 6); let class_name_loc = PyLocation::new(loc.row(), loc.column() + 6);
let ident = self.convert_ident(name, class_name_loc); let ident = self.convert_ident(name, class_name_loc);
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None)); let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
self.namespace.push(ident.inspect().to_string()); self.namespace.push(ident.inspect().to_string());
let (base_type, methods) = self.extract_method_list(ident, body); let (base_type, methods) = self.extract_method_list(ident, body, inherit);
let pos_args = if let Some(base) = base_type { let classdef = if inherit {
vec![PosArg::new(base)] // TODO: multiple inheritance
let pos_args = vec![PosArg::new(bases.remove(0))];
let args = Args::pos_only(pos_args, None);
let inherit_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident("Inherit".to_string(), loc),
));
let inherit_call = inherit_acc.call_expr(args);
let body = DefBody::new(EQUAL, Block::new(vec![inherit_call]), DefId(0));
let def = Def::new(sig, body);
ClassDef::new(def, methods)
} else { } else {
vec![] let pos_args = if let Some(base) = base_type {
vec![PosArg::new(base)]
} else {
vec![]
};
let args = Args::pos_only(pos_args, None);
let class_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident("Class".to_string(), loc),
));
let class_call = class_acc.call_expr(args);
let inheritable_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident("Inheritable".to_string(), loc),
));
let inheritable_call =
inheritable_acc.call_expr(Args::pos_only(vec![PosArg::new(class_call)], None));
let body = DefBody::new(EQUAL, Block::new(vec![inheritable_call]), DefId(0));
let def = Def::new(sig, body);
ClassDef::new(def, methods)
}; };
let args = Args::pos_only(pos_args, None);
let class_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident("Class".to_string(), loc),
));
let class_call = class_acc.call_expr(args);
let inheritable_acc = Expr::Accessor(Accessor::Ident(
self.convert_ident("Inheritable".to_string(), loc),
));
let inheritable_call =
inheritable_acc.call_expr(Args::pos_only(vec![PosArg::new(class_call)], None));
let body = DefBody::new(EQUAL, Block::new(vec![inheritable_call]), DefId(0));
let def = Def::new(sig, body);
let classdef = ClassDef::new(def, methods);
self.namespace.pop(); self.namespace.pop();
Expr::ClassDef(classdef) Expr::ClassDef(classdef)
} }

View File

@ -36,11 +36,19 @@ class D:
return D(self.c - other.x) return D(self.c - other.x)
def __neg__(self): def __neg__(self):
return D(-self.c) return D(-self.c)
def __gt__(self, other: D):
return self.c > other.c
def __init__(self, c): def __init__(self, c):
self.c = c self.c = c
class E(D):
def __add__(self, other: E):
return E(self.c + other.c)
c1 = D(1).c + 1 c1 = D(1).c + 1
d = D(1) + D(2) d = D(1) + D(2)
err = C(1, 2) + D(1) # ERR err = C(1, 2) + D(1) # ERR
ok = D(1) - C(1, 2) # OK ok = D(1) - C(1, 2) # OK
assert D(1) > D(0)
c = -d # OK c = -d # OK
e = E(1)