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; #[derive(Clone)] pub struct MacroBodyArgCollector { argc: usize, args: Args, cb: Rc 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, own_kws: impl IntoIterator, ) -> MacroBuilder { MacroBuilder { prio, own_kws: own_kws.into_iter().collect(), patterns: Vec::new(), body_consts: Vec::new(), } } pub(crate) struct MacroBuilder { prio: Option, own_kws: Vec<&'static str>, patterns: Vec, body_consts: Vec, } impl MacroBuilder { pub(crate) fn rule( 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::().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 { 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};