From 19f2c6426afc71149b3d1ec25c087e7357a5121c Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Fri, 18 Jul 2025 16:29:52 +0200 Subject: [PATCH] Returned from Italy --- orchid-api/src/lib.rs | 2 - orchid-api/src/proto.rs | 3 +- orchid-api/src/system.rs | 3 +- orchid-api/src/vfs.rs | 32 ---------- orchid-base/src/error.rs | 24 ++++++++ orchid-extension/src/entrypoint.rs | 18 +----- orchid-extension/src/fs.rs | 88 ---------------------------- orchid-extension/src/lib.rs | 1 - orchid-extension/src/system.rs | 4 -- orchid-host/src/fs.rs | 93 ------------------------------ orchid-host/src/lib.rs | 1 - orchid-host/src/parse.rs | 4 +- orchid-host/src/parsed.rs | 4 ++ orchid-host/src/system.rs | 7 --- orchid-std/src/std/std_system.rs | 2 - orcx/src/main.rs | 30 ++++++++-- orcx/src/parse_folder.rs | 91 +++++++++++++++++++++++++++++ 17 files changed, 149 insertions(+), 258 deletions(-) delete mode 100644 orchid-api/src/vfs.rs delete mode 100644 orchid-extension/src/fs.rs delete mode 100644 orchid-host/src/fs.rs create mode 100644 orcx/src/parse_folder.rs diff --git a/orchid-api/src/lib.rs b/orchid-api/src/lib.rs index 8041110..fae9832 100644 --- a/orchid-api/src/lib.rs +++ b/orchid-api/src/lib.rs @@ -22,5 +22,3 @@ mod system; pub use system::*; mod tree; pub use tree::*; -mod vfs; -pub use vfs::*; diff --git a/orchid-api/src/proto.rs b/orchid-api/src/proto.rs index 12b6fc3..c58fe14 100644 --- a/orchid-api/src/proto.rs +++ b/orchid-api/src/proto.rs @@ -28,7 +28,7 @@ use async_std::io::{Read, Write}; use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact, write_exact}; -use crate::{atom, expr, interner, lexer, logging, parser, system, tree, vfs}; +use crate::{atom, expr, interner, lexer, logging, parser, system, tree}; static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n"; pub struct HostHeader { @@ -120,7 +120,6 @@ pub enum HostExtReq { ParseLine(parser::ParseLine), FetchParsedConst(parser::FetchParsedConst), GetMember(tree::GetMember), - VfsRead(vfs::VfsRead), } /// Notifications sent from the host to the extension diff --git a/orchid-api/src/system.rs b/orchid-api/src/system.rs index 4db9666..81ec932 100644 --- a/orchid-api/src/system.rs +++ b/orchid-api/src/system.rs @@ -5,7 +5,7 @@ use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; use ordered_float::NotNan; -use crate::{CharFilter, EagerVfs, ExtHostReq, HostExtNotif, HostExtReq, MemberKind, TStr}; +use crate::{CharFilter, ExtHostReq, HostExtNotif, HostExtReq, MemberKind, TStr}; /// ID of a system type #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] @@ -63,7 +63,6 @@ pub struct NewSystemResponse { pub lex_filter: CharFilter, pub line_types: Vec, pub const_root: HashMap, - pub vfs: HashMap, } #[derive(Clone, Debug, Coding, Hierarchy)] diff --git a/orchid-api/src/vfs.rs b/orchid-api/src/vfs.rs deleted file mode 100644 index fe24db2..0000000 --- a/orchid-api/src/vfs.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::collections::HashMap; -use std::num::NonZeroU16; - -use orchid_api_derive::{Coding, Hierarchy}; -use orchid_api_traits::Request; - -use crate::error::OrcResult; -use crate::interner::TStr; -use crate::proto::HostExtReq; -use crate::system::SysId; - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] -pub struct VfsId(pub NonZeroU16); - -#[derive(Clone, Debug, Coding)] -pub enum Loaded { - Code(String), - Collection(Vec), -} - -#[derive(Clone, Debug, Coding, Hierarchy)] -#[extends(HostExtReq)] -pub struct VfsRead(pub SysId, pub VfsId, pub Vec); -impl Request for VfsRead { - type Response = OrcResult; -} - -#[derive(Clone, Debug, Coding)] -pub enum EagerVfs { - Lazy(VfsId), - Eager(HashMap), -} diff --git a/orchid-base/src/error.rs b/orchid-base/src/error.rs index 42974c5..0252ecd 100644 --- a/orchid-base/src/error.rs +++ b/orchid-base/src/error.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::ffi::OsStr; use std::fmt; use std::ops::Add; use std::sync::Arc; @@ -177,6 +178,29 @@ pub fn mk_errv>( mk_err(description, message, posv.into_iter().map_into()).into() } +pub async fn async_io_err>( + err: async_std::io::Error, + i: &Interner, + posv: impl IntoIterator, +) -> OrcErrv { + mk_errv(i.i(&err.kind().to_string()).await, err.to_string(), posv) +} + +pub async fn os_str_to_string<'a, I: Into>( + str: &'a OsStr, + i: &Interner, + posv: impl IntoIterator, +) -> OrcRes<&'a str> { + match str.to_str() { + Some(str) => Ok(str), + None => Err(mk_errv( + i.i("Non-unicode string").await, + format!("{str:?} is not representable as unicode"), + posv, + )), + } +} + pub struct Reporter { errors: RefCell>, } diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 86eb1f2..d30ea0c 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -30,7 +30,6 @@ use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId}; use crate::atom_owned::take_atom; use crate::expr::{Expr, ExprHandle}; -use crate::fs::VirtFS; use crate::lexer::{LexContext, err_cascade, err_not_applicable}; use crate::parser::{ParsCtx, get_const, linev_into_api}; use crate::system::{SysCtx, atom_by_idx}; @@ -56,7 +55,6 @@ pub enum MemberRecord { } pub struct SystemRecord { - vfses: HashMap, lazy_members: HashMap, ctx: SysCtx, } @@ -169,7 +167,6 @@ pub fn extension_init( let (sys_id, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system)) .expect("NewSystem call received for invalid system"); let cted = data.systems[sys_id].new_system(&new_sys); - let mut vfses = HashMap::new(); let lex_filter = cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| { char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned())) @@ -192,12 +189,10 @@ pub fn extension_init( }) .collect() .await; - let vfs = cted.inst().dyn_vfs().to_api_rec(&mut vfses, &i).await; - let record = SystemRecord { vfses, ctx, lazy_members: lazy_mems.into_inner() }; + let record = SystemRecord { ctx, lazy_members: lazy_mems.into_inner() }; let systems = systems_weak.upgrade().expect("System constructed during shutdown"); systems.lock().await.insert(new_sys.id, record); - let response = - api::NewSystemResponse { lex_filter, const_root, line_types: vec![], vfs }; + let response = api::NewSystemResponse { lex_filter, const_root, line_types: vec![] }; hand.handle(&new_sys, &response).await }, api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => { @@ -227,15 +222,6 @@ pub fn extension_init( let sys = ctx.cted().inst(); sys.dyn_request(hand, payload).await }, - api::HostExtReq::VfsRead(vfs_read) => { - let api::VfsRead(sys_id, vfs_id, path) = &vfs_read; - let ctx = get_ctx(*sys_id).await; - let systems = systems_weak.upgrade().expect("VFS requested during shutdoown"); - let systems_g = systems.lock().await; - let path = join_all(path.iter().map(|t| Tok::from_api(*t, &i))).await; - let vfs = systems_g[sys_id].vfses[vfs_id].load(&path, ctx).await; - hand.handle(&vfs_read, &vfs).await - }, api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) => { let sys_ctx = get_ctx(sys).await; let text = Tok::from_api(text, &i).await; diff --git a/orchid-extension/src/fs.rs b/orchid-extension/src/fs.rs deleted file mode 100644 index 626f71d..0000000 --- a/orchid-extension/src/fs.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::borrow::Cow; -use std::num::NonZero; - -use futures::FutureExt; -use futures::future::LocalBoxFuture; -use hashbrown::HashMap; -use orchid_base::interner::{Interner, Tok}; -use orchid_base::pure_seq::pushed; - -use crate::api; -use crate::system::SysCtx; - -pub trait VirtFS: Send + Sync + 'static { - fn load<'a>( - &'a self, - path: &'a [Tok], - ctx: SysCtx, - ) -> LocalBoxFuture<'a, api::OrcResult>; -} - -#[derive(Clone)] -pub struct DeclVmod(Cow<'static, [(&'static str, DeclFs)]>); -impl DeclVmod { - pub fn new(items: &'static [(&'static str, DeclFs)]) -> DeclVmod { - DeclVmod(Cow::Borrowed(items)) - } - pub fn entry( - key: &'static str, - items: &'static [(&'static str, DeclFs)], - ) -> (&'static str, DeclVmod) { - (key, DeclVmod(Cow::Borrowed(items))) - } - pub fn merge(&self, other: &Self) -> Result> { - let mut items = Vec::new(); - for (k, v1) in self.0.iter() { - items.push((*k, match other.0.iter().find(|(k2, _)| k == k2) { - Some((_, v2)) => v1.merge(v2).map_err(|e| pushed::<_, Vec<_>>(e, *k))?, - None => v1.clone(), - })); - } - for (k, v) in other.0.iter() { - if !items.iter().any(|(k2, _)| k2 == k) { - items.push((*k, v.clone())) - } - } - Ok(Self(Cow::Owned(items))) - } - pub async fn to_api_rec( - &self, - vfses: &mut HashMap, - i: &Interner, - ) -> std::collections::HashMap { - let mut output = std::collections::HashMap::new(); - for (k, v) in self.0.iter() { - output.insert(i.i::(*k).await.to_api(), v.to_api_rec(vfses, i).boxed_local().await); - } - output - } -} - -#[derive(Clone)] -pub enum DeclFs { - Lazy(&'static dyn VirtFS), - Mod(DeclVmod), -} -impl DeclFs { - pub fn merge(&self, other: &Self) -> Result> { - match (self, other) { - (Self::Mod(m1), Self::Mod(m2)) => Ok(Self::Mod(m1.merge(m2)?)), - (..) => Err(Vec::new()), - } - } - pub async fn to_api_rec( - &self, - vfses: &mut HashMap, - i: &Interner, - ) -> api::EagerVfs { - match self { - DeclFs::Lazy(fs) => { - let vfsc: u16 = vfses.len().try_into().expect("too many vfses (more than u16::MAX)"); - let id = api::VfsId(NonZero::new(vfsc + 1).unwrap()); - vfses.insert(id, *fs); - api::EagerVfs::Lazy(id) - }, - DeclFs::Mod(m) => api::EagerVfs::Eager(m.to_api_rec(vfses, i).await), - } - } -} diff --git a/orchid-extension/src/lib.rs b/orchid-extension/src/lib.rs index dd7be36..9b4d779 100644 --- a/orchid-extension/src/lib.rs +++ b/orchid-extension/src/lib.rs @@ -6,7 +6,6 @@ pub mod atom_thin; pub mod conv; pub mod entrypoint; pub mod expr; -pub mod fs; pub mod func_atom; pub mod gen_expr; pub mod lexer; diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs index 69f8f6b..8845a2e 100644 --- a/orchid-extension/src/system.rs +++ b/orchid-extension/src/system.rs @@ -18,7 +18,6 @@ use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; use crate::entrypoint::ExtReq; -use crate::fs::DeclVmod; use crate::func_atom::Fun; use crate::lexer::LexerObj; use crate::parser::ParserObj; @@ -83,7 +82,6 @@ impl DynSystemCard for T { /// System as defined by author pub trait System: Send + Sync + SystemCard + 'static { fn env() -> Vec; - fn vfs() -> DeclVmod; fn lexers() -> Vec; fn parsers() -> Vec; fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future>; @@ -91,7 +89,6 @@ pub trait System: Send + Sync + SystemCard + 'static { pub trait DynSystem: Send + Sync + DynSystemCard + 'static { fn dyn_env(&self) -> Vec; - fn dyn_vfs(&self) -> DeclVmod; fn dyn_lexers(&self) -> Vec; fn dyn_parsers(&self) -> Vec; fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>>; @@ -100,7 +97,6 @@ pub trait DynSystem: Send + Sync + DynSystemCard + 'static { impl DynSystem for T { fn dyn_env(&self) -> Vec { Self::env() } - fn dyn_vfs(&self) -> DeclVmod { Self::vfs() } fn dyn_lexers(&self) -> Vec { Self::lexers() } fn dyn_parsers(&self) -> Vec { Self::parsers() } fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>> { diff --git a/orchid-host/src/fs.rs b/orchid-host/src/fs.rs deleted file mode 100644 index 1b9f695..0000000 --- a/orchid-host/src/fs.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::rc::Rc; - -use async_std::path::PathBuf; -use async_stream::stream; -use futures::{FutureExt, StreamExt}; -use hashbrown::HashMap; -use orchid_base::interner::Tok; -use orchid_base::name::Sym; -use orchid_base::pure_seq::pushed; - -use crate::api; -use crate::system::System; - -#[derive(Clone)] -pub struct DeclMod(Rc, DeclFS>>); -impl DeclMod { - pub async fn from_api( - api: &std::collections::HashMap, - owner: System, - ) -> Self { - let item_stream = stream! { - for (key, value) in api { - yield ( - owner.i().ex(*key).await, - DeclFS::from_api(value, owner.clone()).boxed_local().await - ) - } - }; - Self(Rc::new(item_stream.collect().await)) - } -} - -#[derive(Clone)] -pub enum DeclFS { - Lazy(System, api::VfsId), - Eager(DeclMod), - Path(PathBuf), -} -impl DeclFS { - pub async fn from_api(api: &api::EagerVfs, owner: System) -> Self { - match api { - api::EagerVfs::Eager(items) => Self::Eager(DeclMod::from_api(items, owner.clone()).await), - api::EagerVfs::Lazy(id) => Self::Lazy(owner.clone(), *id), - } - } - pub fn merge(&self, other: &Self) -> Result>> { - let (Self::Eager(m1), Self::Eager(m2)) = (self, other) else { return Err(Vec::new()) }; - let mut mix = m1.0.iter().map(|(k, v)| (k.clone(), v.clone())).collect::>(); - for (key, value) in m2.0.iter() { - match mix.entry(key.clone()) { - hashbrown::hash_map::Entry::Vacant(ent) => { - ent.insert(value.clone()); - }, - hashbrown::hash_map::Entry::Occupied(mut ent) => match ent.get().merge(value) { - Err(e) => return Err(pushed(e, key.clone())), - Ok(new) => { - ent.insert(new); - }, - }, - } - } - Ok(Self::Eager(DeclMod(Rc::new(mix)))) - } - /// Walk through eager fs. Returns if the path ends, if it hits anything other - /// than eager, or if the path is invalid. - pub fn walk<'a, 'b>(&'a self, path: &'b [Tok]) -> (&'a DeclFS, &'b [Tok]) { - let mut cur = self; - for (i, step) in path.iter().enumerate() { - match self { - fs @ (DeclFS::Path(_) | DeclFS::Lazy(..)) => return (fs, &path[i..]), - fs @ DeclFS::Eager(m) => match &m.0.get(step) { - None => return (fs, &path[i..]), - Some(next) => cur = next, - }, - } - } - (cur, &[]) - } -} - -pub async fn load_code(fs: &DeclFS, systems: &[System], path: &[Tok]) -> OrcRes Result { - let (head, tail) = systems.split_first().expect("Empty system list"); - let mut vfs = head.vfs().await; - for sys in tail { - match vfs.merge(&sys.vfs().await) { - Err(e) => return Err(Sym::new(e.iter().rev().cloned(), head.i()).await.unwrap()), - Ok(next) => vfs = next, - } - } - Ok(vfs) -} diff --git a/orchid-host/src/lib.rs b/orchid-host/src/lib.rs index dd79894..eb9617a 100644 --- a/orchid-host/src/lib.rs +++ b/orchid-host/src/lib.rs @@ -7,7 +7,6 @@ pub mod execute; pub mod expr; pub mod expr_store; pub mod extension; -pub mod fs; pub mod lex; pub mod parse; pub mod parsed; diff --git a/orchid-host/src/parse.rs b/orchid-host/src/parse.rs index 7529a6e..3fb4de3 100644 --- a/orchid-host/src/parse.rs +++ b/orchid-host/src/parse.rs @@ -23,11 +23,11 @@ pub struct HostParseCtxImpl<'a> { pub ctx: Ctx, pub src: Sym, pub systems: &'a [System], - pub reporter: &'a Reporter, + pub rep: &'a Reporter, } impl ParseCtx for HostParseCtxImpl<'_> { - fn reporter(&self) -> &Reporter { self.reporter } + fn reporter(&self) -> &Reporter { self.rep } fn i(&self) -> &Interner { &self.ctx.i } } diff --git a/orchid-host/src/parsed.rs b/orchid-host/src/parsed.rs index dc46344..9ccf4c4 100644 --- a/orchid-host/src/parsed.rs +++ b/orchid-host/src/parsed.rs @@ -113,6 +113,10 @@ impl ParsedModule { (self.items.iter()) .filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None }) } + pub fn default_item(self, name: Tok, sr: SrcRange) -> Item { + let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) }; + Item { comments: vec![], sr, kind: ItemKind::Member(mem) } + } } impl Tree for ParsedModule { type Ctx<'a> = (); diff --git a/orchid-host/src/system.rs b/orchid-host/src/system.rs index ad779fa..26ec2c9 100644 --- a/orchid-host/src/system.rs +++ b/orchid-host/src/system.rs @@ -27,7 +27,6 @@ use crate::dealias::{absolute_path, walk}; use crate::expr::{ExprParseCtx, ExprWillPanic}; use crate::expr_store::ExprStore; use crate::extension::{Extension, WeakExtension}; -use crate::fs::{DeclFS, DeclMod}; use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule}; use crate::tree::Root; @@ -40,7 +39,6 @@ struct SystemInstData { lex_filter: api::CharFilter, id: api::SysId, line_types: Vec>, - vfs: std::collections::HashMap, pub(crate) const_paths: MemoMap, } impl Drop for SystemInstData { @@ -79,10 +77,6 @@ impl System { #[must_use] pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() } #[must_use] - pub async fn vfs(&self) -> DeclFS { - DeclFS::Eager(DeclMod::from_api(&self.0.vfs, self.clone()).await) - } - #[must_use] pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) } /// Have this system lex a part of the source. It is assumed that /// [Self::can_lex] was called and returned true. @@ -255,7 +249,6 @@ impl SystemCtor { ext: ext.clone(), ctx: ext.ctx().clone(), lex_filter: sys_inst.lex_filter, - vfs: sys_inst.vfs, line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i))) .await, id, diff --git a/orchid-std/src/std/std_system.rs b/orchid-std/src/std/std_system.rs index c03ef72..4a7c5b8 100644 --- a/orchid-std/src/std/std_system.rs +++ b/orchid-std/src/std/std_system.rs @@ -2,7 +2,6 @@ use never::Never; use orchid_base::reqnot::Receipt; use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; use orchid_extension::entrypoint::ExtReq; -use orchid_extension::fs::DeclVmod; use orchid_extension::lexer::LexerObj; use orchid_extension::parser::ParserObj; use orchid_extension::system::{System, SystemCard}; @@ -36,6 +35,5 @@ impl System for StdSystem { async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} } fn lexers() -> Vec { vec![&StringLexer, &NumLexer] } fn parsers() -> Vec { vec![] } - fn vfs() -> DeclVmod { DeclVmod::new(&[]) } fn env() -> Vec { merge_trivial([gen_num_lib(), gen_str_lib()]) } } diff --git a/orcx/src/main.rs b/orcx/src/main.rs index 3b4e1c5..93a315f 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -1,3 +1,5 @@ +pub mod parse_folder; + use std::cell::RefCell; use std::fs::File; use std::io::{Read, Write}; @@ -6,6 +8,7 @@ use std::process::{Command, ExitCode}; use std::rc::Rc; use async_std::io::stdin; +use async_std::path::PathBuf; use async_stream::try_stream; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; @@ -27,6 +30,8 @@ use orchid_host::system::init_systems; use substack::Substack; use tokio::task::{LocalSet, spawn_local}; +use crate::parse_folder::parse_folder; + #[derive(Parser, Debug)] #[command(version, about, long_about)] pub struct Args { @@ -54,6 +59,8 @@ pub enum Commands { }, Repl, Execute { + #[arg(long)] + proj: Option, #[arg()] code: String, }, @@ -117,7 +124,7 @@ async fn main() -> io::Result { }; let reporter = Reporter::new(); let pctx = HostParseCtxImpl { - reporter: &reporter, + rep: &reporter, systems: &systems, ctx: ctx.clone(), src: sym!(usercode; i).await, @@ -155,7 +162,7 @@ async fn main() -> io::Result { let reporter = Reporter::new(); let parse_ctx = HostParseCtxImpl { ctx: ctx.clone(), - reporter: &reporter, + rep: &reporter, src: path.clone(), systems: &systems[..], }; @@ -185,18 +192,29 @@ async fn main() -> io::Result { ExecResult::Gas(_) => println!("Ran out of gas!"), } }, - Commands::Execute { code } => { - let (root, systems) = init_systems(&args.system, &extensions).await.unwrap(); + Commands::Execute { proj, code } => { + let reporter = Reporter::new(); + let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); + if let Some(proj_path) = proj { + let path = PathBuf::from(proj_path.into_std_path_buf()); + match parse_folder(&root, path, sym!(src; i).await, &reporter, ctx.clone()).await { + Ok(r) => root = r, + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + } + } let lexemes = lex(i.i(code.trim()).await, sym!(usercode; i).await, &systems, ctx).await.unwrap(); if args.logs { println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)); } let path = sym!(usercode; i).await; - let reporter = Reporter::new(); let parse_ctx = HostParseCtxImpl { ctx: ctx.clone(), - reporter: &reporter, + rep: &reporter, src: path.clone(), systems: &systems[..], }; diff --git a/orcx/src/parse_folder.rs b/orcx/src/parse_folder.rs new file mode 100644 index 0000000..5bb2530 --- /dev/null +++ b/orcx/src/parse_folder.rs @@ -0,0 +1,91 @@ +use std::ffi::OsStr; + +use async_std::fs; +use async_std::fs::File; +use async_std::io::ReadExt; +use async_std::path::{Path, PathBuf}; +use async_std::stream::StreamExt; +use futures::FutureExt; +use itertools::Itertools; +use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_errv, 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; + +pub async fn parse_folder( + root: &Root, + path: PathBuf, + ns: Sym, + rep: &Reporter, + ctx: Ctx, +) -> OrcRes { + 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> { + let sr = SrcRange::new(0..0, &ns); + if path.is_dir().await { + let Some(name_os) = path.file_name() else { + return Err(mk_errv( + 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.push(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, + }; + while let Some(entry_res) = stream.next().await { + let entry = match entry_res { + Ok(ent) => ent, + 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(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.push(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([]))) }; + let items = parse_items(&hpctx, Substack::Bottom, Snippet::new(fst, &lexemes)).await?; + Ok(Some(ParsedModule::new(items))) + } else { + Ok(None) + } + } +}