- interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason
212 lines
6.0 KiB
Rust
212 lines
6.0 KiB
Rust
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};
|
|
use orchid_base::id_store::IdStore;
|
|
use orchid_base::interner::IStr;
|
|
use orchid_base::location::SrcRange;
|
|
use orchid_base::match_mapping;
|
|
use orchid_base::name::Sym;
|
|
use orchid_base::parse::{Comment, Snippet};
|
|
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
|
use task_local::task_local;
|
|
|
|
use crate::api;
|
|
use crate::conv::ToExpr;
|
|
use crate::entrypoint::request;
|
|
use crate::expr::Expr;
|
|
use crate::gen_expr::GExpr;
|
|
use crate::system::sys_id;
|
|
use crate::tree::{GenTok, GenTokTree};
|
|
|
|
pub type PTok = Token<Expr, Never>;
|
|
pub type PTokTree = TokTree<Expr, Never>;
|
|
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<Item = PTokTree>) -> Vec<GenTokTree> {
|
|
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<Comment>,
|
|
line: PSnippet<'a>,
|
|
) -> impl Future<Output = OrcRes<Vec<ParsedLine>>> + 'a;
|
|
}
|
|
|
|
pub trait DynParser: Send + Sync + 'static {
|
|
fn line_head(&self) -> &'static str;
|
|
fn parse<'a>(
|
|
&self,
|
|
ctx: ParsCtx<'a>,
|
|
exported: bool,
|
|
comments: Vec<Comment>,
|
|
line: PSnippet<'a>,
|
|
) -> LocalBoxFuture<'a, OrcRes<Vec<ParsedLine>>>;
|
|
}
|
|
|
|
impl<T: Parser> DynParser for T {
|
|
fn line_head(&self) -> &'static str { Self::LINE_HEAD }
|
|
fn parse<'a>(
|
|
&self,
|
|
ctx: ParsCtx<'a>,
|
|
exported: bool,
|
|
comments: Vec<Comment>,
|
|
line: PSnippet<'a>,
|
|
) -> LocalBoxFuture<'a, OrcRes<Vec<ParsedLine>>> {
|
|
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,
|
|
}
|
|
impl<'a> ParsCtx<'a> {
|
|
pub(crate) fn new(module: Sym) -> Self { Self { _parse: PhantomData, module } }
|
|
pub fn module(&self) -> Sym { self.module.clone() }
|
|
}
|
|
|
|
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
|
|
|
task_local! {
|
|
static PARSED_CONST_CTX: IdStore<BoxConstCallback>
|
|
}
|
|
|
|
pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
|
Box::pin(PARSED_CONST_CTX.scope(IdStore::default(), fut))
|
|
}
|
|
|
|
pub struct ParsedLine {
|
|
pub sr: SrcRange,
|
|
pub comments: Vec<Comment>,
|
|
pub kind: ParsedLineKind,
|
|
}
|
|
impl ParsedLine {
|
|
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>(
|
|
sr: &SrcRange,
|
|
comments: impl IntoIterator<Item = &'a Comment>,
|
|
exported: bool,
|
|
name: IStr,
|
|
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<Item = &'a Comment>,
|
|
exported: bool,
|
|
name: &IStr,
|
|
use_prelude: bool,
|
|
lines: impl IntoIterator<Item = ParsedLine>,
|
|
) -> 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(
|
|
PARSED_CONST_CTX.with(|consts| 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<ParsedLine>) -> Vec<api::ParsedLine> {
|
|
join_all(v.into_iter().map(|l| l.into_api())).await
|
|
}
|
|
|
|
pub enum ParsedLineKind {
|
|
Mem(ParsedMem),
|
|
Rec(Vec<GenTokTree>),
|
|
}
|
|
|
|
pub struct ParsedMem {
|
|
pub name: IStr,
|
|
pub exported: bool,
|
|
pub kind: ParsedMemKind,
|
|
}
|
|
|
|
pub enum ParsedMemKind {
|
|
Const(BoxConstCallback),
|
|
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ConstCtx {
|
|
constid: api::ParsedConstId,
|
|
}
|
|
impl ConstCtx {
|
|
pub fn names<'b>(
|
|
&'b self,
|
|
names: impl IntoIterator<Item = &'b Sym> + 'b,
|
|
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
|
|
let resolve_names = api::ResolveNames {
|
|
constid: self.constid,
|
|
sys: sys_id(),
|
|
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
|
|
};
|
|
stream(async |mut cx| {
|
|
for name_opt in request(resolve_names).await {
|
|
cx.emit(match name_opt {
|
|
Err(e) => Err(OrcErrv::from_api(&e).await),
|
|
Ok(name) => Ok(Sym::from_api(name).await),
|
|
})
|
|
.await
|
|
}
|
|
})
|
|
}
|
|
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
|
|
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
|
let cb = PARSED_CONST_CTX
|
|
.with(|ent| ent.get(id.0).expect("Bad ID or double read of parsed const").remove());
|
|
cb(ConstCtx { constid: id }).await
|
|
}
|