diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 6749617..233bea6 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -8,12 +8,12 @@ use erg_compiler::artifact::IncompleteArtifact; use erg_compiler::erg_parser::ast::{ Accessor, Args, Array, ArrayTypeSpec, BinOp, Block, ClassAttr, ClassAttrs, ClassDef, ConstAccessor, ConstArgs, ConstExpr, Decorator, Def, DefBody, DefId, DefaultParamSignature, - Dict, Dummy, Expr, Identifier, KeyValue, Lambda, LambdaSignature, Literal, Methods, Module, - NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, 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, + Dict, Dummy, Expr, Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, + Module, NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, + 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, }; use erg_compiler::erg_parser::desugar::Desugarer; use erg_compiler::erg_parser::token::{Token, TokenKind, COLON, DOT, EQUAL}; @@ -1231,7 +1231,10 @@ impl ASTConverter { let classdef = if inherit { // TODO: multiple inheritance let pos_args = vec![PosArg::new(bases.remove(0))]; - let args = Args::pos_only(pos_args, None); + let mut args = Args::pos_only(pos_args, None); + if let Some(rec @ Expr::Record(_)) = base_type { + args.push_kw(KwArg::new(Token::symbol("Additional"), None, rec)); + } let inherit_acc = Expr::Accessor(Accessor::Ident( self.convert_ident("Inherit".to_string(), loc), )); diff --git a/crates/py2erg/gen_decl.rs b/crates/py2erg/gen_decl.rs index e388b2c..ba36e9f 100644 --- a/crates/py2erg/gen_decl.rs +++ b/crates/py2erg/gen_decl.rs @@ -5,7 +5,8 @@ use erg_common::config::Input; use erg_common::log; use erg_compiler::context::register::PylyzerStatus; use erg_compiler::hir::{Expr, HIR}; -use erg_compiler::ty::HasType; +use erg_compiler::ty::value::{GenTypeObj, TypeObj}; +use erg_compiler::ty::{HasType, Type}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CheckStatus { @@ -28,7 +29,7 @@ pub struct DeclFile { } fn escape_type(typ: String) -> String { - typ.replace('%', "Type_") + typ.replace('%', "Type_").replace("", "") } pub fn gen_decl_er(input: &Input, hir: HIR, status: CheckStatus) -> DeclFile { @@ -45,28 +46,65 @@ pub fn gen_decl_er(input: &Input, hir: HIR, status: CheckStatus) -> DeclFile { }; let mut code = format!("{status}\n"); for chunk in hir.module.into_iter() { - match chunk { - Expr::Def(def) => { - let name = def.sig.ident().inspect().replace('\0', ""); - let typ = def.sig.ident().ref_t().to_string(); - let typ = escape_type(typ); - let decl = format!(".{name}: {typ}"); - code += &decl; - } - Expr::ClassDef(def) => { - let name = def.sig.ident().inspect().replace('\0', ""); - let decl = format!(".{name}: ClassType"); - code += &decl; - } - _ => {} - } - code.push('\n'); + gen_chunk_decl("", chunk, &mut code); } log!("code:\n{code}"); let filename = input.unescaped_filename().replace(".py", ".d.er"); DeclFile { filename, code } } +fn gen_chunk_decl(namespace: &str, chunk: Expr, code: &mut String) { + match chunk { + Expr::Def(def) => { + let name = def.sig.ident().inspect().replace('\0', ""); + let typ = def.sig.ident().ref_t().to_string(); + let typ = escape_type(typ); + let decl = format!("{namespace}.{name}: {typ}"); + *code += &decl; + } + Expr::ClassDef(def) => { + let class_name = def.sig.ident().inspect().replace('\0', ""); + let namespace = format!("{namespace}.{class_name}"); + let decl = format!(".{class_name}: ClassType"); + *code += &decl; + code.push('\n'); + if let GenTypeObj::Subclass(class) = &def.obj { + let sup = class.sup.as_ref().typ().to_string(); + let sup = escape_type(sup); + let decl = format!(".{class_name} <: {sup}\n"); + *code += &decl; + } + if let Some(TypeObj::Builtin { + t: Type::Record(rec), + .. + }) = def.obj.base_or_sup() + { + for (attr, t) in rec.iter() { + let typ = escape_type(t.to_string()); + let decl = format!("{namespace}.{}: {typ}\n", attr.symbol); + *code += &decl; + } + } + if let Some(TypeObj::Builtin { + t: Type::Record(rec), + .. + }) = def.obj.additional() + { + for (attr, t) in rec.iter() { + let typ = escape_type(t.to_string()); + let decl = format!("{namespace}.{}: {typ}\n", attr.symbol); + *code += &decl; + } + } + for attr in def.methods.into_iter() { + gen_chunk_decl(&namespace, attr, code); + } + } + _ => {} + } + code.push('\n'); +} + pub fn dump_decl_er(input: Input, hir: HIR, status: CheckStatus) { let file = gen_decl_er(&input, hir, status); let mut dir = input.dir(); diff --git a/tests/export.py b/tests/export.py index cfc56f4..e4f901f 100644 --- a/tests/export.py +++ b/tests/export.py @@ -2,3 +2,16 @@ test = 1 def add(a, b): return a + b + +class C: + x: int + const = 1 + def __init__(self, x): + self.x = x + def method(self, y): return self.x + y + +class D(C): + y: int + def __init__(self, x, y): + self.x = x + self.y = y diff --git a/tests/import.py b/tests/import.py index 9bfa9fb..5d7563f 100644 --- a/tests/import.py +++ b/tests/import.py @@ -14,6 +14,15 @@ print(export.test) print(export.add(1, 2)) assert export.add("a", "b") == 1 # ERR +c = export.C(1) +assert c.const == 1 +assert c.x == 2 +assert c.method(2) == 3 + +d = export.D(1, 2) +assert d.x == 1 +assert d.y == 2 + from glob import glob print(glob("*")) glob = None