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::{NameLike, OrcRes, Sym, VPath, is}; use orchid_extension::gen_expr::{GExpr, new_atom}; use orchid_extension::tree::{GenMember, MemKind, lazy}; use orchid_extension::{ Atomic, ExecHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, TryFromExpr, exec, }; use substack::Substack; use crate::macros::lower::lower; use crate::macros::macro_value::{Macro, MacroData, Rule}; use crate::macros::mactree::MacTreeSeq; use crate::macros::resolve::resolve; 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 Fn(RuleCtx<'a>, Args) -> LocalBoxFuture<'a, 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) -> 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) -> 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(atom.own().await); if self.argc == self.args.len() { exec(async move |handle| { let rule_ctx = RuleCtx { handle }; (self.cb)(rule_ctx, self.args).await }) .await } else { new_atom(self) } } } fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") } pub struct RuleCtx<'a> { handle: ExecHandle<'a>, } impl RuleCtx<'_> { /// Recursively resolve a subexpression pub async fn recur(&mut self, mt: MacTree) -> MacTree { resolve(&mut self.handle, mt).await } /// Recursively resolve a value from a delegate macro which is expected to /// match only keywords and return any subexpressions in a datastructure /// /// If this is used with syntax that matches an expression macro, names bound /// in the enclosing scope will not be correctly matched and the conversion at /// the end will fail. pub async fn recur_call(&mut self, mt: MacTree) -> OrcRes { let resolved = self.recur(mt).await; let lowered = lower(&resolved, Substack::Bottom).await; self.exec(lowered).await } /// Normalize a value, run an expression to completion. pub async fn exec(&mut self, val: impl ToExpr) -> OrcRes { self.handle.exec(val).await } } 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(RuleCtx, [MacTree; N]) -> OrcRes + 'static, ) -> Self { let body = Rc::new(body); let name = &body_name(self.own_kws[0], self.body_consts.len()); self.body_consts.extend(lazy(true, name, async |_| { MemKind::Const(if N == 0 { exec(async move |handle| { let empty = std::iter::empty().collect_array::().unwrap(); Ok(new_atom(body(RuleCtx { handle }, empty).await?)) }) .await } else { new_atom(MacroBodyArgCollector { argc: N, args: Vec::new(), cb: Rc::new(move |rec, argv| { let arr = argv.into_iter().collect_array::().expect("argc should enforce the length"); let body = body.clone(); Box::pin(async move { body(rec, arr).await.map(new_atom).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(new_atom(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, })))) }); 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(main_const_name.to_gen().await) }) }); 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::is(stringify!($name)).await, kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: false, priority: $prio } }).at(orchid_base::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::is(stringify!($name)).await, kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: true, priority: $prio } }).at(orchid_base::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::is(stringify!(name)).await, kind: $crate::macros::mactree::PhKind::Scalar }).at(orchid_base::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; (@RECUR $ret:ident "Val" $arg:expr) => { $crate::macros::utils::mactreev_impl!(@RECUR $ret "Val" $arg ;) }; (@RECUR $ret:ident "Val" $arg:expr ; $($tail:tt)*) => { $ret.push( $crate::macros::mactree::MacTok::Value(orchid_extension::ToExpr::to_expr($arg).await) .at(orchid_base::Pos::Inherit) ); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; (@RECUR $ret:ident "push" $arg:expr) => { $crate::macros::utils::mactreev_impl!(@RECUR $ret "push" $arg ;) }; (@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) => { $crate::macros::utils::mactreev_impl!(@RECUR $ret "pushv" $arg ;) }; (@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::Pos::Inherit), $crate::macros::utils::mactreev!($($body)*) ).at(orchid_base::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; (@RECUR $ret:ident "l" $arg:literal ($($body:tt)*) $($tail:tt)*) => { assert!( $arg.contains("::"), "{} was treated as a name, but it doesn't have a namespace prefix", $arg ); let sym = orchid_base::Sym::parse($arg).await.expect("Empty string in sym literal in Rust"); $ret.push(MacTok::Lambda( MacTok::Name(sym).at(orchid_base::Pos::Inherit), $crate::macros::utils::mactreev!($($body)*) ).at(orchid_base::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::Sym::parse($name).await.expect("Empty string in sym literal in Rust"); $ret.push( $crate::macros::mactree::MacTok::Name(sym).at(orchid_base::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::Paren::Round, $crate::macros::utils::mactreev!($($body)*) ) .at(orchid_base::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::Paren::Square, $crate::macros::utils::mactreev!($($body)*) ) .at(orchid_base::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::Paren::Curly, $crate::macros::utils::mactreev!($($body)*) ) .at(orchid_base::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; pub(crate) use mactreev; pub(crate) use mactreev_impl;