Add `ASTBuilder`, `Checker`
This commit is contained in:
Shunsuke Shibayama 2022-09-20 14:42:37 +09:00
parent f12c2ba723
commit d8799f0895
14 changed files with 254 additions and 104 deletions

View file

@ -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<String, TyCheckErrors> {
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<HIR, TyCheckErrors> {
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)
}
}

View file

@ -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());

View file

@ -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);

View file

@ -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<ValueObj> {
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);
}

View file

@ -237,6 +237,15 @@ pub struct TyCheckError {
pub caused_by: AtomicStr,
}
impl From<ParserRunnerError> 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<TyCheckError>);
impl_stream_for_wrapper!(TyCheckErrors, TyCheckError);
impl MultiErrorDisplay<TyCheckError> for TyCheckErrors {}
impl From<Vec<TyCheckError>> for TyCheckErrors {
fn from(errs: Vec<TyCheckError>) -> Self {
@ -1189,6 +1199,12 @@ impl From<TyCheckError> for TyCheckErrors {
}
}
impl From<ParserRunnerErrors> for TyCheckErrors {
fn from(err: ParserRunnerErrors) -> Self {
Self(err.into_iter().map(TyCheckError::from).collect())
}
}
pub type TyCheckResult<T> = Result<T, TyCheckError>;
pub type TyCheckWarning = TyCheckError;
pub type TyCheckWarnings = TyCheckErrors;

View file

@ -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;

View file

@ -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<String, CompileErrors> {
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")

View file

@ -27,24 +27,24 @@ impl ModId {
#[derive(Debug)]
pub struct ModuleEntry {
id: ModId, // builtin == 0, __main__ == 1
hir: Option<HIR>,
_id: ModId, // builtin == 0, __main__ == 1
_hir: Option<HIR>,
ctx: Rc<Context>,
}
impl ModuleEntry {
pub fn new(id: ModId, hir: Option<HIR>, 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),
}
}

View file

@ -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<AST, ParserRunnerErrors> {
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<AST, ParserRunnerErrors> {
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)
}
}

View file

@ -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
}

View file

@ -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<String>,
) -> 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<T> = Result<T, LexError>;

View file

@ -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};

View file

@ -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<AST, ParserRunnerErrors> {
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
Parser::new(ts)
.parse(Str::ever(self.cfg.module))
.parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
}
pub fn parse(&mut self) -> Result<AST, ParserRunnerErrors> {
pub fn parse(&mut self) -> Result<Module, ParserRunnerErrors> {
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<AST, ParserRunnerErrors> {
pub fn parse_with_default_config(input: Input) -> Result<Module, ParserRunnerErrors> {
let cfg = ErgConfig {
input,
..Default::default()
@ -224,20 +222,20 @@ impl ParserRunner {
self_.parse()
}
fn parse_with_input(&mut self, src: String) -> Result<AST, ParserRunnerErrors> {
pub fn parse_with_input(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
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<AST, ParseErrors> {
pub fn parse(&mut self) -> Result<Module, ParseErrors> {
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))
}

View file

@ -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<Str, usize>,
pub deps: Dict<Str, Vec<Str>>,
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<AST, TyCheckErrors> {
log!(info "the linking process has started.");
pub fn reorder(mut self, mut ast: AST) -> Result<AST, ParseErrors> {
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 {