Skip to content

Commit

Permalink
feat: ABC implementation check
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Dec 27, 2024
1 parent 233b7ee commit 2a98535
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 41 deletions.
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer"

[workspace.dependencies]
erg_common = { version = "0.6.49", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.49", features = ["py_compat", "els"] }
els = { version = "0.1.61", features = ["py_compat"] }
erg_common = { version = "0.6.50", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.50", features = ["py_compat", "els"] }
els = { version = "0.1.50", features = ["py_compat"] }
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
# rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
* [x] function/method
* [x] class
* [ ] `async/await`
* [ ] user-defined abstract class
* [x] type inference
* [x] variable
* [x] operator
Expand Down Expand Up @@ -165,10 +166,13 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
* [x] type narrowing
* [ ] others
* [ ] `collections.abc`
* [x] `Collection`
* [x] `Container`
* [x] `Generator`
* [x] `Iterable`
* [x] `Iterator`
* [x] `Mapping`
* [x] `Sequence`
* [x] `Mapping`, `MutableMapping`
* [x] `Sequence`, `MutableSequence`
* [ ] others
* [x] type assertion (`typing.cast`)
* [x] type narrowing (`is`, `isinstance`)
Expand Down
74 changes: 48 additions & 26 deletions crates/py2erg/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ use erg_compiler::erg_parser::ast::{
KeyValue, KwArg, Lambda, LambdaSignature, List, ListComprehension, Literal, Methods, Module,
NonDefaultParamSignature, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple,
ParamPattern, ParamTySpec, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription,
TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern,
VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAppArgs,
TypeAppArgsKind, TypeAscription, TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp,
UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature,
VisModifierSpec,
};
use erg_compiler::erg_parser::desugar::Desugarer;
use erg_compiler::erg_parser::token::{Token, TokenKind, AS, COLON, DOT, EQUAL};
Expand Down Expand Up @@ -991,43 +992,54 @@ impl ASTConverter {
Lambda::new(sig, op, Block::new(body), DefId(0))
}

fn convert_ident_type_spec(&mut self, name: String, loc: PyLocation) -> TypeSpec {
fn convert_ident_type_spec(&mut self, name: String, range: PySourceRange) -> TypeSpec {
let loc = pyloc_to_ergloc(range);
match &name[..] {
// Iterable[T] => Iterable(T), Iterable => Iterable(Obj)
global_unary_collections!() => TypeSpec::poly(
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
.attr(Identifier::private(name.into())),
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
"global".into(),
loc,
)))
.attr(Identifier::private_with_loc(name.into(), loc)),
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
Identifier::private("Obj".into()),
Identifier::private_with_loc("Obj".into(), loc),
))),
),
// MutableSequence[T] => Sequence!(T), MutableSequence => Sequence!(Obj)
global_mutable_unary_collections!() => TypeSpec::poly(
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
.attr(Identifier::private(
format!("{}!", name.trim_start_matches("Mutable")).into(),
)),
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
"global".into(),
loc,
)))
.attr(Identifier::private_with_loc(
format!("{}!", name.trim_start_matches("Mutable")).into(),
loc,
)),
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
Identifier::private("Obj".into()),
Identifier::private_with_loc("Obj".into(), loc),
))),
),
// Mapping => Mapping(Obj, Obj)
global_binary_collections!() => TypeSpec::poly(
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
.attr(Identifier::private(name.into())),
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
"global".into(),
loc,
)))
.attr(Identifier::private_with_loc(name.into(), loc)),
ConstArgs::pos_only(
vec![
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
Identifier::private("Obj".into()),
Identifier::private_with_loc("Obj".into(), loc),
))),
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
Identifier::private("Obj".into()),
Identifier::private_with_loc("Obj".into(), loc),
))),
],
None,
),
),
_ => TypeSpec::mono(self.convert_ident(name, loc)),
_ => TypeSpec::mono(self.convert_ident(name, range.start)),
}
}

Expand Down Expand Up @@ -1427,13 +1439,13 @@ impl ASTConverter {
.unwrap()
.appeared_type_names
.insert(name.id.to_string());
self.convert_ident_type_spec(name.id.to_string(), name.location())
self.convert_ident_type_spec(name.id.to_string(), name.range)
}
py_ast::Expr::Constant(cons) => {
if cons.value.is_none() {
self.convert_ident_type_spec("NoneType".into(), cons.location())
self.convert_ident_type_spec("NoneType".into(), cons.range)
} else if let Some(name) = cons.value.as_str() {
self.convert_ident_type_spec(name.into(), cons.location())
self.convert_ident_type_spec(name.into(), cons.range)
} else {
let err = CompileError::syntax_error(
self.cfg.input.clone(),
Expand All @@ -1458,8 +1470,7 @@ impl ASTConverter {
global_unary_collections!()
| global_mutable_unary_collections!()
| global_binary_collections!() => {
return self
.convert_ident_type_spec(attr.attr.to_string(), attr.range.start)
return self.convert_ident_type_spec(attr.attr.to_string(), attr.range)
}
"Any" => return TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(t)),
_ => {}
Expand Down Expand Up @@ -2218,9 +2229,19 @@ impl ASTConverter {
&mut self,
ident: Identifier,
body: Vec<py_ast::Stmt>,
inherit: bool,
base: Option<py_ast::Expr>,
) -> (Option<Expr>, Vec<Methods>) {
let class = TypeSpec::mono(ident.clone());
let inherit = base.is_some();
let class = if let Some(base) = base {
let base_spec = self.convert_type_spec(base.clone());
let expr = self.convert_expr(base);
let loc = expr.loc();
let base = TypeSpecWithOp::new(COLON, base_spec, expr);
let args = TypeAppArgs::new(loc, TypeAppArgsKind::SubtypeOf(Box::new(base)), loc);
TypeSpec::type_app(TypeSpec::mono(ident.clone()), args)
} else {
TypeSpec::mono(ident.clone())
};
let class_as_expr = Expr::Accessor(Accessor::Ident(ident));
let (base_type, attrs) = self.extract_method(body, inherit);
self.block_id_counter += 1;
Expand Down Expand Up @@ -2398,12 +2419,13 @@ impl ASTConverter {
.into_iter()
.map(|deco| self.convert_expr(deco))
.collect::<Vec<_>>();
let inherit = class_def.bases.first().cloned();
let is_inherit = inherit.is_some();
let mut bases = class_def
.bases
.into_iter()
.map(|base| self.convert_expr(base))
.collect::<Vec<_>>();
let inherit = !bases.is_empty();
self.register_name_info(&name, NameKind::Class);
let class_name_loc = PyLocation {
row: loc.row,
Expand All @@ -2413,7 +2435,7 @@ impl ASTConverter {
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
self.grow(ident.inspect().to_string(), BlockKind::Class);
let (base_type, methods) = self.extract_method_list(ident, class_def.body, inherit);
let classdef = if inherit {
let classdef = if is_inherit {
// TODO: multiple inheritance
let pos_args = vec![PosArg::new(bases.remove(0))];
let mut args = Args::pos_only(pos_args, None);
Expand Down
19 changes: 19 additions & 0 deletions tests/abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from collections.abc import Sequence

class Vec(Sequence):
x: list[int]

def __init__(self):
self.x = []

def __getitem__(self, i: int) -> int:
return self.x[i]

def __iter__(self):
return iter(self.x)

def __len__(self) -> int:
return len(self.x)

def __contains__(self, i: int) -> bool:
return i in self.x
5 changes: 5 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ pub fn expect(file_path: &'static str, warns: usize, errors: usize) -> Result<()
exec_new_thread(move || _expect(file_path, warns, errors), file_path)
}

#[test]
fn exec_abc() -> Result<(), String> {
expect("tests/abc.py", 0, 0)
}

#[test]
fn exec_test() -> Result<(), String> {
expect("tests/test.py", 0, 11)
Expand Down

0 comments on commit 2a98535

Please sign in to comment.