erg_compiler 0.5.5-nightly.0

Centimetre: the Erg compiler
Documentation
use std::mem;
use std::path::PathBuf;

use erg_common::config::{ErgConfig, Input};
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{enum_unwrap, log};

use erg_parser::ast::DefId;
use erg_parser::token::{Token, TokenKind};

use erg_type::value::ValueObj;
use erg_type::Type;

use crate::hir::{
    Accessor, Args, Block, Call, Def, DefBody, Expr, Identifier, Literal, PosArg, HIR,
};
use crate::mod_cache::SharedModuleCache;

pub struct Linker {}

impl Linker {
    #[allow(deprecated)]
    pub fn link(cfg: ErgConfig, mut main: HIR, mod_cache: SharedModuleCache) -> HIR {
        log!(info "the linking process has started.");
        for chunk in main.module.iter_mut() {
            match chunk {
                // x = import "mod"
                //                // x = ModuleType("mod")
                // exec(code, x.__dict__) # `code` is the mod's content
                Expr::Def(ref def) if def.def_kind().is_erg_import() => {
                    // In the case of REPL, entries cannot be used up
                    let hir = if cfg.input.is_repl() {
                        mod_cache
                            .get(&def.sig.ident().inspect()[..])
                            .and_then(|entry| entry.hir.clone())
                    } else {
                        mod_cache
                            .remove(&def.sig.ident().inspect()[..])
                            .and_then(|entry| entry.hir)
                    };
                    let mod_name = enum_unwrap!(def.body.block.first().unwrap(), Expr::Call)
                        .args
                        .get_left_or_key("path")
                        .unwrap();
                    // let sig = option_enum_unwrap!(&def.sig, Signature::Var)
                    //    .unwrap_or_else(|| todo!("module subroutines are not allowed"));
                    if let Some(hir) = hir {
                        let code = Expr::Code(Block::new(Vec::from(hir.module)));
                        let module_type = Expr::Accessor(Accessor::private_with_line(
                            Str::ever("#ModuleType"),
                            def.ln_begin().unwrap_or(0),
                        ));
                        let args =
                            Args::new(vec![PosArg::new(mod_name.clone())], None, vec![], None);
                        let block = Block::new(vec![Expr::Call(Call::new(
                            module_type,
                            None,
                            args,
                            Type::Uninited,
                        ))]);
                        let mod_def = Expr::Def(Def::new(
                            def.sig.clone(),
                            DefBody::new(Token::dummy(), block, DefId(0)),
                        ));
                        let exec = Expr::Accessor(Accessor::public_with_line(
                            Str::ever("exec"),
                            mod_def.ln_begin().unwrap_or(0),
                        ));
                        let module = Expr::Accessor(Accessor::Ident(def.sig.ident().clone()));
                        let __dict__ = Identifier::public("__dict__");
                        let m_dict =
                            Expr::Accessor(Accessor::attr(module, __dict__, Type::Uninited));
                        let args = Args::new(
                            vec![PosArg::new(code), PosArg::new(m_dict)],
                            None,
                            vec![],
                            None,
                        );
                        let exec_code = Expr::Call(Call::new(exec, None, args, Type::Uninited));
                        let compound = Block::new(vec![mod_def, exec_code]);
                        *chunk = Expr::Compound(compound);
                    }
                }
                // x = pyimport "x" (called from dir "a")
                //                // x = __import__("a.x").x
                Expr::Def(def) if def.def_kind().is_py_import() => {
                    let mut dir = if let Input::File(mut path) = cfg.input.clone() {
                        path.pop();
                        path
                    } else {
                        PathBuf::new()
                    };
                    let args =
                        &mut enum_unwrap!(def.body.block.first_mut().unwrap(), Expr::Call).args;
                    let mod_name_lit =
                        enum_unwrap!(args.remove_left_or_key("path").unwrap(), Expr::Lit);
                    let mod_name_str = enum_unwrap!(mod_name_lit.value.clone(), ValueObj::Str);
                    let mod_name_str = if let Some(stripped) = mod_name_str.strip_prefix("./") {
                        stripped
                    } else {
                        &mod_name_str
                    };
                    dir.push(mod_name_str);
                    let mut comps = dir.components();
                    let _first = comps.next().unwrap();
                    let path = dir.to_string_lossy().replace('/', ".").replace('\\', ".");
                    let token = Token::new(
                        TokenKind::StrLit,
                        path,
                        mod_name_lit.ln_begin().unwrap(),
                        mod_name_lit.col_begin().unwrap(),
                    );
                    let mod_name = Expr::Lit(Literal::from(token));
                    args.insert_pos(0, PosArg::new(mod_name));
                    let expr = def.body.block.first_mut().unwrap();
                    let line = expr.ln_begin().unwrap_or(0);
                    for attr in comps {
                        *expr = Expr::Accessor(Accessor::attr(
                            // instead of mem::take(),
                            mem::replace(expr, Expr::Code(Block::empty())),
                            Identifier::public_with_line(
                                Token::dummy(),
                                Str::rc(attr.as_os_str().to_str().unwrap()),
                                line,
                            ),
                            Type::Uninited,
                        ));
                    }
                }
                _ => {}
            }
        }
        log!(info "linked: {main}");
        main
    }
}