forked from Orchid/orchid
task_local context over context objects
- 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
This commit is contained in:
@@ -4,15 +4,14 @@ use async_fn_stream::stream;
|
||||
use async_once_cell::OnceCell;
|
||||
use futures::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::{OrcRes, Reporter, mk_errv};
|
||||
use orchid_base::error::{OrcRes, mk_errv, report, with_reporter};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::parse::{
|
||||
Comment, ParseCtx, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv,
|
||||
try_pop_no_fluff,
|
||||
Comment, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv, try_pop_no_fluff,
|
||||
};
|
||||
use orchid_base::tree::{Paren, Token};
|
||||
use orchid_base::{clone, sym};
|
||||
use orchid_extension::atom::TAtom;
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||
use orchid_extension::gen_expr::{call, sym_ref};
|
||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||
@@ -35,22 +34,22 @@ impl Parser for MacroLine {
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("macros are always exported").await,
|
||||
is("macros are always exported").await,
|
||||
"The export keyword is forbidden here to avoid confusion\n\
|
||||
because macros are exported by default",
|
||||
[line.sr()],
|
||||
));
|
||||
}
|
||||
let module = ctx.module();
|
||||
let Parsed { output: prio_or_body, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||
let Parsed { output: prio_or_body, tail } = try_pop_no_fluff(line).await?;
|
||||
let bad_first_item_err = || {
|
||||
token_errv(&ctx, prio_or_body, "Expected priority or block", |s| {
|
||||
token_errv(prio_or_body, "Expected priority or block", |s| {
|
||||
format!("Expected a priority number or a () block, found {s}")
|
||||
})
|
||||
};
|
||||
let (prio, body) = match &prio_or_body.tok {
|
||||
Token::S(Paren::Round, body) => {
|
||||
expect_end(&ctx, tail).await?;
|
||||
expect_end(tail).await?;
|
||||
(None, body)
|
||||
},
|
||||
Token::Handle(expr) => match TAtom::<Int>::try_from_expr(expr.clone()).await {
|
||||
@@ -58,33 +57,32 @@ impl Parser for MacroLine {
|
||||
return Err(e + bad_first_item_err().await);
|
||||
},
|
||||
Ok(prio) => {
|
||||
let Parsed { output: body, tail } = try_pop_no_fluff(&ctx, tail).await?;
|
||||
let Parsed { output: body, tail } = try_pop_no_fluff(tail).await?;
|
||||
let Token::S(Paren::Round, block) = &body.tok else {
|
||||
return Err(
|
||||
token_errv(&ctx, prio_or_body, "Expected () block", |s| {
|
||||
token_errv(prio_or_body, "Expected () block", |s| {
|
||||
format!("Expected a () block, found {s}")
|
||||
})
|
||||
.await,
|
||||
);
|
||||
};
|
||||
expect_end(&ctx, tail).await?;
|
||||
expect_end(tail).await?;
|
||||
(Some(prio), block)
|
||||
},
|
||||
},
|
||||
_ => return Err(bad_first_item_err().await),
|
||||
};
|
||||
let lines = line_items(&ctx, Snippet::new(prio_or_body, body)).await;
|
||||
let lines = line_items(Snippet::new(prio_or_body, body)).await;
|
||||
let Some((kw_line, rule_lines)) = lines.split_first() else { return Ok(Vec::new()) };
|
||||
let mut keywords = Vec::new();
|
||||
let Parsed { tail: kw_tail, .. } =
|
||||
expect_tok(&ctx, kw_line.tail, ctx.i().i("keywords").await).await?;
|
||||
let Parsed { tail: kw_tail, .. } = expect_tok(kw_line.tail, is("keywords").await).await?;
|
||||
for kw_tok in kw_tail.iter().filter(|kw| !kw.is_fluff()) {
|
||||
match kw_tok.as_name() {
|
||||
Some(kw) => {
|
||||
keywords.push((kw, kw_tok.sr()));
|
||||
},
|
||||
None => ctx.rep().report(
|
||||
token_errv(&ctx, kw_tok, "invalid macro keywords list", |tok| {
|
||||
None => report(
|
||||
token_errv(kw_tok, "invalid macro keywords list", |tok| {
|
||||
format!("The keywords list must be a sequence of names; received {tok}")
|
||||
})
|
||||
.await,
|
||||
@@ -93,7 +91,7 @@ impl Parser for MacroLine {
|
||||
}
|
||||
let Some((macro_name, _)) = keywords.first().cloned() else {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("macro with no keywords").await,
|
||||
is("macro with no keywords").await,
|
||||
"Macros must define at least one macro of their own.",
|
||||
[kw_line.tail.sr()],
|
||||
));
|
||||
@@ -102,18 +100,18 @@ impl Parser for MacroLine {
|
||||
let mut lines = Vec::new();
|
||||
for (idx, line) in rule_lines.iter().enumerate().map(|(n, v)| (n as u32, v)) {
|
||||
let sr = line.tail.sr();
|
||||
let name = ctx.i().i(&format!("rule::{}::{}", macro_name, idx)).await;
|
||||
let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?;
|
||||
let arrow_token = ctx.i().i("=>").await;
|
||||
let name = is(&format!("rule::{}::{}", macro_name, idx)).await;
|
||||
let Parsed { tail, .. } = expect_tok(line.tail, is("rule").await).await?;
|
||||
let arrow_token = is("=>").await;
|
||||
let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else {
|
||||
ctx.rep().report(mk_errv(
|
||||
ctx.i().i("Missing => in rule").await,
|
||||
report(mk_errv(
|
||||
is("Missing => in rule").await,
|
||||
"Rule lines are of the form `rule ...pattern => ...body`",
|
||||
[line.tail.sr()],
|
||||
));
|
||||
continue;
|
||||
};
|
||||
let pattern = parse_tokv(pattern, &ctx).await;
|
||||
let pattern = parse_tokv(pattern).await;
|
||||
let mut placeholders = Vec::new();
|
||||
pattern.map(&mut false, &mut |tok| {
|
||||
if let MacTok::Ph(ph) = tok.tok() {
|
||||
@@ -121,9 +119,9 @@ impl Parser for MacroLine {
|
||||
}
|
||||
None
|
||||
});
|
||||
let mut body_mactree = parse_tokv(body, &ctx).await;
|
||||
let mut body_mactree = parse_tokv(body).await;
|
||||
for (ph, ph_pos) in placeholders.iter().rev() {
|
||||
let name = ctx.module().suffix([ph.name.clone()], ctx.i()).await;
|
||||
let name = ctx.module().suffix([ph.name.clone()]).await;
|
||||
body_mactree =
|
||||
MacTreeSeq::new([
|
||||
MacTok::Lambda(MacTok::Name(name).at(ph_pos.clone()), body_mactree).at(ph_pos.clone())
|
||||
@@ -132,45 +130,40 @@ impl Parser for MacroLine {
|
||||
let body_sr = body.sr();
|
||||
rules.push((name.clone(), placeholders, pattern));
|
||||
lines.push(ParsedLine::cnst(&sr, &line.output, true, name, async move |ctx| {
|
||||
let rep = Reporter::new();
|
||||
let body = dealias_mac_v(&body_mactree, &ctx, &rep).await;
|
||||
let macro_input = MacTok::S(Paren::Round, body).at(body_sr.pos());
|
||||
if let Some(e) = rep.errv() {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(call(sym_ref(sym!(macros::resolve; i())), [macro_input.to_gen().await]))
|
||||
let macro_input =
|
||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?)
|
||||
.at(body_sr.pos());
|
||||
Ok(call(sym_ref(sym!(macros::resolve)), [macro_input.to_gen().await]))
|
||||
}))
|
||||
}
|
||||
let mac_cell = Rc::new(OnceCell::new());
|
||||
let rules = Rc::new(rules);
|
||||
for (kw, sr) in &*keywords {
|
||||
clone!(mac_cell, rules, module, macro_name, prio);
|
||||
let kw_key = i().i(&format!("__macro__{kw}")).await;
|
||||
let kw_key = is(&format!("__macro__{kw}")).await;
|
||||
lines.push(ParsedLine::cnst(&sr.clone(), &comments, true, kw_key, async move |cctx| {
|
||||
let mac_future = async {
|
||||
let rep = Reporter::new();
|
||||
let rules = stream(async |mut h| {
|
||||
for (body, ph_names, pattern_rel) in rules.iter() {
|
||||
let pattern = dealias_mac_v(pattern_rel, &cctx, &rep).await;
|
||||
let ph_names = ph_names.iter().map(|(ph, _)| ph.name.clone()).collect_vec();
|
||||
match Matcher::new(pattern.clone()).await {
|
||||
Ok(matcher) =>
|
||||
h.emit(Rule { body: body.clone(), matcher, pattern, ph_names }).await,
|
||||
Err(e) => rep.report(e),
|
||||
let rules = with_reporter(
|
||||
stream(async |mut h| {
|
||||
for (body, ph_names, pattern_rel) in rules.iter() {
|
||||
let pattern = dealias_mac_v(pattern_rel, &cctx).await;
|
||||
let ph_names = ph_names.iter().map(|(ph, _)| ph.name.clone()).collect_vec();
|
||||
match Matcher::new(pattern.clone()).await {
|
||||
Ok(matcher) =>
|
||||
h.emit(Rule { body: body.clone(), matcher, pattern, ph_names }).await,
|
||||
Err(e) => report(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
match rep.errv() {
|
||||
Some(e) => Err(e),
|
||||
None => Ok(Macro(Rc::new(MacroData {
|
||||
canonical_name: module.suffix([macro_name], &i()).await,
|
||||
module,
|
||||
prio: prio.map(|i| i.0 as u64),
|
||||
rules,
|
||||
}))),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.await?;
|
||||
Ok(Macro(Rc::new(MacroData {
|
||||
canonical_name: module.suffix([macro_name]).await,
|
||||
module,
|
||||
prio: prio.map(|i| i.0 as u64),
|
||||
rules,
|
||||
})))
|
||||
};
|
||||
mac_cell.get_or_init(mac_future).await.clone().to_gen().await
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user