diff --git a/README.md b/README.md index 27ee5fc..183ea38 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker. * [x] `Union` * [x] `Optional` * [x] `list` + * [x] `dict` + * [x] `tuple` * [x] `Literal` * [ ] `TypedDict` * [ ] others diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 9dcfe5c..51c0c65 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -14,7 +14,7 @@ use erg_compiler::erg_parser::ast::{ NormalTuple, ParamPattern, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set, Signature, SimpleTypeSpec, SubrSignature, Tuple, TypeAscription, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, - VarSignature, VisModifierSpec, ConstPosArg, + VarSignature, VisModifierSpec, ConstPosArg, ConstDict, ConstKeyValue, TupleTypeSpec, }; use erg_compiler::erg_parser::desugar::Desugarer; use erg_compiler::erg_parser::token::{Token, TokenKind, AS, DOT, EQUAL}; @@ -565,6 +565,41 @@ impl ASTConverter { let len = ConstPosArg::new(len); TypeSpec::poly(Identifier::private("Array!".into()), ConstArgs::new(vec![elem_t, len], None, vec![], None)) } + "dict" => { + let ExpressionType::Tuple { mut elements } = args.node else { + return Self::gen_dummy_type_spec(args.location); + }; + let (l_brace, r_brace) = Self::gen_enclosure_tokens(TokenKind::LBrace, elements.iter(), args.location); + let key_t = self.convert_expr(elements.remove(0)); + let key_t = match Parser::validate_const_expr(key_t) { + Ok(key_t) => key_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 val_t = self.convert_expr(elements.remove(0)); + let val_t = match Parser::validate_const_expr(val_t) { + Ok(val_t) => val_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 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)) + } + "tuple" => { + let ExpressionType::Tuple { elements } = args.node else { + return Self::gen_dummy_type_spec(args.location); + }; + let parens = Self::gen_enclosure_tokens(TokenKind::LParen, elements.iter(), args.location); + let tys = elements.into_iter().map(|elem| self.convert_type_spec(elem)).collect(); + let tuple = TupleTypeSpec::new(Some(parens), tys); + TypeSpec::Tuple(tuple) + } _ => Self::gen_dummy_type_spec(args.location), } } @@ -653,6 +688,7 @@ impl ASTConverter { match expr.node { ExpressionType::Number { value } => { let (kind, cont) = match value { + Number::Integer { value } if value >= 0.into() => (TokenKind::NatLit, value.to_string()), Number::Integer { value } => (TokenKind::IntLit, value.to_string()), Number::Float { value } => (TokenKind::RatioLit, value.to_string()), Number::Complex { .. } => { diff --git a/tests/collections.py b/tests/collections.py index 2e7630c..da3d694 100644 --- a/tests/collections.py +++ b/tests/collections.py @@ -10,7 +10,13 @@ union_arr.append(None) # ERR dic = {"a": 1} dic["b"] = 2 - _ = dic["a"] _ = dic["b"] -_ = dic["c"] # ERR \ No newline at end of file +_ = dic["c"] # ERR + +dic2: dict[str, int] = {"a": 1} +_ = dic2["c"] # OK + +t: tuple[int, str] = (1, "a") +_ = t[0] == 1 # OK +_ = t[1] == 1 # ERR diff --git a/tests/test.rs b/tests/test.rs index 1a807da..a100d9f 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -95,6 +95,6 @@ fn exec_casting() { } #[test] -fn exec_collection() { - expect("tests/collections.py", 0, 3); +fn exec_collections() { + expect("tests/collections.py", 0, 4); }