213 lines
6.2 KiB
Rust
213 lines
6.2 KiB
Rust
use std::marker::PhantomData;
|
|
|
|
use async_stream::stream;
|
|
use futures::future::{LocalBoxFuture, join_all};
|
|
use futures::{FutureExt, Stream, StreamExt, pin_mut};
|
|
use itertools::Itertools;
|
|
use never::Never;
|
|
use orchid_api::ResolveNames;
|
|
use orchid_base::error::{OrcRes, Reporter};
|
|
use orchid_base::id_store::IdStore;
|
|
use orchid_base::interner::{Interner, Tok};
|
|
use orchid_base::location::SrcRange;
|
|
use orchid_base::name::Sym;
|
|
use orchid_base::parse::{Comment, ParseCtx, Snippet};
|
|
use orchid_base::reqnot::{ReqHandlish, Requester};
|
|
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
|
|
|
use crate::api;
|
|
use crate::expr::Expr;
|
|
use crate::gen_expr::GExpr;
|
|
use crate::system::{SysCtx, SysCtxEntry};
|
|
use crate::tree::GenTokTree;
|
|
|
|
pub type PTok = Token<Expr, Never>;
|
|
pub type PTokTree = TokTree<Expr, Never>;
|
|
pub type PSnippet<'a> = Snippet<'a, Expr, Never>;
|
|
|
|
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 ()>,
|
|
ctx: SysCtx,
|
|
module: Sym,
|
|
reporter: &'a Reporter,
|
|
}
|
|
impl<'a> ParsCtx<'a> {
|
|
pub(crate) fn new(ctx: SysCtx, module: Sym, reporter: &'a Reporter) -> Self {
|
|
Self { _parse: PhantomData, ctx, module, reporter }
|
|
}
|
|
pub fn ctx(&self) -> &SysCtx { &self.ctx }
|
|
pub fn module(&self) -> Sym { self.module.clone() }
|
|
}
|
|
impl ParseCtx for ParsCtx<'_> {
|
|
fn i(&self) -> &Interner { self.ctx.i() }
|
|
fn rep(&self) -> &Reporter { self.reporter }
|
|
}
|
|
|
|
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
|
|
|
#[derive(Default)]
|
|
struct ParsedConstCtxEntry {
|
|
consts: IdStore<BoxConstCallback>,
|
|
}
|
|
impl SysCtxEntry for ParsedConstCtxEntry {}
|
|
|
|
pub struct ParsedLine {
|
|
pub sr: SrcRange,
|
|
pub comments: Vec<Comment>,
|
|
pub kind: ParsedLineKind,
|
|
}
|
|
impl ParsedLine {
|
|
pub async fn into_api(self, ctx: SysCtx, hand: &dyn ReqHandlish) -> 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::<ParsedConstCtxEntry>().consts.add(cb).id(),
|
|
)),
|
|
ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module {
|
|
lines: linev_into_api(lines, ctx, hand).boxed_local().await,
|
|
use_prelude,
|
|
},
|
|
},
|
|
}),
|
|
ParsedLineKind::Rec(tv) =>
|
|
api::ParsedLineKind::Recursive(ttv_into_api(tv, &mut (), &mut (ctx, hand)).await),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn linev_into_api(
|
|
v: Vec<ParsedLine>,
|
|
ctx: SysCtx,
|
|
hand: &dyn ReqHandlish,
|
|
) -> Vec<api::ParsedLine> {
|
|
join_all(v.into_iter().map(|l| l.into_api(ctx.clone(), hand))).await
|
|
}
|
|
|
|
pub enum ParsedLineKind {
|
|
Mem(ParsedMem),
|
|
Rec(Vec<GenTokTree>),
|
|
}
|
|
|
|
pub struct ParsedMem {
|
|
pub name: Tok<String>,
|
|
pub exported: bool,
|
|
pub kind: ParsedMemKind,
|
|
}
|
|
|
|
pub enum ParsedMemKind {
|
|
Const(BoxConstCallback),
|
|
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
|
|
}
|
|
|
|
impl ParsedMemKind {
|
|
pub fn cnst<F: AsyncFnOnce(ConstCtx) -> GExpr + 'static>(f: F) -> Self {
|
|
Self::Const(Box::new(|ctx| Box::pin(f(ctx))))
|
|
}
|
|
pub fn module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
|
|
Self::Mod { lines: lines.into_iter().collect(), use_prelude: true }
|
|
}
|
|
pub fn clean_module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
|
|
Self::Mod { lines: lines.into_iter().collect(), use_prelude: false }
|
|
}
|
|
}
|
|
|
|
/* TODO: how the macro runner uses the multi-stage loader
|
|
|
|
Since the macro runner actually has to invoke the interpreter,
|
|
it'll run at const-time and not at postparse-time anyway.
|
|
|
|
pasing stage establishes the role of every constant as a macro keyword
|
|
postparse / const load links up constants with every macro they can directly invoke
|
|
the constants representing the keywords might not actually be postparsed,
|
|
\ the connection is instead made by detecting in the macro system that the
|
|
\ resolved name is owned by a macro
|
|
the returned constant from this call is always an entrypoint call to
|
|
\ the macro system
|
|
the constants representing the keywords resolve to panic
|
|
execute relies on these links detected in the extension to dispatch relevant macros
|
|
*/
|
|
|
|
pub struct ConstCtx {
|
|
ctx: SysCtx,
|
|
constid: api::ParsedConstId,
|
|
}
|
|
impl ConstCtx {
|
|
pub fn names<'a>(
|
|
&'a self,
|
|
names: impl IntoIterator<Item = &'a Sym> + 'a,
|
|
) -> impl Stream<Item = Option<Sym>> + 'a {
|
|
let resolve_names = ResolveNames {
|
|
constid: self.constid,
|
|
sys: self.ctx.sys_id(),
|
|
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
|
|
};
|
|
stream! {
|
|
for name_opt in self.ctx.reqnot().request(resolve_names).await {
|
|
yield match name_opt {
|
|
None => None,
|
|
Some(name) => Some(Sym::from_api(name, self.ctx.i()).await)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [Option<Sym>; N] {
|
|
let mut results = [const { None }; N];
|
|
let names = self.names(names).enumerate().filter_map(|(i, n)| async move { Some((i, n?)) });
|
|
pin_mut!(names);
|
|
while let Some((i, name)) = names.next().await {
|
|
results[i] = Some(name);
|
|
}
|
|
results
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn get_const(id: api::ParsedConstId, ctx: SysCtx) -> GExpr {
|
|
let ent = ctx.get::<ParsedConstCtxEntry>();
|
|
let rec = ent.consts.get(id.0).expect("Bad ID or double read of parsed const");
|
|
let ctx = ConstCtx { constid: id, ctx: ctx.clone() };
|
|
rec.remove()(ctx).await
|
|
}
|