316 lines
10 KiB
Rust
316 lines
10 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::{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<MacTree>;
|
|
|
|
#[derive(Clone)]
|
|
pub struct MacroBodyArgCollector {
|
|
argc: usize,
|
|
args: Args,
|
|
cb: Rc<dyn for<'a> 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::<MacTree>::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<T: TryFromExpr>(&mut self, mt: MacTree) -> OrcRes<T> {
|
|
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<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
|
|
self.handle.exec(val).await
|
|
}
|
|
}
|
|
|
|
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>(
|
|
mut self,
|
|
pat: MacTreeSeq,
|
|
body: impl AsyncFn(RuleCtx, [MacTree; N]) -> OrcRes<MacTree> + '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::<N>().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::<N>().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<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(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;
|