Added support for defining macros in Rust within the macro system

Also fixed a lot of bugs
This commit is contained in:
2025-09-30 21:23:16 +02:00
parent 7971a2b4eb
commit b77653f841
52 changed files with 849 additions and 502 deletions

View File

@@ -0,0 +1,166 @@
use std::rc::Rc;
use async_fn_stream::stream;
use futures::StreamExt;
use itertools::{Itertools, chain};
use orchid_base::name::{NameLike, Sym, VPath};
use orchid_extension::conv::ToExpr;
use orchid_extension::gen_expr::sym_ref;
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
use crate::macros::macro_value::{Macro, MacroData, Matcher, Rule};
use crate::macros::mactree::map_mactree_v;
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
use crate::{MacTok, MacTree};
pub(crate) fn mk_macro<B: ToExpr + Clone + 'static>(
prio: Option<u64>,
own_kws: impl IntoIterator<Item = &'static str>,
rules: impl IntoIterator<Item = (Vec<MacTree>, B)>,
) -> Vec<GenMember> {
let own_kws = own_kws.into_iter().collect_vec();
let name = own_kws[0];
let (patterns, bodies) = rules.into_iter().unzip::<_, _, Vec<Vec<MacTree>>, Vec<B>>();
let main_const = lazy(true, name, async move |path, ctx| {
let module = (Sym::new(path.split_last_seg().1.iter().cloned(), ctx.i()).await)
.expect("Default macro in global root");
MemKind::Const(
Macro(Rc::new(MacroData {
module,
prio,
rules: stream(async |mut h| {
for (counter, pat) in patterns.into_iter().enumerate() {
let mut placeholders = Vec::new();
map_mactree_v(&pat, &mut false, &mut |tt| {
if let MacTok::Ph(ph) = &*tt.tok {
placeholders.push(ph.name.clone())
}
None
});
let pattern = match prio {
Some(_) => Matcher::Priod(PriodMatcher::new(&pat, ctx.i()).await.unwrap()),
None => Matcher::Named(NamedMatcher::new(&pat, ctx.i()).await.unwrap()),
};
h.emit(Rule {
glossary: pat.iter().flat_map(|t| t.glossary()).cloned().collect(),
pattern,
placeholders,
body_name: ctx.i().i(&format!("({name})::{counter}")).await,
})
.await;
}
})
.collect()
.await,
}))
.to_expr()
.await,
)
});
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
lazy(true, kw, async |path, ctx| {
let name = VPath::new(path.split_last_seg().1.iter().cloned())
.name_with_suffix(ctx.i().i(*kw).await)
.to_sym(ctx.i())
.await;
MemKind::Const(sym_ref(name))
})
});
let body_consts = (bodies.into_iter().enumerate())
.flat_map(|(counter, body)| cnst(false, &format!("({name})::{counter}"), body));
chain!(main_const, kw_consts, body_consts).collect()
}
macro_rules! mactree {
($i:expr; $($body:tt)*) => {
$crate::macros::utils::mactreev!($i; ($($body)*)).remove(0)
};
}
macro_rules! mactreev {
(@RECUR $i:expr; $ret:ident) => {};
(@RECUR $i:expr; $ret:ident "..$" $name:ident $prio:literal $($tail:tt)*) => {
ret.push(MacTok::Ph(Ph{
name: i.i(stringify!($name)).await,
kind: PhKind::Vector{ at_least_one: false, priority: $prio }
}).at(Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
$ret.push(MacTok::Ph(Ph{
name: $i.i(stringify!($name)).await,
kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: true, priority: $prio }
}).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "$" $name:ident $($tail:tt)*) => {
$ret.push(MacTok::Ph(Ph{
name: $i.i(stringify!(name)).await,
kind: $crate::macros::mactree::PhKind::Scalar
}).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "'" $arg:expr ; $($tail:tt)*) => {
$ret.push(MacTok::Value($arg).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "" $arg:expr ; $($tail:tt)*) => {
$ret.push($arg);
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
$ret.push(MacTok::Lambda(
MacTok::Value($arg).at(orchid_base::location::Pos::Inherit),
mactreev!(i; $($body)*)
).at(Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "l" $argh:tt $(:: $arg:tt)* ($($body:tt)*) $($tail:tt)*) => {
$ret.push(MacTok::Lambda(
MacTok::Name(sym!($argh $(:: $arg)*; $i).await).at(orchid_base::location::Pos::Inherit),
mactreev!(i; $($body)*)
).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
$ret.push(
MacTok::S(orchid_base::tree::Paren::Round, mactreev!($i; $($body)*))
.at(orchid_base::location::Pos::Inherit)
);
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
$ret.push(
MacTok::S(orchid_base::tree::Paren::Square, mactreev!($i; $($body)*))
.at(orchid_base::location::Pos::Inherit)
);
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident { $($body:tt)* } $($tail:tt)*) => {
$ret.push(
MacTok::S(orchid_base::tree::Paren::Curly, mactreev!($i; $($body)*))
.at(orchid_base::location::Pos::Inherit)
);
mactreev!(@RECUR $i; $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident $nhead:tt $($tail:tt)*) => {
mactreev!(@NAME_MUNCHER $i; $ret ($nhead) $($tail)*)
};
(@NAME_MUNCHER $i:expr; $ret:ident ($($munched:tt)*) :: $name:tt $($tail:tt)*) => {
mactreev!(@NAME_MUNCHER $i; $ret ($($munched)* :: $name) $($tail)*)
};
(@NAME_MUNCHER $i:expr; $ret:ident ($($munched:tt)*) $($tail:tt)*) => {
let sym = orchid_base::sym!($($munched)* ; $i).await;
$ret.push(MacTok::Name(sym).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
};
($i:expr; ) => { Vec::new() };
($i:expr; $($tail:tt)*) => {
{
let mut ret = Vec::<MacTree>::new();
mactreev!(@RECUR $i; ret $($tail)*);
ret
}
};
}
pub(crate) use {mactree, mactreev};