use futures::FutureExt; use futures::future::join_all; use itertools::Itertools; use orchid_base::error::{OrcErrv, OrcRes}; use orchid_base::interner::{IStr, es}; use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::parse::Comment; use orchid_base::reqnot::ClientExt; use orchid_base::tree::ttv_from_api; use substack::Substack; use crate::api; use crate::expr_store::ExprStore; use crate::parse::HostParseCtx; use crate::parsed::{ Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, tt_to_api, }; use crate::system::System; pub struct Parser { pub(crate) system: System, pub(crate) idx: u16, } type ModPath<'a> = Substack<'a, IStr>; impl Parser { pub async fn parse( &self, ctx: &impl HostParseCtx, path: ModPath<'_>, line: Vec, exported: bool, comments: Vec, callback: &mut impl AsyncFnMut(ModPath<'_>, Vec) -> OrcRes>, ) -> OrcRes> { let mut temp_store = self.system.ctx().exprs.derive(); let src_path = line.first().expect("cannot be empty").sr.path(); let line = join_all((line.into_iter()).map(|t| async { tt_to_api(&mut temp_store.clone(), t).await })) .await; let mod_path = ctx.src_path().suffix(path.unreverse()).await; let comments = comments.iter().map(Comment::to_api).collect_vec(); let req = api::ParseLine { idx: self.idx, module: mod_path.to_api(), src: src_path.to_api(), exported, sys: self.system.id(), comments, line, }; match self.system.client().request(req).await.unwrap() { Ok(parsed_v) => conv(parsed_v, path, callback, &mut ConvCtx { mod_path: &mod_path, ext_exprs: &mut temp_store, src_path: &src_path, sys: &self.system, }) .await, Err(e) => Err(OrcErrv::from_api(&e).await), } } } struct ConvCtx<'a> { sys: &'a System, mod_path: &'a Sym, src_path: &'a Sym, ext_exprs: &'a mut ExprStore, } async fn conv( parsed_v: Vec, module: Substack<'_, IStr>, callback: &'_ mut impl AsyncFnMut(Substack<'_, IStr>, Vec) -> OrcRes>, ctx: &mut ConvCtx<'_>, ) -> OrcRes> { let mut items = Vec::new(); for parsed in parsed_v { let (name, exported, kind) = match parsed.kind { api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) => (name, exported, kind), api::ParsedLineKind::Recursive(rec) => { let tokens = ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path).await; items.extend(callback(module.clone(), tokens).await?); continue; }, }; let name = es(name).await; let mem_path = module.push(name.clone()); let mkind = match kind { api::ParsedMemberKind::Module { lines, use_prelude } => { let items = conv(lines, mem_path, callback, ctx).boxed_local().await?; ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items)) }, api::ParsedMemberKind::Constant(cid) => { ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await); ParsedMemberKind::Const(cid, ctx.sys.clone()) }, }; items.push(Item { comments: join_all( parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone())), ) .await, sr: SrcRange::from_api(&parsed.source_range).await, kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }), }) } Ok(items) }