New macro system and stdlib additions

This commit is contained in:
2025-11-21 14:25:03 +01:00
parent b77653f841
commit 603efef28e
230 changed files with 3033 additions and 16640 deletions

View File

@@ -1,166 +1,275 @@
use std::borrow::Cow;
use std::rc::Rc;
use async_fn_stream::stream;
use futures::StreamExt;
use futures::future::LocalBoxFuture;
use itertools::{Itertools, chain};
use never::Never;
use orchid_base::name::{NameLike, Sym, VPath};
use orchid_extension::atom::{Atomic, TAtom};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::context::i;
use orchid_extension::conv::ToExpr;
use orchid_extension::gen_expr::sym_ref;
use orchid_extension::gen_expr::{GExpr, 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::macros::macro_value::{Macro, MacroData, Rule};
use crate::macros::mactree::MacTreeSeq;
use crate::macros::rule::matcher::Matcher;
use crate::{MacTok, MacTree};
pub(crate) fn mk_macro<B: ToExpr + Clone + 'static>(
pub type Args = Vec<MacTree>;
#[derive(Clone)]
pub struct MacroBodyArgCollector {
argc: usize,
args: Args,
cb: Rc<dyn Fn(Args) -> LocalBoxFuture<'static, GExpr>>,
}
impl Atomic for MacroBodyArgCollector {
type Data = ();
type Variant = OwnedVariant;
}
impl OwnedAtom for MacroBodyArgCollector {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: orchid_extension::expr::Expr) -> GExpr {
eprintln!("This is an intermediary value. It should never be copied");
self.clone().call(arg).await
}
async fn call(mut self, arg: orchid_extension::expr::Expr) -> GExpr {
let atom = (TAtom::downcast(arg.handle()).await).unwrap_or_else(|_| {
panic!("This is an intermediary value, the argument types are known in advance")
});
self.args.push(own(&atom).await);
if self.argc == self.args.len() {
(self.cb)(self.args).await.to_gen().await
} else {
self.to_gen().await
}
}
}
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
pub(crate) fn build_macro(
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()
) -> MacroBuilder {
MacroBuilder {
prio,
own_kws: own_kws.into_iter().collect(),
patterns: Vec::new(),
body_consts: Vec::new(),
}
}
pub(crate) struct MacroBuilder {
prio: Option<u64>,
own_kws: Vec<&'static str>,
patterns: Vec<MacTreeSeq>,
body_consts: Vec<GenMember>,
}
impl MacroBuilder {
pub(crate) fn rule<const N: usize, R: ToExpr>(
mut self,
pat: MacTreeSeq,
body: [impl AsyncFn([MacTree; N]) -> R + 'static; 1],
) -> Self {
let [body] = body;
let body = Rc::new(body);
let name = &body_name(self.own_kws[0], self.body_consts.len());
self.body_consts.extend(match N {
0 => lazy(true, name, async move |_| {
let argv = [].into_iter().collect_array().expect("N is 0");
MemKind::Const(body(argv).await.to_gen().await)
}),
1.. => cnst(true, name, MacroBodyArgCollector {
argc: N,
args: Vec::new(),
cb: Rc::new(move |argv| {
let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
let body = body.clone();
Box::pin(async move { body(arr).await.to_gen().await })
}),
}),
});
self.patterns.push(pat);
self
}
pub(crate) fn finish(self) -> Vec<GenMember> {
let Self { own_kws, prio, patterns, body_consts } = self;
let name = own_kws[0];
let main_const = lazy(true, name, async move |path| {
let module = (Sym::new(path.split_last_seg().1.iter().cloned(), &i()).await)
.expect("Default macro in global root");
MemKind::Const(
Macro(Rc::new(MacroData {
module,
prio,
rules: stream(async |mut h| {
for (counter, pattern) in patterns.into_iter().enumerate() {
let mut placeholders = Vec::new();
pattern.map(&mut false, &mut |tt| {
if let MacTok::Ph(ph) = &*tt.tok {
placeholders.push(ph.name.clone())
}
None
});
h.emit(Rule {
matcher: Matcher::new(pattern.clone()).await.unwrap(),
pattern,
placeholders,
body_name: i().i(&format!("({name})::{counter}")).await,
})
.await;
}
})
.collect()
.await,
}))
.to_gen()
.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()
)
});
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
lazy(true, kw, async |path| {
let main_const_name = VPath::new(path.split_last_seg().1.iter().cloned())
.name_with_suffix(i().i(name).await)
.to_sym(&i())
.await;
MemKind::Const(sym_ref(main_const_name))
})
});
chain!(main_const, kw_consts, body_consts).collect()
}
}
macro_rules! mactree {
($i:expr; $($body:tt)*) => {
$crate::macros::utils::mactreev!($i; ($($body)*)).remove(0)
($($body:tt)*) => {
$crate::macros::utils::mactreev!(($($body)*)).items[0].clone()
};
}
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)*);
macro_rules! mactreev_impl {
(@RECUR $ret:ident) => {};
(@RECUR $ret:ident "..$" $name:ident $prio:literal $($tail:tt)*) => {
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
name: orchid_extension::context::i().i(stringify!($name)).await,
kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: false, priority: $prio }
}).at(orchid_base::location::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
$ret.push(MacTok::Ph(Ph{
name: $i.i(stringify!($name)).await,
(@RECUR $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
name: orchid_extension::context::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)*);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "$" $name:ident $($tail:tt)*) => {
$ret.push(MacTok::Ph(Ph{
name: $i.i(stringify!(name)).await,
(@RECUR $ret:ident "$" $name:ident $($tail:tt)*) => {
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
name: orchid_extension::context::i().i(stringify!(name)).await,
kind: $crate::macros::mactree::PhKind::Scalar
}).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
$crate::macros::utils::mactreev_impl!(@RECUR $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 $ret:ident "Val" $arg:expr ; $($tail:tt)*) => {
$ret.push(
$crate::macros::mactree::MacTok::Value($arg)
.at(orchid_base::location::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "" $arg:expr ; $($tail:tt)*) => {
(@RECUR $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
$ret.push($arg);
mactreev!(@RECUR $i; $ret $($tail)*);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
(@RECUR $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)*)
MacTok::Name($arg).at(orchid_base::location::Pos::Inherit),
$crate::macros::utils::mactreev!($($body)*)
).at(orchid_base::location::Pos::Inherit));
mactreev!(@RECUR $i; $ret $($tail)*);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
(@RECUR $ret:ident "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => {
$ret.push(MacTok::Lambda(
MacTok::Name(sym!($argh $(:: $arg)+; orchid_extension::context::i()).await).at(orchid_base::location::Pos::Inherit),
$crate::macros::utils::mactreev!($($body)*)
).at(orchid_base::location::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident $name:literal $($tail:tt)*) => {
assert!(
$name.contains("::"),
"{} was treated as a name, but it doesn't have a namespace prefix",
$name
);
let sym = orchid_base::name::Sym::parse(
$name,
&orchid_extension::context::i()
).await.expect("Empty string in sym literal in Rust");
$ret.push(
MacTok::S(orchid_base::tree::Paren::Round, mactreev!($i; $($body)*))
$crate::macros::mactree::MacTok::Name(sym)
.at(orchid_base::location::Pos::Inherit)
);
mactreev!(@RECUR $i; $ret $($tail)*);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $i:expr; $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
(@RECUR $ret:ident ( $($body:tt)* ) $($tail:tt)*) => {
$ret.push(
MacTok::S(orchid_base::tree::Paren::Square, mactreev!($i; $($body)*))
$crate::macros::mactree::MacTok::S(
orchid_base::tree::Paren::Round,
$crate::macros::utils::mactreev!($($body)*)
)
.at(orchid_base::location::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident [ $($body:tt)* ] $($tail:tt)*) => {
$ret.push(
$crate::macros::mactree::MacTok::S(
orchid_base::tree::Paren::Square,
$crate::macros::utils::mactreev!($($body)*)
)
.at(orchid_base::location::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident { $($body:tt)* } $($tail:tt)*) => {
$ret.push(
$crate::macros::mactree::MacTok::S(
orchid_base::tree::Paren::Curly,
$crate::macros::utils::mactreev!($($body)*)
)
.at(orchid_base::location::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident $ns:ident :: $nhead:tt $($tail:tt)*) => {
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($ns :: $nhead) $($tail)*)
};
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) :: $name:tt $($tail:tt)*) => {
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($($munched)* :: $name) $($tail)*)
};
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) $($tail:tt)*) => {
let sym = orchid_base::sym!($($munched)* ; orchid_extension::context::i());
$ret.push(
$crate::macros::mactree::MacTok::Name(sym)
.at(orchid_base::location::Pos::Inherit)
);
mactreev!(@RECUR $i; $ret $($tail)*);
$crate::macros::utils::mactreev_impl!(@RECUR $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)*) => {
() => { Vec::new() };
}
macro_rules! mactreev {
($($tail:tt)*) => {
{
let mut ret = Vec::<MacTree>::new();
mactreev!(@RECUR $i; ret $($tail)*);
ret
let mut ret = Vec::<$crate::macros::mactree::MacTree>::new();
ret.extend([]); // silence unneeded mut warning
$crate::macros::utils::mactreev_impl!(@RECUR ret $($tail)*);
$crate::macros::mactree::MacTreeSeq::new(ret)
}
};
}
pub(crate) use {mactree, mactreev};
pub(crate) use {mactree, mactreev, mactreev_impl};