forked from Orchid/orchid
91 lines
3.1 KiB
Rust
91 lines
3.1 KiB
Rust
use std::ffi::OsStr;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use futures::FutureExt;
|
|
use itertools::Itertools;
|
|
use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_err, os_str_to_string};
|
|
use orchid_base::location::SrcRange;
|
|
use orchid_base::name::Sym;
|
|
use orchid_base::parse::Snippet;
|
|
use orchid_host::ctx::Ctx;
|
|
use orchid_host::lex::lex;
|
|
use orchid_host::parse::{HostParseCtxImpl, parse_items};
|
|
use orchid_host::parsed::ParsedModule;
|
|
use orchid_host::tree::Root;
|
|
use substack::Substack;
|
|
use tokio::fs::{self, File};
|
|
use tokio::io::AsyncReadExt;
|
|
|
|
pub async fn parse_folder(
|
|
root: &Root,
|
|
path: PathBuf,
|
|
ns: Sym,
|
|
rep: &Reporter,
|
|
ctx: Ctx,
|
|
) -> OrcRes<Root> {
|
|
let parsed_module = (recur(&path, ns.clone(), rep, ctx).await?)
|
|
.expect("Project folder is a single non-orchid file");
|
|
return Ok(root.add_parsed(&parsed_module, ns, rep).await);
|
|
async fn recur(path: &Path, ns: Sym, rep: &Reporter, ctx: Ctx) -> OrcRes<Option<ParsedModule>> {
|
|
let sr = SrcRange::new(0..0, &ns);
|
|
if path.is_dir() {
|
|
let Some(name_os) = path.file_name() else {
|
|
return Err(mk_err(
|
|
ctx.i.i("Could not read directory name").await,
|
|
format!("Path {} ends in ..", path.to_string_lossy()),
|
|
[sr],
|
|
));
|
|
};
|
|
let name = ctx.i.i(os_str_to_string(name_os, &ctx.i, [sr]).await?).await;
|
|
let ns = ns.suffix([name.clone()], &ctx.i).await;
|
|
let sr = SrcRange::new(0..0, &ns);
|
|
let mut items = Vec::new();
|
|
let mut stream = match fs::read_dir(path).await {
|
|
Err(err) => return Err(async_io_err(err, &ctx.i, [sr]).await),
|
|
Ok(s) => s,
|
|
};
|
|
loop {
|
|
let entry = match stream.next_entry().await {
|
|
Ok(Some(ent)) => ent,
|
|
Ok(None) => break,
|
|
Err(err) => {
|
|
rep.report(async_io_err(err, &ctx.i, [sr.clone()]).await);
|
|
continue;
|
|
},
|
|
};
|
|
match recur(&entry.path(), ns.clone(), rep, ctx.clone()).boxed_local().await {
|
|
Err(e) => {
|
|
rep.report(e);
|
|
continue;
|
|
},
|
|
Ok(None) => continue,
|
|
Ok(Some(module)) => items.push(module.default_item(name.clone(), sr.clone())),
|
|
}
|
|
}
|
|
Ok(Some(ParsedModule::new(false, items)))
|
|
} else if path.extension() == Some(OsStr::new("orc")) {
|
|
let name_os = path.file_stem().expect("If there is an extension, there must be a stem");
|
|
let name = ctx.i.i(os_str_to_string(name_os, &ctx.i, [sr]).await?).await;
|
|
let ns = ns.suffix([name], &ctx.i).await;
|
|
let sr = SrcRange::new(0..0, &ns);
|
|
let mut file = match File::open(path).await {
|
|
Err(e) => return Err(async_io_err(e, &ctx.i, [sr]).await),
|
|
Ok(file) => file,
|
|
};
|
|
let mut text = String::new();
|
|
if let Err(e) = file.read_to_string(&mut text).await {
|
|
return Err(async_io_err(e, &ctx.i, [sr]).await);
|
|
}
|
|
let systems =
|
|
ctx.systems.read().await.iter().filter_map(|(_, sys)| sys.upgrade()).collect_vec();
|
|
let lexemes = lex(ctx.i.i(&text).await, ns.clone(), &systems, &ctx).await?;
|
|
let hpctx = HostParseCtxImpl { ctx: ctx.clone(), rep, src: ns.clone(), systems: &systems };
|
|
let Some(fst) = lexemes.first() else { return Ok(Some(ParsedModule::new(false, []))) };
|
|
let items = parse_items(&hpctx, Substack::Bottom, Snippet::new(fst, &lexemes)).await?;
|
|
Ok(Some(ParsedModule::new(false, items)))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|