diff --git a/compiler/erg_compiler/checker.rs b/compiler/erg_compiler/checker.rs new file mode 100644 index 00000000..9b8f6d2a --- /dev/null +++ b/compiler/erg_compiler/checker.rs @@ -0,0 +1,72 @@ +use erg_common::config::ErgConfig; +use erg_common::error::MultiErrorDisplay; +use erg_common::traits::Runnable; + +use erg_parser::ast::AST; +use erg_parser::builder::ASTBuilder; + +use crate::effectcheck::SideEffectChecker; +use crate::error::{TyCheckError, TyCheckErrors}; +use crate::hir::HIR; +use crate::lower::ASTLowerer; +use crate::ownercheck::OwnershipChecker; + +/// Summarize lowering, side-effect checking, and ownership checking +#[derive(Debug)] +pub struct Checker { + cfg: ErgConfig, + lowerer: ASTLowerer, + ownership_checker: OwnershipChecker, +} + +impl Runnable for Checker { + type Err = TyCheckError; + type Errs = TyCheckErrors; + const NAME: &'static str = "Erg type-checker"; + + fn new(cfg: ErgConfig) -> Self { + Self { + ownership_checker: OwnershipChecker::new(), + lowerer: ASTLowerer::new(), + cfg, + } + } + + #[inline] + fn cfg(&self) -> &ErgConfig { + &self.cfg + } + + #[inline] + fn finish(&mut self) {} + + fn clear(&mut self) {} + + fn exec(&mut self) -> Result<(), Self::Errs> { + let mut builder = ASTBuilder::new(self.cfg.copy()); + let ast = builder.build()?; + let hir = self.check(ast, "exec")?; + println!("{hir}"); + Ok(()) + } + + fn eval(&mut self, src: String) -> Result { + let mut builder = ASTBuilder::new(self.cfg.copy()); + let ast = builder.build_with_input(src)?; + let hir = self.check(ast, "eval")?; + Ok(hir.to_string()) + } +} + +impl Checker { + pub fn check(&mut self, ast: AST, mode: &str) -> Result { + let (hir, warns) = self.lowerer.lower(ast, mode)?; + if self.cfg.verbose >= 2 { + warns.fmt_all_stderr(); + } + let effect_checker = SideEffectChecker::new(); + let hir = effect_checker.check(hir)?; + let hir = self.ownership_checker.check(hir)?; + Ok(hir) + } +} diff --git a/compiler/erg_compiler/compile.rs b/compiler/erg_compiler/compile.rs index ec819777..0c2f9807 100644 --- a/compiler/erg_compiler/compile.rs +++ b/compiler/erg_compiler/compile.rs @@ -4,19 +4,15 @@ use std::path::Path; use erg_common::config::{ErgConfig, Input}; -use erg_common::error::MultiErrorDisplay; use erg_common::log; use erg_common::traits::{Runnable, Stream}; use erg_type::codeobj::CodeObj; -use erg_parser::ParserRunner; +use erg_parser::builder::ASTBuilder; +use crate::checker::Checker; use crate::codegen::CodeGenerator; -use crate::effectcheck::SideEffectChecker; use crate::error::{CompileError, CompileErrors, TyCheckErrors}; -use crate::lower::ASTLowerer; -use crate::ownercheck::OwnershipChecker; -use crate::reorder::Reorderer; /// * registered as global -> Global /// * defined in the toplevel scope (and called in the inner scope) -> Global @@ -91,13 +87,11 @@ impl AccessKind { } } -/// Generates a `CodeObj` from an `AST`. -/// The input AST is not typed, so it's typed by `ASTLowerer` according to the cfg.opt_level. +/// Generates a `CodeObj` from an String or other File inputs. #[derive(Debug)] pub struct Compiler { cfg: ErgConfig, - lowerer: ASTLowerer, - ownership_checker: OwnershipChecker, + checker: Checker, code_generator: CodeGenerator, } @@ -108,9 +102,8 @@ impl Runnable for Compiler { fn new(cfg: ErgConfig) -> Self { Self { + checker: Checker::new(cfg.copy()), code_generator: CodeGenerator::new(cfg.copy()), - ownership_checker: OwnershipChecker::new(), - lowerer: ASTLowerer::new(), cfg, } } @@ -163,25 +156,11 @@ impl Compiler { log!(info "the compiling process has started."); let mut cfg = self.cfg.copy(); cfg.input = Input::Str(src); - let mut parser = ParserRunner::new(cfg); - let ast = parser.parse()?; - let linker = Reorderer::new(); - let ast = linker.link(ast).map_err(|errs| self.convert(errs))?; - let (hir, warns) = self - .lowerer - .lower(ast, mode) - .map_err(|errs| self.convert(errs))?; - if self.cfg.verbose >= 2 { - let warns = self.convert(warns); - warns.fmt_all_stderr(); - } - let effect_checker = SideEffectChecker::new(); - let hir = effect_checker - .check(hir) - .map_err(|errs| self.convert(errs))?; + let mut ast_builder = ASTBuilder::new(cfg); + let ast = ast_builder.build()?; let hir = self - .ownership_checker - .check(hir) + .checker + .check(ast, mode) .map_err(|errs| self.convert(errs))?; let codeobj = self.code_generator.codegen(hir); log!(info "code object:\n{}", codeobj.code_info()); diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 9e10a86d..139afc00 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -559,10 +559,15 @@ impl Context { if sub == Type::Never { return Ok(mono_proj(*lhs, rhs)); } - for (_ty, ty_ctx) in self - .get_nominal_super_type_ctxs(&sub) - .ok_or_else(|| todo!("{sub}"))? - { + for (_ty, ty_ctx) in self.get_nominal_super_type_ctxs(&sub).ok_or_else(|| { + EvalError::no_var_error( + line!() as usize, + t_loc, + self.caused_by(), + &rhs, + None, // TODO: + ) + })? { if let Ok(obj) = ty_ctx.get_const_local(&Token::symbol(&rhs), &self.name) { if let ValueObj::Type(quant_t) = obj { let subst_ctx = SubstContext::new(&sub, ty_ctx); diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 96d01ced..afb3df48 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -246,10 +246,15 @@ impl Context { } } } - for (_, ctx) in self - .get_nominal_super_type_ctxs(&self_t) - .ok_or_else(|| todo!())? - { + for (_, ctx) in self.get_nominal_super_type_ctxs(&self_t).ok_or_else(|| { + TyCheckError::no_var_error( + line!() as usize, + obj.loc(), + self.caused_by(), + &self_t.to_string(), + None, // TODO: + ) + })? { match ctx.rec_get_var_t(ident, namespace) { Ok(t) => { return Ok(t); @@ -348,7 +353,15 @@ impl Context { if let Some(method_name) = method_name.as_ref() { for (_, ctx) in self .get_nominal_super_type_ctxs(obj.ref_t()) - .ok_or_else(|| todo!())? + .ok_or_else(|| { + TyCheckError::no_var_error( + line!() as usize, + obj.loc(), + self.caused_by(), + &obj.to_string(), + None, // TODO: + ) + })? { if let Some(vi) = ctx .locals @@ -856,10 +869,15 @@ impl Context { namespace: &Str, ) -> TyCheckResult { let self_t = obj.ref_t(); - for (_, ctx) in self - .get_nominal_super_type_ctxs(self_t) - .ok_or_else(|| todo!())? - { + for (_, ctx) in self.get_nominal_super_type_ctxs(self_t).ok_or_else(|| { + TyCheckError::no_var_error( + line!() as usize, + obj.loc(), + self.caused_by(), + &self_t.to_string(), + None, // TODO: + ) + })? { if let Ok(t) = ctx.get_const_local(name, namespace) { return Ok(t); } diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index 29468e76..dfc3ac11 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -237,6 +237,15 @@ pub struct TyCheckError { pub caused_by: AtomicStr, } +impl From for TyCheckError { + fn from(err: ParserRunnerError) -> Self { + Self { + core: err.core, + caused_by: "".into(), + } + } +} + impl ErrorDisplay for TyCheckError { fn core(&self) -> &ErrorCore { &self.core @@ -1169,6 +1178,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" pub struct TyCheckErrors(Vec); impl_stream_for_wrapper!(TyCheckErrors, TyCheckError); +impl MultiErrorDisplay for TyCheckErrors {} impl From> for TyCheckErrors { fn from(errs: Vec) -> Self { @@ -1189,6 +1199,12 @@ impl From for TyCheckErrors { } } +impl From for TyCheckErrors { + fn from(err: ParserRunnerErrors) -> Self { + Self(err.into_iter().map(TyCheckError::from).collect()) + } +} + pub type TyCheckResult = Result; pub type TyCheckWarning = TyCheckError; pub type TyCheckWarnings = TyCheckErrors; diff --git a/compiler/erg_compiler/lib.rs b/compiler/erg_compiler/lib.rs index 392a1fbc..795219c4 100644 --- a/compiler/erg_compiler/lib.rs +++ b/compiler/erg_compiler/lib.rs @@ -3,6 +3,7 @@ extern crate erg_common; pub extern crate erg_parser; +mod checker; mod compile; pub use compile::*; mod codegen; @@ -14,5 +15,4 @@ pub mod lower; pub mod mod_cache; pub mod optimize; pub mod ownercheck; -pub mod reorder; pub mod varinfo; diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 46eab189..94923557 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -2,7 +2,7 @@ //! //! ASTLowerer(ASTからHIRへの変換器)を実装 use erg_common::astr::AtomicStr; -use erg_common::config::{ErgConfig, Input}; +use erg_common::config::ErgConfig; use erg_common::error::{Location, MultiErrorDisplay}; use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::vis::Visibility; @@ -10,10 +10,8 @@ use erg_common::{enum_unwrap, fmt_option, fn_name, get_hash, log, switch_lang, S use erg_parser::ast; use erg_parser::ast::AST; -use erg_parser::error::ParserRunnerErrors; -use erg_parser::lex::Lexer; +use erg_parser::builder::ASTBuilder; use erg_parser::token::{Token, TokenKind}; -use erg_parser::Parser; use erg_type::constructors::{array, array_mut, free_var, func, mono, poly, proc, quant}; use erg_type::free::Constraint; @@ -27,7 +25,6 @@ use crate::error::{ }; use crate::hir; use crate::hir::HIR; -use crate::reorder::Reorderer; use crate::varinfo::VarKind; use Visibility::*; @@ -62,14 +59,8 @@ impl Runnable for ASTLowererRunner { } fn exec(&mut self) -> Result<(), Self::Errs> { - let ts = Lexer::new(self.input().clone()) - .lex() - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; - let ast = Parser::new(ts) - .parse(Str::ever(self.cfg.module)) - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; - let linker = Reorderer::new(); - let ast = linker.link(ast).map_err(|errs| self.convert(errs))?; + let mut ast_builder = ASTBuilder::new(self.cfg.copy()); + let ast = ast_builder.build()?; let (hir, warns) = self .lowerer .lower(ast, "exec") @@ -83,14 +74,8 @@ impl Runnable for ASTLowererRunner { } fn eval(&mut self, src: String) -> Result { - let ts = Lexer::new(Input::Str(src)) - .lex() - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; - let ast = Parser::new(ts) - .parse(Str::ever(self.cfg.module)) - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; - let linker = Reorderer::new(); - let ast = linker.link(ast).map_err(|errs| self.convert(errs))?; + let mut ast_builder = ASTBuilder::new(self.cfg.copy()); + let ast = ast_builder.build_with_input(src)?; let (hir, _) = self .lowerer .lower(ast, "eval") diff --git a/compiler/erg_compiler/mod_cache.rs b/compiler/erg_compiler/mod_cache.rs index be423fbe..25499462 100644 --- a/compiler/erg_compiler/mod_cache.rs +++ b/compiler/erg_compiler/mod_cache.rs @@ -27,24 +27,24 @@ impl ModId { #[derive(Debug)] pub struct ModuleEntry { - id: ModId, // builtin == 0, __main__ == 1 - hir: Option, + _id: ModId, // builtin == 0, __main__ == 1 + _hir: Option, ctx: Rc, } impl ModuleEntry { pub fn new(id: ModId, hir: Option, ctx: Context) -> Self { Self { - id, - hir, + _id: id, + _hir: hir, ctx: Rc::new(ctx), } } pub fn builtin(ctx: Context) -> Self { Self { - id: ModId::builtin(), - hir: None, + _id: ModId::builtin(), + _hir: None, ctx: Rc::new(ctx), } } diff --git a/compiler/erg_parser/builder.rs b/compiler/erg_parser/builder.rs new file mode 100644 index 00000000..3659863f --- /dev/null +++ b/compiler/erg_parser/builder.rs @@ -0,0 +1,50 @@ +use erg_common::config::ErgConfig; +use erg_common::traits::Runnable; +use erg_common::Str; + +use crate::ast::AST; +use crate::desugar::Desugarer; +use crate::error::ParserRunnerErrors; +use crate::parse::ParserRunner; +use crate::reorder::Reorderer; + +/// Summarize parsing, desugaring, and reordering +pub struct ASTBuilder { + runner: ParserRunner, +} + +impl ASTBuilder { + pub fn new(cfg: ErgConfig) -> Self { + Self { + runner: ParserRunner::new(cfg), + } + } + + pub fn build(&mut self) -> Result { + let module = self.runner.parse()?; + let mut desugarer = Desugarer::new(); + let module = desugarer.desugar(module); + let mut desugarer = Desugarer::new(); + let module = desugarer.desugar(module); + let ast = AST::new(Str::ever(self.runner.cfg().module), module); + let reorderer = Reorderer::new(); + let ast = reorderer + .reorder(ast) + .map_err(|errs| ParserRunnerErrors::convert(self.runner.input(), errs))?; + Ok(ast) + } + + pub fn build_with_input(&mut self, src: String) -> Result { + let module = self.runner.parse_with_input(src)?; + let mut desugarer = Desugarer::new(); + let module = desugarer.desugar(module); + let mut desugarer = Desugarer::new(); + let module = desugarer.desugar(module); + let ast = AST::new(Str::ever(self.runner.cfg().module), module); + let reorderer = Reorderer::new(); + let ast = reorderer + .reorder(ast) + .map_err(|errs| ParserRunnerErrors::convert(self.runner.input(), errs))?; + Ok(ast) + } +} diff --git a/compiler/erg_parser/desugar.rs b/compiler/erg_parser/desugar.rs index fec221da..5383cb9f 100644 --- a/compiler/erg_parser/desugar.rs +++ b/compiler/erg_parser/desugar.rs @@ -8,7 +8,7 @@ use erg_common::set::Set; use erg_common::traits::{Locational, Stream}; use erg_common::Str; -use erg_common::{enum_unwrap, get_hash, set}; +use erg_common::{enum_unwrap, get_hash, log, set}; use crate::ast::{ Accessor, Args, Array, ArrayComprehension, ArrayWithLength, BinOp, Block, Call, DataPack, Def, @@ -48,10 +48,12 @@ impl Desugarer { #[allow(clippy::let_and_return)] pub fn desugar(&mut self, module: Module) -> Module { + log!(info "the desugaring process has started."); let module = self.desugar_multiple_pattern_def(module); let module = self.desugar_pattern(module); let module = self.desugar_shortened_record(module); - // let module = self.desugar_self(module); + log!(info "AST (desugared):\n{module}"); + log!(info "the desugaring process has completed."); module } diff --git a/compiler/erg_parser/error.rs b/compiler/erg_parser/error.rs index bd1b207e..07e7cbf9 100644 --- a/compiler/erg_parser/error.rs +++ b/compiler/erg_parser/error.rs @@ -2,6 +2,7 @@ //! //! パーサーが出すエラーを定義 use erg_common::astr::AtomicStr; +use erg_common::color::{RED, RESET}; use erg_common::config::Input; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; use erg_common::traits::Stream; @@ -82,6 +83,35 @@ impl LexError { ) -> Self { Self::new(ErrorCore::new(errno, SyntaxWarning, loc, desc, hint)) } + + pub fn no_var_error( + errno: usize, + loc: Location, + name: &str, + similar_name: Option, + ) -> Self { + let hint = similar_name.map(|n| { + switch_lang!( + "japanese" => format!("似た名前の変数があります: {n}"), + "simplified_chinese" => format!("存在相同名称变量:{n}"), + "traditional_chinese" => format!("存在相同名稱變量:{n}"), + "english" => format!("exists a similar name variable: {n}"), + ) + .into() + }); + Self::new(ErrorCore::new( + errno, + NameError, + loc, + switch_lang!( + "japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"), + "simplified_chinese" => format!("{RED}{name}{RESET}未定义"), + "traditional_chinese" => format!("{RED}{name}{RESET}未定義"), + "english" => format!("{RED}{name}{RESET} is not defined"), + ), + hint, + )) + } } pub type LexResult = Result; diff --git a/compiler/erg_parser/lib.rs b/compiler/erg_parser/lib.rs index 0a1777b4..e2fcd877 100644 --- a/compiler/erg_parser/lib.rs +++ b/compiler/erg_parser/lib.rs @@ -4,10 +4,12 @@ extern crate erg_common; pub mod ast; +pub mod builder; pub mod desugar; pub mod error; pub mod lex; pub mod parse; +pub mod reorder; pub mod token; pub use parse::{Parser, ParserRunner}; diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 08cea8f8..5d455dea 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -12,13 +12,11 @@ use erg_common::option_enum_unwrap; use erg_common::set::Set as HashSet; use erg_common::traits::Runnable; use erg_common::traits::{Locational, Stream}; -use erg_common::Str; use erg_common::{ caused_by, debug_power_assert, enum_unwrap, fn_name, log, set, switch_lang, switch_unreachable, }; use crate::ast::*; -use crate::desugar::Desugarer; use crate::error::{ParseError, ParseErrors, ParseResult, ParserRunnerError, ParserRunnerErrors}; use crate::lex::Lexer; use crate::token::{Token, TokenCategory, TokenKind, TokenStream}; @@ -201,13 +199,13 @@ impl Runnable for ParserRunner { } impl ParserRunner { - pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result { + pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result { Parser::new(ts) - .parse(Str::ever(self.cfg.module)) + .parse() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) } - pub fn parse(&mut self) -> Result { + pub fn parse(&mut self) -> Result { let ts = Lexer::new(self.input().clone()) .lex() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; @@ -215,7 +213,7 @@ impl ParserRunner { } /// Parses with default configuration - pub fn parse_with_default_config(input: Input) -> Result { + pub fn parse_with_default_config(input: Input) -> Result { let cfg = ErgConfig { input, ..Default::default() @@ -224,20 +222,20 @@ impl ParserRunner { self_.parse() } - fn parse_with_input(&mut self, src: String) -> Result { + pub fn parse_with_input(&mut self, src: String) -> Result { let ts = Lexer::new(Input::Str(src)) .lex() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; Parser::new(ts) - .parse(Str::ever(self.cfg.module)) + .parse() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) } } impl Parser { - pub fn parse(&mut self, mod_name: Str) -> Result { + pub fn parse(&mut self) -> Result { if self.tokens.is_empty() { - return Ok(AST::new(mod_name, Module::empty())); + return Ok(Module::empty()); } log!(info "the parsing process has started."); log!(info "token stream: {}", self.tokens); @@ -255,13 +253,8 @@ impl Parser { } log!(info "the parsing process has completed."); log!(info "AST:\n{module}"); - log!(info "the desugaring process has started."); - let mut desugarer = Desugarer::new(); - let module = desugarer.desugar(module); - log!(info "AST (desugared):\n{module}"); - log!(info "the desugaring process has completed.{RESET}"); if self.errs.is_empty() { - Ok(AST::new(mod_name, module)) + Ok(module) } else { Err(mem::take(&mut self.errs)) } diff --git a/compiler/erg_compiler/reorder.rs b/compiler/erg_parser/reorder.rs similarity index 77% rename from compiler/erg_compiler/reorder.rs rename to compiler/erg_parser/reorder.rs index 6a66faf8..fb573b09 100644 --- a/compiler/erg_compiler/reorder.rs +++ b/compiler/erg_parser/reorder.rs @@ -3,9 +3,9 @@ use erg_common::log; use erg_common::traits::{Locational, Stream}; use erg_common::Str; -use erg_parser::ast::{ClassDef, Expr, Module, PreDeclTypeSpec, TypeSpec, AST}; +use crate::ast::{ClassDef, Expr, Module, PreDeclTypeSpec, TypeSpec, AST}; -use crate::error::{TyCheckError, TyCheckErrors}; +use crate::error::{ParseError, ParseErrors}; /// Combine method definitions across multiple modules, specialized class contexts, etc. #[derive(Debug, Default)] @@ -13,7 +13,7 @@ pub struct Reorderer { // TODO: inner scope types pub def_root_pos_map: Dict, pub deps: Dict>, - pub errs: TyCheckErrors, + pub errs: ParseErrors, } impl Reorderer { @@ -21,12 +21,12 @@ impl Reorderer { Self { def_root_pos_map: Dict::new(), deps: Dict::new(), - errs: TyCheckErrors::empty(), + errs: ParseErrors::empty(), } } - pub fn link(mut self, mut ast: AST) -> Result { - log!(info "the linking process has started."); + pub fn reorder(mut self, mut ast: AST) -> Result { + log!(info "the reordering process has started."); let mut new = vec![]; while let Some(chunk) = ast.module.lpop() { match chunk { @@ -63,17 +63,15 @@ impl Reorderer { class_def.methods_list.push(methods); new.insert(*pos, Expr::ClassDef(class_def)); } else { - let similar_name = Str::from( - self.def_root_pos_map - .keys() - .fold("".to_string(), |acc, key| acc + &key[..] + ","), - ); - self.errs.push(TyCheckError::no_var_error( + let similar_name = self + .def_root_pos_map + .keys() + .fold("".to_string(), |acc, key| acc + &key[..] + ","); + self.errs.push(ParseError::no_var_error( line!() as usize, methods.class.loc(), - "".into(), simple.name.inspect(), - Some(&similar_name), + Some(similar_name), )); } } @@ -85,7 +83,7 @@ impl Reorderer { } } let ast = AST::new(ast.name, Module::new(new)); - log!(info "the linking process has completed:\n{}", ast); + log!(info "the reordering process has completed:\n{}", ast); if self.errs.is_empty() { Ok(ast) } else {