use std::marker::PhantomData; use async_fn_stream::stream; use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, Stream, StreamExt}; use itertools::Itertools; use never::Never; use orchid_base::error::{OrcErrv, OrcRes, Reporter}; use orchid_base::id_store::IdStore; use orchid_base::interner::{Interner, Tok}; use orchid_base::location::SrcRange; use orchid_base::match_mapping; use orchid_base::name::Sym; use orchid_base::parse::{Comment, ParseCtx, Snippet}; use orchid_base::reqnot::Requester; use orchid_base::tree::{TokTree, Token, ttv_into_api}; use crate::api; use crate::context::{SysCtxEntry, ctx, i}; use crate::conv::ToExpr; use crate::expr::Expr; use crate::gen_expr::GExpr; use crate::tree::{GenTok, GenTokTree}; pub type PTok = Token; pub type PTokTree = TokTree; pub type PSnippet<'a> = Snippet<'a, Expr, Never>; pub fn p_tok2gen(tok: PTok) -> GenTok { match_mapping!(tok, PTok => GenTok { Comment(s), Name(n), BR, Handle(ex), Bottom(err), LambdaHead(arg => Box::new(p_tree2gen(*arg))), NS(n, arg => Box::new(p_tree2gen(*arg))), S(p, body () p_v2gen), } { PTok::NewExpr(never) => match never {} }) } pub fn p_tree2gen(tree: PTokTree) -> GenTokTree { TokTree { tok: p_tok2gen(tree.tok), sr: tree.sr } } pub fn p_v2gen(v: impl IntoIterator) -> Vec { v.into_iter().map(p_tree2gen).collect_vec() } pub trait Parser: Send + Sync + Sized + Default + 'static { const LINE_HEAD: &'static str; fn parse<'a>( ctx: ParsCtx<'a>, exported: bool, comments: Vec, line: PSnippet<'a>, ) -> impl Future>> + 'a; } pub trait DynParser: Send + Sync + 'static { fn line_head(&self) -> &'static str; fn parse<'a>( &self, ctx: ParsCtx<'a>, exported: bool, comments: Vec, line: PSnippet<'a>, ) -> LocalBoxFuture<'a, OrcRes>>; } impl DynParser for T { fn line_head(&self) -> &'static str { Self::LINE_HEAD } fn parse<'a>( &self, ctx: ParsCtx<'a>, exported: bool, comments: Vec, line: PSnippet<'a>, ) -> LocalBoxFuture<'a, OrcRes>> { Box::pin(async move { Self::parse(ctx, exported, comments, line).await }) } } pub type ParserObj = &'static dyn DynParser; pub struct ParsCtx<'a> { _parse: PhantomData<&'a mut ()>, module: Sym, reporter: &'a Reporter, i: Interner, } impl<'a> ParsCtx<'a> { pub(crate) fn new(module: Sym, reporter: &'a Reporter) -> Self { Self { _parse: PhantomData, module, reporter, i: i() } } pub fn module(&self) -> Sym { self.module.clone() } } impl ParseCtx for ParsCtx<'_> { fn i(&self) -> &Interner { &self.i } fn rep(&self) -> &Reporter { self.reporter } } type BoxConstCallback = Box LocalBoxFuture<'static, GExpr>>; #[derive(Default)] pub(crate) struct ParsedConstCtxEntry { pub(crate) consts: IdStore, } impl SysCtxEntry for ParsedConstCtxEntry {} pub struct ParsedLine { pub sr: SrcRange, pub comments: Vec, pub kind: ParsedLineKind, } impl ParsedLine { pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>( sr: &SrcRange, comments: impl IntoIterator, exported: bool, name: Tok, f: F, ) -> Self { let cb = Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local()); let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) }); let comments = comments.into_iter().cloned().collect(); ParsedLine { comments, sr: sr.clone(), kind } } pub fn module<'a>( sr: &SrcRange, comments: impl IntoIterator, exported: bool, name: &Tok, use_prelude: bool, lines: impl IntoIterator, ) -> Self { let mem_kind = ParsedMemKind::Mod { lines: lines.into_iter().collect(), use_prelude }; let line_kind = ParsedLineKind::Mem(ParsedMem { name: name.clone(), exported, kind: mem_kind }); let comments = comments.into_iter().cloned().collect(); ParsedLine { comments, sr: sr.clone(), kind: line_kind } } pub async fn into_api(self) -> api::ParsedLine { api::ParsedLine { comments: self.comments.into_iter().map(|c| c.to_api()).collect(), source_range: self.sr.to_api(), kind: match self.kind { ParsedLineKind::Mem(mem) => api::ParsedLineKind::Member(api::ParsedMember { name: mem.name.to_api(), exported: mem.exported, kind: match mem.kind { ParsedMemKind::Const(cb) => api::ParsedMemberKind::Constant(api::ParsedConstId( ctx().get_or_default::().consts.add(cb).id(), )), ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module { lines: linev_into_api(lines).boxed_local().await, use_prelude, }, }, }), ParsedLineKind::Rec(tv) => api::ParsedLineKind::Recursive(ttv_into_api(tv, &mut (), &mut ()).await), }, } } } pub(crate) async fn linev_into_api(v: Vec) -> Vec { join_all(v.into_iter().map(|l| l.into_api())).await } pub enum ParsedLineKind { Mem(ParsedMem), Rec(Vec), } pub struct ParsedMem { pub name: Tok, pub exported: bool, pub kind: ParsedMemKind, } pub enum ParsedMemKind { Const(BoxConstCallback), Mod { lines: Vec, use_prelude: bool }, } #[derive(Clone)] pub struct ConstCtx { constid: api::ParsedConstId, } impl ConstCtx { pub fn names<'b>( &'b self, names: impl IntoIterator + 'b, ) -> impl Stream> + 'b { let resolve_names = api::ResolveNames { constid: self.constid, sys: ctx().sys_id(), names: names.into_iter().map(|n| n.to_api()).collect_vec(), }; stream(async |mut cx| { for name_opt in ctx().reqnot().request(resolve_names).await { cx.emit(match name_opt { Err(e) => Err(OrcErrv::from_api(&e, &i()).await), Ok(name) => Ok(Sym::from_api(name, &i()).await), }) .await } }) } pub async fn names_n(&self, names: [&Sym; N]) -> [OrcRes; N] { self.names(names).collect::>().await.try_into().expect("Lengths must match") } } pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr { let cb = (ctx().get_or_default::().consts.get(id.0)) .expect("Bad ID or double read of parsed const") .remove(); cb(ConstCtx { constid: id }).await }