Updated macro system to process and return mactree and then lower it separately to gexpr
Some checks failed
Rust / build (push) Has been cancelled
Some checks failed
Rust / build (push) Has been cancelled
This commit is contained in:
@@ -1,50 +1,18 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Add, Range};
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::{FutureExt, StreamExt, stream};
|
||||
use futures::FutureExt;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv};
|
||||
use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call, call_v, new_atom};
|
||||
use orchid_extension::{
|
||||
Atomic, ExecHandle, OwnedAtom, OwnedVariant, ReflMemKind, TAtom, ToExpr, ToExprFuture, exec, refl,
|
||||
};
|
||||
use orchid_base::{NameLike, Paren, Pos, VPath, fmt, is, log, mk_errv, report};
|
||||
use orchid_extension::gen_expr::{GExpr, call_v, new_atom};
|
||||
use orchid_extension::{ExecHandle, ReflMemKind, TAtom, ToExpr, refl};
|
||||
use subslice_offset::SubsliceOffset;
|
||||
|
||||
use crate::macros::macro_value::{Macro, Rule};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::postmac::{PostMac, PostMacAtom};
|
||||
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
pub enum ArgStackKind {
|
||||
End,
|
||||
Cons(Sym, ArgStack),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ArgStack {
|
||||
kind: Rc<ArgStackKind>,
|
||||
len: usize,
|
||||
}
|
||||
impl ArgStack {
|
||||
pub fn end() -> Self { ArgStack { kind: Rc::new(ArgStackKind::End), len: 0 } }
|
||||
}
|
||||
impl Default for ArgStack {
|
||||
fn default() -> Self { Self::end() }
|
||||
}
|
||||
impl Atomic for ArgStack {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for ArgStack {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
|
||||
/// # TODO
|
||||
///
|
||||
/// convert macro system to return MacTree or otherwise bring it up to
|
||||
@@ -100,7 +68,7 @@ impl OwnedAtom for ArgStack {
|
||||
/// The best option probably remains for resolve to process and return MacTree,
|
||||
/// and for there to be a separate "lower" function. Nothing as yet suggests
|
||||
/// however that macros can't be allowed to return different types
|
||||
pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree, arg_stk: ArgStack) -> PostMacAtom {
|
||||
pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree) -> MacTree {
|
||||
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await;
|
||||
let root = refl();
|
||||
let mut macros = HashMap::new();
|
||||
@@ -138,8 +106,9 @@ pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree, arg_stk: ArgStack) ->
|
||||
}
|
||||
}
|
||||
let mut rctx = ResolveCtx { exclusive, priod };
|
||||
let gex = resolve_one(&mut rctx, arg_stk, &val).await;
|
||||
writeln!(log("debug"), "Macro-resolution over {}", fmt(&val).await).await;
|
||||
let gex = resolve_one(&mut rctx, &val).await.unwrap_or(val.clone());
|
||||
writeln!(log("debug"), "Macro-resolution over {} yielded {}", fmt(&val).await, fmt(&gex).await)
|
||||
.await;
|
||||
gex
|
||||
}
|
||||
|
||||
@@ -158,54 +127,25 @@ struct ResolveCtx<'a> {
|
||||
pub priod: Vec<FilteredMacroRecord<'a>>,
|
||||
}
|
||||
|
||||
async fn resolve_one(ctx: &mut ResolveCtx<'_>, arg_stk: ArgStack, value: &MacTree) -> PostMacAtom {
|
||||
async fn resolve_one(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTree> {
|
||||
eprintln!("Resolving unit {}", fmt(value).await);
|
||||
match value.tok() {
|
||||
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
||||
MacTok::Bottom(err) => PostMac::new(bot(err.clone())).atom(),
|
||||
MacTok::Value(v) => {
|
||||
eprintln!("Found value {}", fmt(v).await);
|
||||
PostMac::new(v.clone()).atom()
|
||||
},
|
||||
MacTok::Name(n) => {
|
||||
eprintln!("Looking for {n} among [");
|
||||
let mut cur = &arg_stk;
|
||||
let mut counter = 0;
|
||||
while let ArgStackKind::Cons(name, next) = &*cur.kind {
|
||||
cur = next;
|
||||
counter += 1;
|
||||
eprintln!("{name}, ");
|
||||
if name == n {
|
||||
return PostMac::new(GExprKind::Arg(counter).at(value.pos())).atom();
|
||||
}
|
||||
}
|
||||
PostMac::new(n.clone()).atom()
|
||||
},
|
||||
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
|
||||
MacTok::Lambda(arg, body) => {
|
||||
eprintln!("Found lambda \\{} {}", fmt(arg).await, fmt(body).await);
|
||||
let MacTok::Name(name) = &*arg.tok else {
|
||||
return PostMac::new(bot(mk_errv(
|
||||
is("Syntax error after macros").await,
|
||||
"This token ends up as a binding, consider replacing it with a name",
|
||||
[arg.pos()],
|
||||
)))
|
||||
.atom();
|
||||
};
|
||||
let arg_stk =
|
||||
ArgStack { len: arg_stk.len + 1, kind: Rc::new(ArgStackKind::Cons(name.clone(), arg_stk)) };
|
||||
let body = resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await;
|
||||
let body2 = body.clone();
|
||||
let pos = value.pos();
|
||||
PostMac::with(async |cx| GExprKind::Lambda(Box::new(cx.ex(body).to_gen().await)).at(pos))
|
||||
.atom()
|
||||
let new_arg = resolve_one(ctx, arg).boxed_local().await;
|
||||
let new_body = resolve_seq(ctx, body, value.pos()).await;
|
||||
if new_arg.is_none() && new_body.is_none() {
|
||||
return None;
|
||||
}
|
||||
let tok = MacTok::Lambda(
|
||||
new_arg.unwrap_or_else(|| arg.clone()),
|
||||
new_body.unwrap_or_else(|| body.clone()),
|
||||
);
|
||||
Some(tok.at(value.pos()))
|
||||
},
|
||||
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await,
|
||||
MacTok::S(..) => PostMac::new(bot(mk_errv(
|
||||
is("Leftover [] or {} not matched by macro").await,
|
||||
format!("{} was not matched by any macro", fmt(value).await),
|
||||
[value.pos()],
|
||||
)))
|
||||
.atom(),
|
||||
MacTok::S(pty, body) =>
|
||||
resolve_seq(ctx, body, value.pos()).await.map(|body| MacTok::S(*pty, body).at(value.pos())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,18 +165,11 @@ fn subsection<T>(
|
||||
|
||||
async fn resolve_seq(
|
||||
ctx: &mut ResolveCtx<'_>,
|
||||
arg_stk: ArgStack,
|
||||
val: MacTreeSeq,
|
||||
val: &MacTreeSeq,
|
||||
fallback_pos: Pos,
|
||||
) -> PostMacAtom {
|
||||
) -> Option<MacTreeSeq> {
|
||||
if val.items.is_empty() {
|
||||
return PostMac::new(bot(mk_errv(
|
||||
is("Empty sequence").await,
|
||||
"() or (\\arg ) left after macro execution. \
|
||||
This is usually caused by an incomplete call to a macro with bad error detection",
|
||||
[fallback_pos],
|
||||
)))
|
||||
.atom();
|
||||
return None;
|
||||
}
|
||||
// A sorted collection of overlapping but non-nested matches to exclusive
|
||||
// macros
|
||||
@@ -289,6 +222,7 @@ async fn resolve_seq(
|
||||
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
|
||||
}
|
||||
}
|
||||
let mut any_match = !x_matches.is_empty();
|
||||
// apply exclusive matches
|
||||
if !x_matches.is_empty() {
|
||||
// ranges of indices into x_matches which setwise conflict with each other.
|
||||
@@ -319,19 +253,19 @@ async fn resolve_seq(
|
||||
})
|
||||
.reduce(|l, r| l + r);
|
||||
if let Some(error) = error {
|
||||
return PostMac::new(bot(error)).atom();
|
||||
report(error.clone());
|
||||
return Some(MacTreeSeq::new([MacTok::Bottom(error).at(fallback_pos)]));
|
||||
}
|
||||
// no conflicts, apply all exclusive matches
|
||||
for (range, mac, rule, state) in x_matches.into_iter().rev() {
|
||||
// backwards so that the non-overlapping ranges remain valid
|
||||
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
|
||||
.expect("All macro rules must contain at least one locally defined name");
|
||||
let subex =
|
||||
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
|
||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
||||
}
|
||||
};
|
||||
// Does this glossary refresh actually pay off?
|
||||
// TODO: Does this glossary refresh actually pay off?
|
||||
let top_glossary = (new_val.iter())
|
||||
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
|
||||
.collect::<HashSet<_>>();
|
||||
@@ -342,41 +276,20 @@ async fn resolve_seq(
|
||||
continue;
|
||||
}
|
||||
let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue };
|
||||
any_match = true;
|
||||
let range = pre.len()..new_val.len() - suf.len();
|
||||
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
|
||||
.expect("All macro rules must contain at least one locally defined name");
|
||||
let subex =
|
||||
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
|
||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
||||
std::mem::drop(state);
|
||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
||||
}
|
||||
}
|
||||
let mut exprs = stream(async |mut h| {
|
||||
for mt in new_val {
|
||||
h.emit(resolve_one(ctx, arg_stk.clone(), &mt).await).await
|
||||
}
|
||||
})
|
||||
.collect::<VecDeque<_>>()
|
||||
.boxed_local()
|
||||
.await;
|
||||
let first = exprs.pop_front().expect(
|
||||
"We checked first that it isn't empty, and named macros get replaced with their results",
|
||||
);
|
||||
PostMac::with(async move |cx| {
|
||||
stream::iter(exprs).fold(cx.ex(first), async |f, x| call(f, cx.ex(x)).await).await
|
||||
})
|
||||
.await
|
||||
.atom()
|
||||
any_match.then_some(MacTreeSeq::new(new_val))
|
||||
}
|
||||
|
||||
async fn mk_body_call(
|
||||
mac: &Macro,
|
||||
rule: &Rule,
|
||||
state: &MatchState<'_>,
|
||||
pos: Pos,
|
||||
arg_stk: ArgStack,
|
||||
) -> GExpr {
|
||||
let mut call_args = vec![new_atom(arg_stk).at(Pos::None)];
|
||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr {
|
||||
let mut call_args = vec![];
|
||||
for name in rule.ph_names.iter() {
|
||||
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||
StateEntry::Scalar(scal) => new_atom((**scal).clone()),
|
||||
|
||||
Reference in New Issue
Block a user