Skip to content

Commit

Permalink
Merge pull request #479 from erg-lang/ext-pkg
Browse files Browse the repository at this point in the history
Support external package importing
  • Loading branch information
mtshiba authored Jan 22, 2024
2 parents 67fefd1 + c6f84f0 commit 6a6fd76
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 19 deletions.
67 changes: 66 additions & 1 deletion crates/els/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use erg_common::env::erg_path;
use erg_common::pathutil::NormalizedPathBuf;
use erg_common::shared::{MappedRwLockReadGuard, Shared};
use erg_common::spawn::{safe_yield, spawn_new_thread};
use erg_common::{fn_name, normalize_path};
use erg_common::traits::Stream;
use erg_common::vfs::VFS;
use erg_common::{fn_name, lsp_log, normalize_path};

use erg_compiler::artifact::BuildRunnable;
use erg_compiler::build_package::PackageBuilder;
Expand Down Expand Up @@ -233,6 +235,7 @@ impl<C: BuildRunnable, P: Parsable> Clone for Server<C, P> {
impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
pub fn new(cfg: ErgConfig, stdout_redirect: Option<mpsc::Sender<Value>>) -> Self {
let flags = Flags::default();
let cfg = Self::register_packages(cfg);
Self {
comp_cache: CompletionCache::new(cfg.copy(), flags.clone()),
shared: SharedCompilerResource::new(cfg.copy()),
Expand All @@ -253,6 +256,68 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
}
}

fn register_packages(mut cfg: ErgConfig) -> ErgConfig {
use erg_compiler::erg_parser::ast;
let home = normalize_path(std::env::current_dir().unwrap_or_default());
let Ok(src) = VFS.read(home.join("package.lock.er")) else {
lsp_log!("package.lock.er not found");
return cfg;
};
let Ok(artifact) = <erg_compiler::erg_parser::Parser as Parsable>::parse(src) else {
lsp_log!("failed to parse package.lock.er");
return cfg;
};
let Some(pkgs) = artifact.ast.get_attr("packages") else {
lsp_log!("failed to get packages: {}", artifact.ast);
return cfg;
};
let Some(ast::Expr::Array(ast::Array::Normal(arr))) = pkgs.body.block.first() else {
lsp_log!("packages must be an array: {pkgs}");
return cfg;
};
for rec in arr.iter() {
let ast::Expr::Record(rec) = rec else {
lsp_log!("packages must be records: {rec}");
return cfg;
};
let Some(ast::Expr::Literal(name)) =
rec.get("name").and_then(|name| name.body.block.first())
else {
return cfg;
};
let name = name.token.content.replace('\"', "");
let Some(ast::Expr::Literal(as_name)) = rec
.get("as_name")
.and_then(|as_name| as_name.body.block.first())
else {
return cfg;
};
let as_name = as_name.token.content.replace('\"', "");
let Some(ast::Expr::Literal(version)) = rec
.get("version")
.and_then(|version| version.body.block.first())
else {
return cfg;
};
let version = version.token.content.replace('\"', "");
let path = rec.get("path").and_then(|path| {
if let Some(ast::Expr::Literal(lit)) = path.body.block.first() {
Some(lit.token.content.replace('\"', ""))
} else {
None
}
});
let package = erg_common::config::Package::new(
name.leak(),
as_name.leak(),
version.leak(),
path.map(|p| &*p.leak()),
);
cfg.packages.push(package);
}
cfg
}

pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
loop {
let msg = self.read_message()?;
Expand Down
70 changes: 70 additions & 0 deletions crates/erg_common/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ impl From<&str> for TranspileTarget {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Package {
pub name: &'static str,
pub as_name: &'static str,
pub version: &'static str,
pub path: Option<&'static str>,
}

impl Package {
pub const fn new(
name: &'static str,
as_name: &'static str,
version: &'static str,
path: Option<&'static str>,
) -> Self {
Self {
name,
as_name,
version,
path,
}
}
}

#[derive(Debug, Clone)]
pub struct ErgConfig {
pub mode: ErgMode,
Expand Down Expand Up @@ -122,6 +146,7 @@ pub struct ErgConfig {
pub ps1: &'static str,
pub ps2: &'static str,
pub runtime_args: Vec<&'static str>,
pub packages: Vec<Package>,
}

impl Default for ErgConfig {
Expand All @@ -146,6 +171,7 @@ impl Default for ErgConfig {
ps1: ">>> ",
ps2: "... ",
runtime_args: vec![],
packages: vec![],
}
}
}
Expand Down Expand Up @@ -267,6 +293,50 @@ impl ErgConfig {
process::exit(1);
});
}
"--use-package" => {
let name = args
.next()
.expect("`name` of `--use-package` is not passed")
.into_boxed_str();
let as_name = args
.next()
.expect("`as_name` of `--use-package` is not passed")
.into_boxed_str();
let version = args
.next()
.expect("`version` of `--use-package` is not passed")
.into_boxed_str();
cfg.packages.push(Package::new(
Box::leak(name),
Box::leak(as_name),
Box::leak(version),
None,
));
}
"--use-local-package" => {
let name = args
.next()
.expect("`name` of `--use-package` is not passed")
.into_boxed_str();
let as_name = args
.next()
.expect("`as_name` of `--use-package` is not passed")
.into_boxed_str();
let version = args
.next()
.expect("`version` of `--use-package` is not passed")
.into_boxed_str();
let path = args
.next()
.expect("`path` of `--use-package` is not passed")
.into_boxed_str();
cfg.packages.push(Package::new(
Box::leak(name),
Box::leak(as_name),
Box::leak(version),
Some(Box::leak(path)),
));
}
"--ping" => {
println!("pong");
process::exit(0);
Expand Down
16 changes: 16 additions & 0 deletions crates/erg_common/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ fn _erg_external_lib_path() -> PathBuf {
fallback_erg_path().join("lib/external")
})
}
fn _erg_pkgs_path() -> PathBuf {
_erg_path()
.join("lib")
.join("pkgs")
.canonicalize()
.unwrap_or_else(|_| {
eprintln!("{RED}[ERR] ERG_PATH/lib/pkgs not found {RESET}");
fallback_erg_path().join("lib/pkgs")
})
}
fn _python_site_packages() -> impl Iterator<Item = PathBuf> {
get_sys_path(None)
.unwrap_or_default()
Expand All @@ -91,6 +101,7 @@ pub static ERG_CORE_DECL_PATH: OnceLock<PathBuf> = OnceLock::new();
pub static ERG_STD_PATH: OnceLock<PathBuf> = OnceLock::new();
pub static ERG_PYSTD_PATH: OnceLock<PathBuf> = OnceLock::new();
pub static ERG_EXTERNAL_LIB_PATH: OnceLock<PathBuf> = OnceLock::new();
pub static ERG_PKGS_PATH: OnceLock<PathBuf> = OnceLock::new();
pub static PYTHON_SITE_PACKAGES: OnceLock<Vec<PathBuf>> = OnceLock::new();

/// == `Path::new("~/.erg")` if ERG_PATH is not set
Expand Down Expand Up @@ -123,6 +134,11 @@ pub fn erg_py_external_lib_path() -> &'static PathBuf {
ERG_EXTERNAL_LIB_PATH.get_or_init(|| normalize_path(_erg_external_lib_path()))
}

/// == `Path::new("~/.erg/lib/pkgs")` if ERG_PATH is not set
pub fn erg_pkgs_path() -> &'static PathBuf {
ERG_PKGS_PATH.get_or_init(|| normalize_path(_erg_pkgs_path()))
}

pub fn python_site_packages() -> &'static Vec<PathBuf> {
PYTHON_SITE_PACKAGES.get_or_init(|| _python_site_packages().collect())
}
Expand Down
24 changes: 20 additions & 4 deletions crates/erg_common/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use std::path::{Path, PathBuf};
use std::process;
use std::process::Stdio;

use crate::config::ErgConfig;
use crate::consts::EXPERIMENTAL_MODE;
use crate::env::{
erg_path, erg_py_external_lib_path, erg_pystd_path, erg_std_path, python_site_packages,
erg_path, erg_pkgs_path, erg_py_external_lib_path, erg_pystd_path, erg_std_path,
python_site_packages,
};
use crate::pathutil::{add_postfix_foreach, remove_postfix};
use crate::python_util::get_sys_path;
Expand Down Expand Up @@ -479,8 +481,8 @@ impl Input {
))
}

pub fn resolve_path(&self, path: &Path) -> Option<PathBuf> {
self.resolve_real_path(path)
pub fn resolve_path(&self, path: &Path, cfg: &ErgConfig) -> Option<PathBuf> {
self.resolve_real_path(path, cfg)
.or_else(|| self.resolve_decl_path(path))
}

Expand All @@ -489,7 +491,8 @@ impl Input {
/// 2. `./{path/to}/__init__.er`
/// 3. `std/{path/to}.er`
/// 4. `std/{path/to}/__init__.er`
pub fn resolve_real_path(&self, path: &Path) -> Option<PathBuf> {
/// 5. `pkgs/{path/to}/lib.er`
pub fn resolve_real_path(&self, path: &Path, cfg: &ErgConfig) -> Option<PathBuf> {
if let Ok(path) = self.resolve_local(path) {
Some(path)
} else if let Ok(path) = erg_std_path()
Expand All @@ -503,13 +506,26 @@ impl Input {
.canonicalize()
{
Some(normalize_path(path))
} else if let Some(pkg) = self.resolve_project_dep_path(path, cfg) {
Some(normalize_path(pkg))
} else if path == Path::new("unsound") {
Some(PathBuf::from("unsound"))
} else {
None
}
}

fn resolve_project_dep_path(&self, path: &Path, cfg: &ErgConfig) -> Option<PathBuf> {
let name = format!("{}", path.display());
let pkg = cfg.packages.iter().find(|p| p.as_name == name)?;
let path = if let Some(path) = pkg.path {
PathBuf::from(path).canonicalize().ok()?
} else {
erg_pkgs_path().join(pkg.name).join(pkg.version)
};
Some(path.join("src").join("lib.er"))
}

/// resolution order:
/// 1. `{path/to}.d.er`
/// 2. `{path/to}/__init__.d.er`
Expand Down
7 changes: 7 additions & 0 deletions crates/erg_common/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ impl Str {
Str::Rc(s.into())
}

pub fn leak(self) -> &'static str {
match self {
Str::Rc(s) => Box::leak(s.into()),
Str::Static(s) => s,
}
}

pub fn into_rc(self) -> ArcStr {
match self {
Str::Rc(s) => s,
Expand Down
2 changes: 1 addition & 1 deletion crates/erg_compiler/build_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ impl<ASTBuilder: ASTBuildable, HIRBuilder: Buildable>
return Ok(());
}
let path = Path::new(&__name__[..]);
let import_path = match cfg.input.resolve_path(path) {
let import_path = match cfg.input.resolve_path(path, cfg) {
Some(path) => path,
None => {
for _ in 0..600 {
Expand Down
2 changes: 1 addition & 1 deletion crates/erg_compiler/context/initialize/const_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ pub(crate) fn resolve_path_func(mut args: ValueArgs, ctx: &Context) -> EvalValue
return Err(type_mismatch("Str", other, "Path"));
}
};
let Some(path) = ctx.cfg.input.resolve_path(path) else {
let Some(path) = ctx.cfg.input.resolve_path(path, &ctx.cfg) else {
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("Path {} is not found", path.display()),
Expand Down
2 changes: 1 addition & 1 deletion crates/erg_compiler/context/inquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3086,7 +3086,7 @@ impl Context {
str_namespace.push_str(namespaces.remove(0));
}
let path = Path::new(&str_namespace);
let mut path = self.cfg.input.resolve_path(path)?;
let mut path = self.cfg.input.resolve_path(path, &self.cfg)?;
for p in namespaces.into_iter() {
path = Input::try_push_path(path, Path::new(p)).ok()?;
}
Expand Down
6 changes: 5 additions & 1 deletion crates/erg_compiler/context/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2132,7 +2132,11 @@ impl Context {
}

fn import_erg_mod(&self, __name__: &Str, loc: &impl Locational) -> CompileResult<PathBuf> {
let path = match self.cfg.input.resolve_real_path(Path::new(&__name__[..])) {
let path = match self
.cfg
.input
.resolve_real_path(Path::new(&__name__[..]), &self.cfg)
{
Some(path) => path,
None => {
return Err(self.import_err(line!(), __name__, loc));
Expand Down
17 changes: 17 additions & 0 deletions crates/erg_compiler/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,10 @@ impl RecordAttrs {
pub fn extend(&mut self, attrs: RecordAttrs) {
self.0.extend(attrs.0);
}

pub fn get(&self, name: &str) -> Option<&Def> {
self.0.iter().find(|def| def.sig.ident().inspect() == name)
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -1264,6 +1268,10 @@ impl Record {
t.insert(Field::from(attr.sig.ident()), attr.body.block.t());
self.attrs.push(attr);
}

pub fn get(&self, name: &str) -> Option<&Def> {
self.attrs.get(name)
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -2983,6 +2991,15 @@ impl Locational for Module {

impl_stream!(Module, Expr);

impl Module {
pub fn get_attr(&self, name: &str) -> Option<&Def> {
self.0.iter().find_map(|e| match e {
Expr::Def(def) if def.sig.ident().inspect() == name => Some(def),
_ => None,
})
}
}

/// High-level Intermediate Representation
/// AST with type information added
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
Loading

0 comments on commit 6a6fd76

Please sign in to comment.