diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index f41fe13..5288fc4 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -1,5 +1,7 @@ +use erg_common::config::ErgConfig; use erg_common::fresh::fresh_varname; -use erg_common::traits::{Locational}; +use erg_common::traits::{Locational, Stream}; +use erg_compiler::artifact::CompleteArtifact; use rustpython_parser::ast::{StatementType, ExpressionType, Located, Program, Number, StringGroup, Operator, BooleanOperator, UnaryOperator, Suite, Parameters, Parameter, Comparison}; use rustpython_parser::ast::Location as PyLocation; @@ -11,6 +13,9 @@ use erg_compiler::erg_parser::ast::{ BinOp, Lambda, LambdaSignature, TypeBoundSpecs, TypeSpec, SubrSignature, Decorator, NonDefaultParamSignature, DefaultParamSignature, ParamPattern, TypeSpecWithOp, Tuple, NormalTuple, Array, NormalArray, Set, NormalSet, Dict, NormalDict, PreDeclTypeSpec, SimpleTypeSpec, ConstArgs, AttrDef, UnaryOp, KeyValue, Dummy, TypeAscription, ClassDef, Record, Methods, NormalRecord, }; +use erg_compiler::error::{CompileErrors}; + +use crate::error::*; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BlockKind { @@ -71,39 +76,47 @@ fn op_to_token(op: Operator) -> Token { Token::from_str(kind, cont) } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub fn pyloc_to_ergloc(loc: PyLocation, cont_len: usize) -> erg_common::error::Location { + erg_common::error::Location::range(loc.row(), loc.column(), loc.row(), loc.column()+cont_len) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct NameInfo { // last_defined_line: usize, defined_times: usize, + referenced: HashSet, } impl NameInfo { - pub const fn new(defined_times: usize) -> Self { + pub fn new(defined_times: usize) -> Self { Self { // last_defined_line, defined_times, + referenced: HashSet::new(), } } + + pub fn add_referrer(&mut self, referrer: String) { + self.referenced.insert(referrer); + } } #[derive(Debug)] pub struct ASTConverter { + cfg: ErgConfig, namespace: Vec, /// Erg does not allow variables to be defined multiple times, so rename them using this names: Vec>, -} - -impl Default for ASTConverter { - fn default() -> Self { - Self::new() - } + warns: CompileErrors, } impl ASTConverter { - pub fn new() -> Self { + pub fn new(cfg: ErgConfig) -> Self { Self { + cfg, namespace: vec![String::from("")], names: vec![HashMap::new()], + warns: CompileErrors::empty(), } } @@ -116,7 +129,7 @@ impl ASTConverter { None } - fn _get_mut_name_global(&mut self, name: &str) -> Option<&mut NameInfo> { + fn get_mut_name_global(&mut self, name: &str) -> Option<&mut NameInfo> { for space in self.names.iter_mut().rev() { if let Some(name) = space.get_mut(name) { return Some(name); @@ -125,9 +138,13 @@ impl ASTConverter { None } - fn convert_ident(&self, name: String, loc: PyLocation) -> Identifier { + fn convert_ident(&mut self, name: String, loc: PyLocation) -> Identifier { + let referrer = self.namespace.last().unwrap().clone(); let name = escape_name(name); - let cont = if let Some(name_info) = self.get_name_global(&name) { + let cont = if let Some(name_info) = self.get_mut_name_global(&name) { + if referrer != "" { + name_info.add_referrer(referrer); + } if name_info.defined_times > 1 { // HACK: add zero-width characters as postfix format!("{name}{}", "\0".repeat(name_info.defined_times)) @@ -149,7 +166,7 @@ impl ASTConverter { ParamPattern::VarName(name) } - fn convert_nd_param(&self, param: Parameter) -> NonDefaultParamSignature { + fn convert_nd_param(&mut self, param: Parameter) -> NonDefaultParamSignature { let pat = Self::convert_param_pattern(param.arg, param.location); let t_spec = param.annotation .map(|anot| self.convert_type_spec(*anot)) @@ -162,7 +179,7 @@ impl ASTConverter { } // TODO: defaults - fn convert_params(&self, args: Box) -> Params { + fn convert_params(&mut self, args: Box) -> Params { let non_defaults = args.args.into_iter().map(|p| self.convert_nd_param(p)).collect(); // let defaults = args. args.defaults.into_iter().map(convert_default_param).collect(); Params::new(non_defaults, None, vec![], None) @@ -182,7 +199,7 @@ impl ASTConverter { } /// (i, j) => $1 (i = $1[0]; j = $1[1]) - fn convert_expr_to_param(&self, expr: Located) -> (NonDefaultParamSignature, Vec) { + fn convert_expr_to_param(&mut self, expr: Located) -> (NonDefaultParamSignature, Vec) { match expr.node { ExpressionType::Identifier { name } => (Self::convert_for_param(name, expr.location), vec![]), ExpressionType::Tuple { elements } => { @@ -222,7 +239,7 @@ impl ASTConverter { Lambda::new(sig, op, Block::new(body), DefId(0)) } - fn convert_type_spec(&self, expr: Located) -> TypeSpec { + fn convert_type_spec(&mut self, expr: Located) -> TypeSpec { match expr.node { ExpressionType::Identifier { name } => TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(self.convert_ident(name, expr.location), ConstArgs::empty()))), @@ -249,7 +266,7 @@ impl ASTConverter { (l_brace, r_brace) } - fn convert_expr(&self, expr: Located) -> Expr { + fn convert_expr(&mut self, expr: Located) -> Expr { match expr.node { ExpressionType::Number { value } => { let (kind, cont) = match value { @@ -668,19 +685,31 @@ impl ASTConverter { decorator_list, returns } => { - // TODO: If a function can be defined multiple times, which function is called in a recursive function? - self.namespace.push(name.clone()); - let decos = decorator_list.into_iter().map(|ex| Decorator(self.convert_expr(ex))).collect::>(); - self.register_name_info(&name); - let ident = self.convert_ident(name, stmt.location); - let params = self.convert_params(args); - let return_t = returns.map(|ret| self.convert_type_spec(ret)); - let sig = Signature::Subr(SubrSignature::new(decos, ident, TypeBoundSpecs::empty(), params, return_t)); - let block = self.convert_block(body, BlockKind::Function); - let body = DefBody::new(EQUAL, block, DefId(0)); - let def = Def::new(sig, body); - self.namespace.pop(); - Expr::Def(def) + // if reassigning of a function referenced by other functions is occurred, it is an error + if self.get_name_global(&name).map(|info| !info.referenced.is_empty()).unwrap_or(false) { + let warn = reassign_func_error( + self.cfg.input.clone(), + line!() as usize, + pyloc_to_ergloc(stmt.location, name.len()), + self.namespace.join("."), + &name + ); + self.warns.push(warn); + Expr::Dummy(Dummy::empty()) + } else { + self.namespace.push(name.clone()); + let decos = decorator_list.into_iter().map(|ex| Decorator(self.convert_expr(ex))).collect::>(); + self.register_name_info(&name); + let ident = self.convert_ident(name, stmt.location); + let params = self.convert_params(args); + let return_t = returns.map(|ret| self.convert_type_spec(ret)); + let sig = Signature::Subr(SubrSignature::new(decos, ident, TypeBoundSpecs::empty(), params, return_t)); + let block = self.convert_block(body, BlockKind::Function); + let body = DefBody::new(EQUAL, block, DefId(0)); + let def = Def::new(sig, body); + self.namespace.pop(); + Expr::Def(def) + } } StatementType::ClassDef { name, body, bases, keywords: _, decorator_list } => { self.convert_classdef(name, body, bases, decorator_list, stmt.location) @@ -818,10 +847,10 @@ impl ASTConverter { } } - pub fn convert_program(mut self, program: Program) -> Module { + pub fn convert_program(mut self, program: Program) -> CompleteArtifact { let program = program.statements.into_iter() .map(|stmt| self.convert_statement(stmt, true)) .collect(); - Module::new(program) + CompleteArtifact::new(Module::new(program), self.warns) } } diff --git a/crates/py2erg/error.rs b/crates/py2erg/error.rs new file mode 100644 index 0000000..20f1437 --- /dev/null +++ b/crates/py2erg/error.rs @@ -0,0 +1,29 @@ +use erg_common::switch_lang; +use erg_common::config::Input; +use erg_common::error::{Location, ErrorCore, ErrorKind, SubMessage}; +use erg_compiler::error::CompileError; + +pub(crate) fn reassign_func_error( + input: Input, + errno: usize, + loc: Location, + caused_by: String, + name: &str, +) -> CompileError { + CompileError::new( + ErrorCore::new( + vec![SubMessage::only_loc(loc)], + switch_lang!( + "japanese" => format!("{name}は既に宣言され、参照されています。このような関数に再代入するのは望ましくありません"), + "simplified_chinese" => format!("{name}已声明,已被引用。不建议再次赋值"), + "traditional_chinese" => format!("{name}已宣告,已被引用。不建議再次賦值"), + "english" => format!("{name} has already been declared and referenced. It is not recommended to reassign such a function"), + ), + errno, + ErrorKind::AssignError, + loc, + ), + input, + caused_by, + ) +} diff --git a/crates/py2erg/lib.rs b/crates/py2erg/lib.rs index 1f7aa16..4f37a66 100644 --- a/crates/py2erg/lib.rs +++ b/crates/py2erg/lib.rs @@ -1,5 +1,6 @@ mod convert; mod gen_decl; +mod error; pub use convert::*; -pub use gen_decl::*; \ No newline at end of file +pub use gen_decl::*; diff --git a/src/analyze.rs b/src/analyze.rs index bcc94a7..81af108 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -81,13 +81,14 @@ impl PythonAnalyzer { let err = CompileError::new(core, self.cfg.input.clone(), "".into()); IncompleteArtifact::new(None, CompileErrors::from(err), CompileErrors::empty()) })?; - let converter = py2erg::ASTConverter::new(); - let erg_module = converter.convert_program(py_program); + let converter = py2erg::ASTConverter::new(self.cfg.copy()); + let CompleteArtifact{ object: erg_module, mut warns } = converter.convert_program(py_program); let erg_ast = AST::new(erg_common::Str::rc(filename), erg_module); erg_common::log!("AST: {erg_ast}"); self.checker.lower(erg_ast, mode).map_err(|iart| { let errors = handle_err::filter_errors(self.checker.get_mod_ctx(), iart.errors); - let warns = handle_err::filter_errors(self.checker.get_mod_ctx(), iart.warns); + let ws = handle_err::filter_errors(self.checker.get_mod_ctx(), iart.warns); + warns.extend(ws); IncompleteArtifact::new(iart.object, errors, warns) }) }