287 lines
9.4 KiB
Rust
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};
|