Files
orchid/orchid-std/src/macros/utils.rs
Lawrence Bethlenfalvy b9f1bb74d7
All checks were successful
Rust / build (push) Successful in 3m34s
Fixed a very nasty deadlock
2026-01-22 20:56:02 +01:00

287 lines
9.4 KiB
Rust

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::interner::is;
use orchid_base::name::{NameLike, Sym, VPath};
use orchid_extension::atom::{Atomic, TAtom};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::ToExpr;
use orchid_extension::gen_expr::{GExpr, sym_ref};
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
use crate::macros::macro_value::{Macro, MacroData, Rule};
use crate::macros::mactree::MacTreeSeq;
use crate::macros::rule::matcher::Matcher;
use crate::{MacTok, MacTree};
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 {
if !self.args.is_empty() {
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>,
) -> 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, &format!("__macro__{name}"), async move |path| {
let module = (Sym::new(path.split_last_seg().1.iter().cloned()).await)
.expect("Default macro in global root");
MemKind::Const(
Macro(Rc::new(MacroData {
canonical_name: module.suffix([is(name).await]).await,
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,
ph_names: placeholders,
body: is(&format!("({name})::{counter}")).await,
})
.await;
}
})
.collect()
.await,
}))
.to_gen()
.await,
)
});
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
lazy(true, &format!("__macro__{kw}"), async move |path| {
let main_const_name = VPath::new(path.split_last_seg().1.iter().cloned())
.name_with_suffix(is(&format!("__macro__{name}")).await)
.to_sym()
.await;
MemKind::Const(sym_ref(main_const_name))
})
});
chain!(main_const, kw_consts, body_consts).collect()
}
}
macro_rules! mactree {
($($body:tt)*) => {
$crate::macros::utils::mactreev!(($($body)*)).items[0].clone()
};
}
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_base::interner::is(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 $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => {
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
name: orchid_base::interner::is(stringify!($name)).await,
kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: true, priority: $prio }
}).at(orchid_base::location::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident "$" $name:ident $($tail:tt)*) => {
$ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{
name: orchid_base::interner::is(stringify!(name)).await,
kind: $crate::macros::mactree::PhKind::Scalar
}).at(orchid_base::location::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $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 $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
$ret.push($arg);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => {
let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else {
panic!("pushv used with non-vec value")
};
for item in body.items.iter() {
$ret.push(item.clone());
}
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
(@RECUR $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => {
$ret.push(MacTok::Lambda(
MacTok::Name($arg).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 "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => {
$ret.push(MacTok::Lambda(
MacTok::Name(sym!($argh $(:: $arg)+).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
).await.expect("Empty string in sym literal in Rust");
$ret.push(
$crate::macros::mactree::MacTok::Name(sym)
.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::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)*);
$ret.push(
$crate::macros::mactree::MacTok::Name(sym)
.at(orchid_base::location::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
() => { Vec::new() };
}
macro_rules! mactreev {
($($tail:tt)*) => {
{
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, mactreev_impl};