Skip to content

Commit

Permalink
feat: support Callable/Iterable
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed May 3, 2023
1 parent b8de76f commit 12cdb09
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 25 deletions.
16 changes: 8 additions & 8 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 @@ -22,9 +22,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer"

[workspace.dependencies]
erg_common = { version = "0.6.12-nightly.4", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.12-nightly.4", features = ["py_compat", "els"] }
els = { version = "0.1.24-nightly.4", features = ["py_compat"] }
erg_common = { version = "0.6.12-nightly.6", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.12-nightly.6", features = ["py_compat", "els"] }
els = { version = "0.1.24-nightly.6", features = ["py_compat"] }
rustpython-parser = "0.1.2"
# erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compat", "els"] }
# erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compat", "els"] }
Expand Down
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,28 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
* [x] builtin modules resolving (partially)
* [x] local scripts resolving
* [ ] local packages resolving
* [ ] compound type checking
* [x] `Union`
* [x] `Optional`
* [x] collection types
* [x] `list`
* [x] `dict`
* [x] `tuple`
* [ ] `typing`
* [x] `Union`
* [x] `Optional`
* [x] `Literal`
* [x] `Callable`
* [ ] `TypedDict`
* [ ] type variable (`TypeVar`, `Generic`)
* [ ] `Protocol`
* [ ] `Final`
* [ ] `Annotated`
* [ ] `TypeAlias`
* [ ] type guard (`TypeGuard`)
* [ ] others
* `collections.abc`
* [x] `Iterable`
* [ ] others
* [ ] type variable (`TypeVar`, `Generic`)
* [ ] `Protocol`
* [ ] `Final`
* [ ] `Annotated`
* [ ] `TypeAlias`
* [x] type assertion (`typing.cast`)
* [x] type narrowing (`is`, `isinstance`)
* [ ] type guard (`TypeGuard`)

---

Expand Down
56 changes: 53 additions & 3 deletions crates/py2erg/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use erg_compiler::erg_parser::ast::{
NormalTuple, ParamPattern, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
Signature, SubrSignature, Tuple, TypeAscription, TypeBoundSpecs, TypeSpec,
TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern,
VarSignature, VisModifierSpec, ConstPosArg, ConstDict, ConstKeyValue, TupleTypeSpec,
VarSignature, VisModifierSpec, ConstPosArg, ConstDict, ConstKeyValue, TupleTypeSpec, SubrTypeSpec, ParamTySpec, ConstAttribute,
};
use erg_compiler::erg_parser::desugar::Desugarer;
use erg_compiler::erg_parser::token::{Token, TokenKind, AS, DOT, EQUAL};
Expand All @@ -29,6 +29,8 @@ use crate::ast_util::length;
use crate::clone::clone_loc_expr;
use crate::error::*;

pub const ARROW: Token = Token::dummy(TokenKind::FuncArrow, "->");

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NameKind {
Variable,
Expand Down Expand Up @@ -550,6 +552,50 @@ impl ASTConverter {
let elems = ConstArgs::new(elems, None, vec![], None);
TypeSpec::Enum(elems)
}
// TODO: distinguish from collections.abc.Callable
"Callable" => {
let ExpressionType::Tuple { mut elements } = args.node else {
return Self::gen_dummy_type_spec(args.location);
};
let params = elements.remove(0);
let mut non_defaults = vec![];
match params.node {
ExpressionType::List { elements } => {
for param in elements.into_iter() {
let t_spec = self.convert_type_spec(param);
non_defaults.push(ParamTySpec::anonymous(t_spec));
}
}
other => {
let err = CompileError::syntax_error(
self.cfg.input.clone(),
line!() as usize,
pyloc_to_ergloc(params.location, length(&other)),
self.cur_namespace(),
"Expected a list of parameters".into(),
None,
);
self.errs.push(err);
}
}
let ret = self.convert_type_spec(elements.remove(0));
TypeSpec::Subr(SubrTypeSpec::new(TypeBoundSpecs::empty(), None, non_defaults, None, vec![], ARROW, ret))
}
"Iterable" => {
let elem_t = self.convert_expr(args);
let elem_t = match Parser::validate_const_expr(elem_t) {
Ok(elem_t) => elem_t,
Err(err) => {
let err = CompileError::new(err.into(), self.cfg.input.clone(), self.cur_namespace());
self.errs.push(err);
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("Obj".into())))
}
};
let elem_t = ConstPosArg::new(elem_t);
let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())));
let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Iterable".into())));
TypeSpec::poly(acc, ConstArgs::pos_only(vec![elem_t], None))
}
"list" => {
let len = ConstExpr::Accessor(ConstAccessor::Local(
self.convert_ident("_".into(), args.location),
Expand All @@ -565,7 +611,9 @@ impl ASTConverter {
};
let elem_t = ConstPosArg::new(elem_t);
let len = ConstPosArg::new(len);
TypeSpec::poly(Identifier::private("Array!".into()), ConstArgs::new(vec![elem_t, len], None, vec![], None))
let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())));
let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Array!".into())));
TypeSpec::poly(acc, ConstArgs::new(vec![elem_t, len], None, vec![], None))
}
"dict" => {
let ExpressionType::Tuple { mut elements } = args.node else {
Expand All @@ -591,7 +639,9 @@ impl ASTConverter {
}
};
let dict = ConstPosArg::new(ConstExpr::Dict(ConstDict::new(l_brace, r_brace, vec![ConstKeyValue::new(key_t, val_t)])));
TypeSpec::poly(Identifier::private("Dict!".into()), ConstArgs::new(vec![dict], None, vec![], None))
let global = ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())));
let acc = ConstAccessor::Attr(ConstAttribute::new(global, Identifier::private("Dict!".into())));
TypeSpec::poly(acc, ConstArgs::new(vec![dict], None, vec![], None))
}
"tuple" => {
let ExpressionType::Tuple { elements } = args.node else {
Expand Down
2 changes: 1 addition & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn exec_warns() {

#[test]
fn exec_typespec() {
expect("tests/typespec.py", 0, 4);
expect("tests/typespec.py", 0, 6);
}

#[test]
Expand Down
12 changes: 11 additions & 1 deletion tests/typespec.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Union, Optional, Literal
from typing import Union, Optional, Literal, Callable
from collections.abc import Iterable

i: Union[int, str] = 1 # OK
j: Union[int, str] = "aa" # OK
Expand All @@ -14,3 +15,12 @@ def f(x: Union[int, str]) -> None:

f(1) # OK
f(None) # ERR

def g(x: int) -> int:
return x

_: Callable[[Union[int, str]], None] = f # OK
_: Callable[[Union[int, str]], None] = g # ERR

_: Iterable[int] = [1] # OK
_: Iterable[int] = ["a"] # ERR

0 comments on commit 12cdb09

Please sign in to comment.