Fixed macro system by reintroducing MacTok::Resolved

This commit is contained in:
2026-04-16 20:12:28 +00:00
parent 23180b66e3
commit 286040c3ec
14 changed files with 130 additions and 58 deletions

View File

@@ -4,8 +4,8 @@ use futures::FutureExt;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
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 orchid_extension::gen_expr::{call_v, new_atom};
use orchid_extension::{ExecHandle, ReflMemKind, TAtom, refl};
use subslice_offset::SubsliceOffset;
use crate::macros::macro_value::{Macro, Rule};
@@ -105,7 +105,7 @@ pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree) -> MacTree {
}
}
}
let mut rctx = ResolveCtx { exclusive, priod };
let mut rctx = ResolveCtx { exclusive, priod, h: &mut *h };
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;
@@ -119,22 +119,22 @@ pub struct FilteredMacroRecord<'a> {
rules: Vec<usize>,
}
struct ResolveCtx<'a> {
struct ResolveCtx<'a, 'b> {
/// If these overlap, that's a compile-time error
pub exclusive: Vec<FilteredMacroRecord<'a>>,
/// If these overlap, the priorities decide the order. In case of a tie, the
/// order is unspecified
pub priod: Vec<FilteredMacroRecord<'a>>,
pub h: &'a mut ExecHandle<'b>,
}
async fn resolve_one(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTree> {
eprintln!("Resolving unit {}", fmt(value).await);
async fn resolve_one(ctx: &mut ResolveCtx<'_, '_>, value: &MacTree) -> Option<MacTree> {
match value.tok() {
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) | MacTok::Resolved(_) => None,
MacTok::Lambda(arg, body) => {
let new_arg = resolve_one(ctx, arg).boxed_local().await;
let new_body = resolve_seq(ctx, body, value.pos()).await;
let new_body = resolve_seq(ctx, body, value.pos()).boxed_local().await;
if new_arg.is_none() && new_body.is_none() {
return None;
}
@@ -144,8 +144,8 @@ async fn resolve_one(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTre
);
Some(tok.at(value.pos()))
},
MacTok::S(pty, body) =>
resolve_seq(ctx, body, value.pos()).await.map(|body| MacTok::S(*pty, body).at(value.pos())),
MacTok::S(pty, body) => (resolve_seq(ctx, body, value.pos()).boxed_local().await)
.map(|body| MacTok::S(*pty, body).at(value.pos())),
}
}
@@ -164,7 +164,7 @@ fn subsection<T>(
}
async fn resolve_seq(
ctx: &mut ResolveCtx<'_>,
ctx: &mut ResolveCtx<'_, '_>,
val: &MacTreeSeq,
fallback_pos: Pos,
) -> Option<MacTreeSeq> {
@@ -261,8 +261,8 @@ async fn resolve_seq(
// 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()).await.to_expr().await;
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
let subex = call_body(ctx.h, mac, rule, &state, pos.clone()).await;
new_val.splice(range, [subex]);
}
};
// TODO: Does this glossary refresh actually pay off?
@@ -280,15 +280,30 @@ async fn resolve_seq(
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()).await.to_expr().await;
let subex = call_body(ctx.h, mac, rule, &state, pos.clone()).await;
std::mem::drop(state);
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
new_val.splice(range, [subex]);
}
}
for item in new_val.iter_mut() {
let Some(new) = resolve_one(ctx, item).await else { continue };
*item = new;
any_match = true;
}
/* TODO:
The macro system was previously built on the assumption that whatever is returned by call_body
can never be matched. This is no longer true, but now double-matches are possible. Address this.
*/
any_match.then_some(MacTreeSeq::new(new_val))
}
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr {
async fn call_body(
h: &mut ExecHandle<'_>,
mac: &Macro,
rule: &Rule,
state: &MatchState<'_>,
pos: Pos,
) -> MacTree {
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") {
@@ -297,5 +312,9 @@ async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos
new_atom(MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None)),
});
}
call_v(mac.0.module.suffix([rule.body.clone()]).await, call_args).await.at(pos.clone())
let f_name = mac.0.module.suffix([rule.body.clone()]).await;
match h.exec::<TAtom<MacTree>>(call_v(f_name, call_args)).await {
Err(e) => MacTok::Bottom(e).at(pos),
Ok(mt) => MacTok::Resolved(mt.own().await).at(mt.pos()),
}
}