Add builder & linker

This commit is contained in:
Shunsuke Shibayama 2022-09-21 01:21:17 +09:00
parent 671fbee518
commit 201b313cd2
19 changed files with 241 additions and 109 deletions

View file

@ -0,0 +1,57 @@
use erg_common::config::{ErgConfig, Input};
use erg_common::traits::Stream;
use erg_parser::ast::VarName;
use erg_parser::builder::ASTBuilder;
use crate::error::{CompileError, CompileErrors, TyCheckErrors};
// use crate::hir::HIR;
use crate::check::Checker;
use crate::mod_cache::SharedModuleCache;
#[derive(Debug)]
pub struct HIRBuilder {
checker: Checker,
mod_cache: SharedModuleCache,
}
impl HIRBuilder {
fn convert(&self, errs: TyCheckErrors) -> CompileErrors {
errs.into_iter()
.map(|e| CompileError::new(e.core, self.checker.cfg.input.clone(), e.caused_by))
.collect::<Vec<_>>()
.into()
}
pub fn new(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
checker: Checker::new(cfg, mod_cache.clone()),
mod_cache,
}
}
pub fn build_and_cache(&mut self, var_name: VarName) -> Result<(), CompileErrors> {
let mut ast_builder = ASTBuilder::new(self.checker.cfg.copy());
let ast = ast_builder.build()?;
let (hir, ctx) = self
.checker
.check(ast, "exec")
.map_err(|errs| self.convert(errs))?;
self.mod_cache.register(var_name, Some(hir), ctx);
Ok(())
}
pub fn build_and_cache_main(&mut self, src: String, mode: &str) -> Result<(), CompileErrors> {
let mut cfg = self.checker.cfg.copy();
cfg.input = Input::Str(src);
let mut ast_builder = ASTBuilder::new(cfg);
let ast = ast_builder.build()?;
let (hir, ctx) = self
.checker
.check(ast, mode)
.map_err(|errs| self.convert(errs))?;
let name = VarName::from_static("<module>");
self.mod_cache.register(name, Some(hir), ctx);
Ok(())
}
}

View file

@ -5,36 +5,61 @@ use erg_common::traits::Runnable;
use erg_parser::ast::AST;
use erg_parser::builder::ASTBuilder;
use crate::context::Context;
use crate::effectcheck::SideEffectChecker;
use crate::error::{TyCheckError, TyCheckErrors};
use crate::hir::HIR;
use crate::lower::ASTLowerer;
use crate::mod_cache::SharedModuleCache;
use crate::ownercheck::OwnershipChecker;
/// Summarize lowering, side-effect checking, and ownership checking
#[derive(Debug)]
pub struct Checker {
cfg: ErgConfig,
pub cfg: ErgConfig,
lowerer: ASTLowerer,
ownership_checker: OwnershipChecker,
}
impl Runnable for Checker {
impl Checker {
pub fn new(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
cfg,
lowerer: ASTLowerer::new(mod_cache),
ownership_checker: OwnershipChecker::new(),
}
}
pub fn check(&mut self, ast: AST, mode: &str) -> Result<(HIR, Context), TyCheckErrors> {
let (hir, ctx, 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, ctx))
}
}
pub struct CheckerRunner {
checker: Checker,
}
impl Runnable for CheckerRunner {
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,
checker: Checker::new(cfg, SharedModuleCache::new()),
}
}
#[inline]
fn cfg(&self) -> &ErgConfig {
&self.cfg
&self.checker.cfg
}
#[inline]
@ -43,30 +68,17 @@ impl Runnable for Checker {
fn clear(&mut self) {}
fn exec(&mut self) -> Result<(), Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg.copy());
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build()?;
let hir = self.check(ast, "exec")?;
let (hir, _) = self.checker.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")?;
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build_with_str(src)?;
let (hir, _) = self.checker.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

@ -3,16 +3,16 @@
//! コンパイラーを定義する
use std::path::Path;
use erg_common::config::{ErgConfig, Input};
use erg_common::config::ErgConfig;
use erg_common::log;
use erg_common::traits::{Runnable, Stream};
use erg_type::codeobj::CodeObj;
use erg_parser::builder::ASTBuilder;
use crate::checker::Checker;
use crate::builder::HIRBuilder;
use crate::codegen::CodeGenerator;
use crate::error::{CompileError, CompileErrors, TyCheckErrors};
use crate::error::{CompileError, CompileErrors};
use crate::link::Linker;
use crate::mod_cache::SharedModuleCache;
/// * registered as global -> Global
/// * defined in the toplevel scope (and called in the inner scope) -> Global
@ -91,7 +91,7 @@ impl AccessKind {
#[derive(Debug)]
pub struct Compiler {
cfg: ErgConfig,
checker: Checker,
mod_cache: SharedModuleCache,
code_generator: CodeGenerator,
}
@ -101,9 +101,10 @@ impl Runnable for Compiler {
const NAME: &'static str = "Erg compiler";
fn new(cfg: ErgConfig) -> Self {
let mod_cache = SharedModuleCache::new();
Self {
checker: Checker::new(cfg.copy()),
code_generator: CodeGenerator::new(cfg.copy()),
mod_cache,
cfg,
}
}
@ -133,13 +134,6 @@ impl Runnable for Compiler {
}
impl Compiler {
fn convert(&self, errs: TyCheckErrors) -> CompileErrors {
errs.into_iter()
.map(|e| CompileError::new(e.core, self.input().clone(), e.caused_by))
.collect::<Vec<_>>()
.into()
}
pub fn compile_and_dump_as_pyc<P: AsRef<Path>>(
&mut self,
src: String,
@ -154,14 +148,9 @@ impl Compiler {
pub fn compile(&mut self, src: String, mode: &str) -> Result<CodeObj, CompileErrors> {
log!(info "the compiling process has started.");
let mut cfg = self.cfg.copy();
cfg.input = Input::Str(src);
let mut ast_builder = ASTBuilder::new(cfg);
let ast = ast_builder.build()?;
let hir = self
.checker
.check(ast, mode)
.map_err(|errs| self.convert(errs))?;
let mut hir_builder = HIRBuilder::new(self.cfg.copy(), self.mod_cache.clone());
hir_builder.build_and_cache_main(src, mode)?;
let hir = Linker::link(self.mod_cache.clone());
let codeobj = self.code_generator.emit(hir);
log!(info "code object:\n{}", codeobj.code_info());
log!(

View file

@ -1783,13 +1783,13 @@ impl Context {
ctx
}
pub fn new_main_module() -> Self {
pub fn new_main_module(mod_cache: SharedModuleCache) -> Self {
Context::new(
"<module>".into(),
ContextKind::Module,
vec![],
Some(Context::init_builtins()),
Some(SharedModuleCache::new()),
Some(mod_cache),
Context::TOP_LEVEL,
)
}

View file

@ -238,6 +238,7 @@ impl From<DefKind> for ContextKind {
DefKind::Class | DefKind::Inherit => Self::Class,
DefKind::Trait | DefKind::Subsume => Self::Trait,
DefKind::StructuralTrait => Self::StructuralTrait,
DefKind::Module => Self::Module,
DefKind::Other => Self::Instant,
}
}

View file

@ -1,5 +1,7 @@
use std::option::Option; // conflicting to Type::Option
use erg_common::config::{ErgConfig, Input};
use erg_common::error::MultiErrorDisplay;
use erg_common::traits::{Locational, Stream};
use erg_common::vis::Visibility;
use erg_common::Str;
@ -14,11 +16,12 @@ use erg_type::value::{GenTypeObj, TypeKind, TypeObj, ValueObj};
use erg_type::{HasType, ParamTy, SubrType, TyBound, Type};
use Type::*;
use crate::builder::HIRBuilder;
use crate::context::{ClassDefType, Context, DefaultInfo, RegistrationMode, TraitInstance};
use crate::error::readable_name;
use crate::error::{TyCheckError, TyCheckResult};
use crate::hir;
use crate::mod_cache::ModuleEntry;
use crate::mod_cache::SharedModuleCache;
use crate::varinfo::{Mutability, ParamIdx, VarInfo, VarKind};
use Mutability::*;
use RegistrationMode::*;
@ -763,56 +766,55 @@ impl Context {
match mod_name {
hir::Expr::Lit(lit) => {
if self.subtype_of(&lit.value.class(), &Str) {
let name = enum_unwrap!(lit.value.clone(), ValueObj::Str);
if let Some(mod_cache) = self.mod_cache.as_mut() {
match &name[..] {
let __name__ = enum_unwrap!(lit.value.clone(), ValueObj::Str);
if let Some(mod_cache) = self.mod_cache.as_ref() {
match &__name__[..] {
"importlib" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_importlib_mod()),
None,
Self::init_py_importlib_mod(),
);
}
"io" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_io_mod()),
);
mod_cache.register(var_name.clone(), None, Self::init_py_io_mod());
}
"math" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_math_mod()),
None,
Self::init_py_math_mod(),
);
}
"random" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_random_mod()),
None,
Self::init_py_random_mod(),
);
}
"socket" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_socket_mod()),
None,
Self::init_py_socket_mod(),
);
}
"sys" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_sys_mod()),
);
mod_cache.register(var_name.clone(), None, Self::init_py_sys_mod());
}
"time" => {
mod_cache.register(
var_name.clone(),
ModuleEntry::builtin(Self::init_py_time_mod()),
None,
Self::init_py_time_mod(),
);
}
other => todo!("importing {other}"),
_ => self.import_user_module(var_name, __name__, mod_cache),
}
} else {
// maybe unreachable
todo!("importing {name} in the builtin module")
todo!("importing {__name__} in the builtin module")
}
} else {
return Err(TyCheckError::type_mismatch_error(
@ -839,6 +841,17 @@ impl Context {
Ok(())
}
fn import_user_module(&self, var_name: &VarName, __name__: Str, mod_cache: &SharedModuleCache) {
let cfg = ErgConfig {
input: Input::File(format!("{__name__}.er")),
..ErgConfig::default()
};
let mut hir_builder = HIRBuilder::new(cfg, mod_cache.clone());
if let Err(errs) = hir_builder.build_and_cache(var_name.clone()) {
errs.fmt_all_stderr();
}
}
pub(crate) fn _push_subtype_bound(&mut self, sub: Type, sup: Type) {
self.bounds.push(TyBound::subtype_of(sub, sup));
}

View file

@ -1332,6 +1332,7 @@ impl Def {
DefKind::Other
}
}
Some("import") => DefKind::Module,
_ => DefKind::Other,
},
_ => DefKind::Other,

View file

@ -3,7 +3,8 @@
extern crate erg_common;
pub extern crate erg_parser;
mod checker;
mod builder;
mod check;
mod compile;
pub use compile::*;
mod codegen;
@ -11,8 +12,10 @@ pub mod context;
pub mod effectcheck;
pub mod error;
pub mod hir;
pub mod link;
pub mod lower;
pub mod mod_cache;
// pub mod name_resolve;
pub mod optimize;
pub mod ownercheck;
pub mod varinfo;

View file

@ -0,0 +1,19 @@
use erg_common::traits::Stream;
use crate::hir::{Expr, HIR};
use crate::mod_cache::SharedModuleCache;
pub struct Linker {}
impl Linker {
pub fn link(mod_cache: SharedModuleCache) -> HIR {
let mut main_mod_hir = mod_cache.remove("<module>").unwrap().hir.unwrap();
for chunk in main_mod_hir.module.iter_mut() {
match chunk {
Expr::Def(def) if def.def_kind().is_module() => {}
_ => {}
}
}
main_mod_hir
}
}

View file

@ -27,6 +27,7 @@ use crate::error::{
};
use crate::hir;
use crate::hir::HIR;
use crate::mod_cache::SharedModuleCache;
use crate::varinfo::VarKind;
use Visibility::*;
@ -48,7 +49,7 @@ impl Runnable for ASTLowererRunner {
fn new(cfg: ErgConfig) -> Self {
Self {
cfg,
lowerer: ASTLowerer::new(),
lowerer: ASTLowerer::new(SharedModuleCache::new()),
}
}
@ -63,7 +64,7 @@ impl Runnable for ASTLowererRunner {
fn exec(&mut self) -> Result<(), Self::Errs> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build()?;
let (hir, warns) = self
let (hir, _, warns) = self
.lowerer
.lower(ast, "exec")
.map_err(|errs| self.convert(errs))?;
@ -77,8 +78,8 @@ impl Runnable for ASTLowererRunner {
fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build_with_input(src)?;
let (hir, _) = self
let ast = ast_builder.build_with_str(src)?;
let (hir, ..) = self
.lowerer
.lower(ast, "eval")
.map_err(|errs| self.convert(errs))?;
@ -105,14 +106,14 @@ pub struct ASTLowerer {
impl Default for ASTLowerer {
fn default() -> Self {
Self::new()
Self::new(SharedModuleCache::new())
}
}
impl ASTLowerer {
pub fn new() -> Self {
pub fn new(mod_cache: SharedModuleCache) -> Self {
Self {
ctx: Context::new_main_module(),
ctx: Context::new_main_module(mod_cache),
errs: LowerErrors::empty(),
warns: LowerWarnings::empty(),
}
@ -1008,7 +1009,11 @@ impl ASTLowerer {
Ok(hir::Block::new(hir_block))
}
pub fn lower(&mut self, ast: AST, mode: &str) -> Result<(HIR, LowerWarnings), LowerErrors> {
pub fn lower(
&mut self,
ast: AST,
mode: &str,
) -> Result<(HIR, Context, LowerWarnings), LowerErrors> {
log!(info "the AST lowering process has started.");
log!(info "the type-checking process has started.");
let mut module = hir::Module::with_capacity(ast.module.len());
@ -1035,7 +1040,11 @@ impl ASTLowerer {
if self.errs.is_empty() {
log!(info "HIR:\n{hir}");
log!(info "the AST lowering process has completed.");
Ok((hir, LowerWarnings::from(self.warns.take_all())))
Ok((
hir,
self.ctx.pop()?,
LowerWarnings::from(self.warns.take_all()),
))
} else {
log!(err "the AST lowering process has failed.");
Err(LowerErrors::from(self.errs.take_all()))

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
pub hir: Option<HIR>,
ctx: Rc<Context>,
}
impl ModuleEntry {
pub fn new(id: ModId, hir: Option<HIR>, ctx: Context) -> Self {
Self {
_id: id,
_hir: hir,
id,
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),
}
}
@ -53,11 +53,15 @@ impl ModuleEntry {
#[derive(Debug, Default)]
pub struct ModuleCache {
cache: Dict<VarName, ModuleEntry>,
last_id: usize,
}
impl ModuleCache {
pub fn new() -> Self {
Self { cache: Dict::new() }
Self {
cache: Dict::new(),
last_id: 0,
}
}
pub fn get<Q: Eq + Hash + ?Sized>(&self, name: &Q) -> Option<&ModuleEntry>
@ -67,7 +71,10 @@ impl ModuleCache {
self.cache.get(name)
}
pub fn register(&mut self, name: VarName, entry: ModuleEntry) {
pub fn register(&mut self, name: VarName, hir: Option<HIR>, ctx: Context) {
self.last_id += 1;
let id = ModId::new(self.last_id);
let entry = ModuleEntry::new(id, hir, ctx);
self.cache.insert(name, entry);
}
@ -77,6 +84,20 @@ impl ModuleCache {
{
self.cache.remove(name)
}
pub fn remove_by_id(&mut self, id: ModId) -> Option<ModuleEntry> {
if let Some(name) = self.cache.iter().find_map(|(name, ent)| {
if ent.id == id {
Some(name.clone())
} else {
None
}
}) {
self.remove(&name)
} else {
None
}
}
}
#[derive(Debug, Clone, Default)]
@ -102,14 +123,18 @@ impl SharedModuleCache {
ref_.get(name).map(|entry| entry.ctx.as_ref())
}
pub fn register(&self, name: VarName, entry: ModuleEntry) {
self.0.borrow_mut().register(name, entry);
pub fn register(&self, name: VarName, hir: Option<HIR>, ctx: Context) {
self.0.borrow_mut().register(name, hir, ctx);
}
pub fn remove<Q: Eq + Hash + ?Sized>(&mut self, name: &Q) -> Option<ModuleEntry>
pub fn remove<Q: Eq + Hash + ?Sized>(&self, name: &Q) -> Option<ModuleEntry>
where
VarName: Borrow<Q>,
{
self.0.borrow_mut().remove(name)
}
pub fn remove_by_id(&self, id: ModId) -> Option<ModuleEntry> {
self.0.borrow_mut().remove_by_id(id)
}
}

View file

@ -0,0 +1,113 @@
use erg_common::dict::Dict;
use erg_common::log;
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use crate::ast::{ClassDef, Expr, Methods, Module, PreDeclTypeSpec, TypeSpec, AST};
use crate::error::{ParseError, ParseErrors};
/// Combine method definitions across multiple modules, specialized class contexts, etc.
#[derive(Debug, Default)]
pub struct Reorderer {
// TODO: inner scope types
pub def_root_pos_map: Dict<Str, usize>,
pub deps: Dict<Str, Vec<Str>>,
pub errs: ParseErrors,
}
impl Reorderer {
pub fn new() -> Self {
Self {
def_root_pos_map: Dict::new(),
deps: Dict::new(),
errs: ParseErrors::empty(),
}
}
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 {
Expr::Def(def) => {
match def.body.block.first().unwrap() {
Expr::Call(call) => {
match call.obj.get_name().map(|s| &s[..]) {
// TODO: decorator
Some("Class" | "Inherit" | "Inheritable") => {
self.def_root_pos_map.insert(
def.sig.ident().unwrap().inspect().clone(),
new.len(),
);
let type_def = ClassDef::new(def, vec![]);
new.push(Expr::ClassDef(type_def));
}
_ => {
new.push(Expr::Def(def));
}
}
}
_ => {
new.push(Expr::Def(def));
}
}
}
Expr::Methods(methods) => match &methods.class {
TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(simple)) => {
self.link_methods(simple.name.inspect().clone(), &mut new, methods)
}
TypeSpec::TypeApp { spec, .. } => {
if let TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(simple)) = spec.as_ref()
{
self.link_methods(simple.name.inspect().clone(), &mut new, methods)
} else {
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(),
&methods.class.to_string(),
Some(similar_name),
));
}
}
other => todo!("{other}"),
},
other => {
new.push(other);
}
}
}
let ast = AST::new(ast.name, Module::new(new));
log!(info "the reordering process has completed:\n{}", ast);
if self.errs.is_empty() {
Ok(ast)
} else {
Err(self.errs)
}
}
fn link_methods(&mut self, name: Str, new: &mut Vec<Expr>, methods: Methods) {
if let Some(pos) = self.def_root_pos_map.get(&name) {
let mut class_def = match new.remove(*pos) {
Expr::ClassDef(class_def) => class_def,
_ => unreachable!(),
};
class_def.methods_list.push(methods);
new.insert(*pos, Expr::ClassDef(class_def));
} else {
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(),
&name,
Some(similar_name),
));
}
}
}

View file

@ -1,15 +1,16 @@
use erg_compiler::context::Context;
use erg_compiler::mod_cache::SharedModuleCache;
#[test]
fn test_subtyping() -> Result<(), ()> {
let context = Context::new_main_module();
let context = Context::new_main_module(SharedModuleCache::new());
context.test_refinement_subtyping()?;
Ok(())
}
#[test]
fn test_instantiation_and_generalization() -> Result<(), ()> {
let context = Context::new_main_module();
let context = Context::new_main_module(SharedModuleCache::new());
context.test_instantiation_and_generalization()?;
Ok(())
}