Updated macro system to process and return mactree and then lower it separately to gexpr
Some checks failed
Rust / build (push) Has been cancelled

This commit is contained in:
2026-04-11 11:00:22 +00:00
parent 9b4c7fa7d7
commit b44f3c1832
14 changed files with 460 additions and 469 deletions

View File

@@ -16,7 +16,7 @@ use task_local::task_local;
use crate::gen_expr::GExpr; use crate::gen_expr::GExpr;
use crate::tree::{GenTok, GenTokTree}; use crate::tree::{GenTok, GenTokTree};
use crate::{Expr, ToExpr, api, request, sys_id}; use crate::{ExecHandle, Expr, ToExpr, TryFromExpr, api, exec, request, sys_id};
/// [PTokTree] without [orchid_base::Pos] metadata /// [PTokTree] without [orchid_base::Pos] metadata
pub type PTok = Token<Expr, Never>; pub type PTok = Token<Expr, Never>;
@@ -118,7 +118,7 @@ impl<'a> ParsCtx<'a> {
pub fn module(&self) -> Sym { self.module.clone() } pub fn module(&self) -> Sym { self.module.clone() }
} }
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>; type BoxConstCallback = Box<dyn for<'a> FnOnce(ConstCtx<'a>) -> LocalBoxFuture<'a, GExpr>>;
task_local! { task_local! {
static CONST_TBL: RefCell<HashMap<NonZero<u64>, BoxConstCallback>>; static CONST_TBL: RefCell<HashMap<NonZero<u64>, BoxConstCallback>>;
@@ -143,14 +143,15 @@ impl ParsedLine {
/// Define a constant. The callback is only called later if the constant is /// Define a constant. The callback is only called later if the constant is
/// referenced, and it can call [crate::refl] to base its value on the module /// referenced, and it can call [crate::refl] to base its value on the module
/// tree or use its argument for context-specific queries /// tree or use its argument for context-specific queries
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>( pub fn cnst<'a, R: ToExpr + 'static, F: for<'b> AsyncFnOnce(ConstCtx<'b>) -> R + 'static>(
sr: &SrcRange, sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>, comments: impl IntoIterator<Item = &'a Comment>,
exported: bool, exported: bool,
name: IStr, name: IStr,
f: F, f: F,
) -> Self { ) -> Self {
let cb = Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local()); let cb: BoxConstCallback =
Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local());
let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) }); let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) });
let comments = comments.into_iter().cloned().collect(); let comments = comments.into_iter().cloned().collect();
ParsedLine { comments, sr: sr.clone(), kind } ParsedLine { comments, sr: sr.clone(), kind }
@@ -241,11 +242,11 @@ pub enum ParsedMemKind {
} }
/// Enable a generated constant to query about its environment /// Enable a generated constant to query about its environment
#[derive(Clone)] pub struct ConstCtx<'a> {
pub struct ConstCtx {
constid: api::ParsedConstId, constid: api::ParsedConstId,
exec: ExecHandle<'a>,
} }
impl ConstCtx { impl<'a> ConstCtx<'a> {
/// Resolve a set of local names into the full names they would point to if /// Resolve a set of local names into the full names they would point to if
/// they were globally bound. Errors produced at this stage are soft, as the /// they were globally bound. Errors produced at this stage are soft, as the
/// names may still be found to be locally bound within the expression /// names may still be found to be locally bound within the expression
@@ -272,10 +273,14 @@ impl ConstCtx {
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] { pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match") self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
} }
pub async fn exec<R: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<R> {
self.exec.exec(val).await
}
pub fn handle(&mut self) -> &mut ExecHandle<'a> { &mut self.exec }
} }
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr { pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
let cb = CONST_TBL let cb = CONST_TBL
.with(|ent| ent.borrow_mut().remove(&id.0).expect("Bad ID or double read of parsed const")); .with(|ent| ent.borrow_mut().remove(&id.0).expect("Bad ID or double read of parsed const"));
cb(ConstCtx { constid: id }).await exec(async move |exec| cb(ConstCtx { constid: id, exec }).await).await
} }

View File

@@ -11,9 +11,10 @@ use orchid_extension::{
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr, ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
}; };
use crate::macros::lower::lower;
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq}; use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
use crate::macros::ph_lexer::PhAtom; use crate::macros::ph_lexer::PhAtom;
use crate::macros::resolve::{ArgStack, resolve}; use crate::macros::resolve::resolve;
#[derive(Default)] #[derive(Default)]
pub struct LetLine; pub struct LetLine;
@@ -36,20 +37,20 @@ impl Parser for LetLine {
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?; let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
let aliased = parse_tokv(tail).await; let aliased = parse_tokv(tail).await;
eprintln!("Parsed tokv: {}", fmt(&aliased).await); eprintln!("Parsed tokv: {}", fmt(&aliased).await);
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| { Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |mut ctx| {
let macro_input = let macro_input =
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos()); MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
eprintln!("Dealiased tokv: {}", fmt(&macro_input).await); eprintln!("Dealiased tokv: {}", fmt(&macro_input).await);
let gx = resolve(macro_input, ArgStack::end()).await; let resolved = resolve(ctx.handle(), macro_input).await;
eprintln!("resolves to: {}", fmt(&gx).await); eprintln!("resolves to: {}", fmt(&resolved).await);
let x = gx.create().await; let x = lower(&resolved, substack::Substack::Bottom).await;
eprintln!("created as: {}", fmt(&x).await); eprintln!("created as: {}", fmt(&x).await);
Ok(x) Ok(x)
})]) })])
} }
} }
pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx) -> MacTreeSeq { pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx<'_>) -> MacTreeSeq {
let keys = aliased.glossary().iter().cloned().collect_vec(); let keys = aliased.glossary().iter().cloned().collect_vec();
let mut names: HashMap<_, _> = HashMap::new(); let mut names: HashMap<_, _> = HashMap::new();
let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys))); let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys)));

View File

@@ -0,0 +1,80 @@
use std::collections::VecDeque;
use async_fn_stream::stream;
use futures::{FutureExt, StreamExt, stream};
use itertools::Itertools;
use orchid_base::{OrcErrv, Paren, Sym, fmt, is, mk_errv, report};
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call};
use substack::Substack;
use crate::macros::mactree::MacTreeSeq;
use crate::{MacTok, MacTree};
fn on_err(e: OrcErrv) -> GExpr {
report(e.clone());
bot(e)
}
pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr {
match mt.tok() {
MacTok::Bottom(b) => on_err(b.clone()),
MacTok::Slot => on_err(mk_errv(
is("Lowering intermediary slotted mactree").await,
"Found a Slot during lowering, which should only exist in temporary templates",
[mt.pos()],
)),
MacTok::Ph(ph) => on_err(mk_errv(
is("Lowering placeholder").await,
format!("Found {ph} during lowering. Placeholders should only exist in macro patterns"),
[mt.pos()],
)),
MacTok::S(Paren::Curly | Paren::Square, _) => on_err(mk_errv(
is("Lowering {...} or [...]").await,
format!("Cannot lower this syntax, probable incorrect macro call: {}", fmt(mt).await),
[mt.pos()],
)),
MacTok::S(Paren::Round, b) if b.items.is_empty() => on_err(mk_errv(
is("Cannot lower empty ()").await,
"Attempted to lower empty (). All expressions must have a value.",
[mt.pos()],
)),
MacTok::Lambda(arg, body) => {
let MacTok::Name(n) = arg.tok() else {
return on_err(mk_errv(
is("Lowering lambda with complex param").await,
format!("In code after macros, lambda params can only be names, not {}", fmt(arg).await),
[arg.pos()],
));
};
if body.items.is_empty() {
return on_err(mk_errv(
is("Lowering lambda with empty body").await,
"Lambdas without a body are invalid, all expressions in Orchid must have a value",
[mt.pos()],
));
}
let body = lower_seq(body, args.push(n.clone())).await;
GExprKind::Lambda(Box::new(body)).at(mt.pos())
},
MacTok::S(Paren::Round, body) => lower_seq(body, args).await,
MacTok::Name(n) => match args.iter().find_position(|b| *b == n) {
Some((i, _)) => GExprKind::Arg(i.try_into().unwrap()).at(mt.pos()),
None => n.clone().to_gen().await,
},
MacTok::Value(expr) => expr.clone().to_gen().await,
}
}
pub async fn lower_seq(mtv: &MacTreeSeq, args: Substack<'_, Sym>) -> GExpr {
let mut exprs = stream(async |mut h| {
for mt in mtv.items.iter() {
h.emit(lower(mt, args.clone()).await).await
}
})
.collect::<VecDeque<_>>()
.boxed_local()
.await;
let first = exprs.pop_front().expect("We checked first that it isn't empty");
stream::iter(exprs).fold(first, async |f, x| call(f, x).await).await
}

View File

@@ -1,79 +1,118 @@
use orchid_base::sym; use orchid_extension::gen_expr::new_atom;
use orchid_extension::TAtom;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{TAtom, exec};
use crate::macros::mactree::MacTree; use crate::macros::mactree::MacTree;
use crate::macros::resolve::{ArgStack, resolve}; use crate::macros::resolve::resolve;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::{HomoTpl, UntypedTuple}; use crate::{HomoTpl, UntypedTuple};
pub async fn gen_macro_lib() -> Vec<GenMember> { pub async fn gen_macro_lib() -> Vec<GenMember> {
prefix("macros", [ prefix("macros", [
fun(true, "resolve", async |tpl: TAtom<MacTree>| { fun(true, "resolve", async |tpl: TAtom<MacTree>| {
resolve(tpl.own().await, ArgStack::end()).await exec(async move |mut h| new_atom(resolve(&mut h, tpl.own().await).await)).await
}), }),
prefix("common", [ prefix("common", [
build_macro(None, ["..", "_", "="]).finish(), build_macro(None, ["..", "_", "="]).finish(),
build_macro(Some(1), ["+"]) build_macro(Some(1), ["+"])
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::add::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::+" "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::add::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(Some(1), ["-"]) build_macro(Some(1), ["-"])
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::sub::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::-" "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::sub::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(Some(2), ["*"]) build_macro(Some(2), ["*"])
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::mul::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::*" "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::mul::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(Some(2), ["/"]) build_macro(Some(2), ["/"])
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::div::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::/" "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::div::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(Some(2), ["%"]) build_macro(Some(2), ["%"])
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::mod::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::%" "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::mod::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(Some(3), ["."]) build_macro(Some(3), ["."])
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |cx, [lhs, rhs]| { .rule(
call(sym!(std::ops::get::resolve), (cx.recur(lhs), cx.recur(rhs))).await mactreev!("...$" lhs 1 "macros::common::." "...$" rhs 0),
}]) async |mut cx, [lhs, rhs]| {
Ok(mactree!("std::ops::get::resolve"
"push" cx.recur(lhs).await;
"push" cx.recur(rhs).await;))
},
)
.finish(), .finish(),
build_macro(None, ["comma_list", ","]) build_macro(None, ["comma_list", ","])
.rule( .rule(
mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)), mactreev!("macros::common::comma_list" (
[async |mut cx, [head, tail]| { "...$" head 0 "macros::common::," "...$" tail 1
)),
async |mut cx, [head, tail]| {
let mut tail: HomoTpl<TAtom<MacTree>> = let mut tail: HomoTpl<TAtom<MacTree>> =
cx.exec(cx.recur(mactree!(macros::common::comma_list "push" tail ;))).await?; cx.recur_call(mactree!("macros::common::comma_list" "push" tail ;)).await?;
tail.0.insert(0, cx.exec(new_atom(head)).await?); tail.0.insert(0, cx.exec(new_atom(head)).await?);
Ok(tail) Ok(mactree!("Val" tail;))
}], },
) )
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [ .rule(
async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]), mactreev!("macros::common::comma_list" ( "...$" final_tail 0 )),
]) async |_cx, [tail]| Ok(mactree!("Val" HomoTpl(vec![new_atom(tail)]);)),
.rule(mactreev!(macros::common::comma_list()), [async |_cx, []| UntypedTuple(Vec::new())]) )
.rule(mactreev!("macros::common::comma_list"()), async |_cx, []| {
Ok(mactree!("Val" UntypedTuple(Vec::new());))
})
.finish(), .finish(),
build_macro(None, ["semi_list", ";"]) build_macro(None, ["semi_list", ";"])
.rule( .rule(
mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)), mactreev!("macros::common::semi_list" (
[async |mut cx, [head, tail]| { "...$" head 0 "macros::common::;" "...$" tail 1
)),
async |mut cx, [head, tail]| {
let mut tail: HomoTpl<TAtom<MacTree>> = let mut tail: HomoTpl<TAtom<MacTree>> =
cx.exec(cx.recur(mactree!(macros::common::semi_list "push" tail ;))).await?; cx.recur_call(mactree!("macros::common::semi_list" "push" tail ;)).await?;
tail.0.insert(0, cx.exec(new_atom(head)).await?); tail.0.insert(0, cx.exec(new_atom(head)).await?);
Ok(tail) Ok(mactree!("Val" tail))
}], },
) )
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [ .rule(
async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]), mactreev!("macros::common::semi_list" ( "...$" final_tail 0 )),
]) async |_cx, [tail]| Ok(mactree!("Val" HomoTpl(vec![new_atom(tail)]))),
.rule(mactreev!(macros::common::semi_list()), [async |_cx, []| UntypedTuple(Vec::new())]) )
.rule(mactreev!("macros::common::semi_list"()), async |_cx, []| {
Ok(mactree!("Val" UntypedTuple(Vec::new())))
})
.finish(), .finish(),
]), ]),
]) ])

View File

@@ -13,7 +13,6 @@ use crate::macros::macro_value::Macro;
use crate::macros::mactree_lexer::MacTreeLexer; use crate::macros::mactree_lexer::MacTreeLexer;
use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib}; use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib};
use crate::macros::ph_lexer::{PhAtom, PhLexer}; use crate::macros::ph_lexer::{PhAtom, PhLexer};
use crate::macros::resolve::ArgStack;
use crate::macros::stdlib::gen_std_macro_lib; use crate::macros::stdlib::gen_std_macro_lib;
use crate::macros::utils::MacroBodyArgCollector; use crate::macros::utils::MacroBodyArgCollector;
use crate::{MacTree, StdSystem}; use crate::{MacTree, StdSystem};
@@ -39,7 +38,6 @@ impl SystemCard for MacroSystem {
Some(PhAtom::ops()), Some(PhAtom::ops()),
Some(MacroBodyArgCollector::ops()), Some(MacroBodyArgCollector::ops()),
Some(MatcherAtom::ops()), Some(MatcherAtom::ops()),
Some(ArgStack::ops()),
] ]
} }
} }

View File

@@ -1,93 +1,67 @@
use std::borrow::Cow; use std::borrow::Cow;
use async_fn_stream::stream;
use futures::future::join_all; use futures::future::join_all;
use futures::{Stream, StreamExt, stream}; use futures::{StreamExt, stream};
use never::Never; use never::Never;
use orchid_api_derive::Coding; use orchid_base::{OrcRes, Sym, fmt, is, mk_errv};
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym};
use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom}; use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{ use orchid_extension::{Atomic, ExecHandle, Expr, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
Atomic, ExecHandle, Expr, ExprHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec,
};
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::std::reflection::sym_atom::SymAtom; use crate::std::reflection::sym_atom::SymAtom;
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api}; use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple};
#[derive(Clone, Coding)]
pub struct MatcherData {
keys: Vec<api::TStrv>,
matcher: api::ExprTicket,
}
impl MatcherData {
async fn matcher(&self) -> Expr { Expr::from_handle(ExprHandle::from_ticket(self.matcher).await) }
pub async fn run_matcher(
&self,
h: &mut ExecHandle<'_>,
val: impl ToExpr,
) -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
h.exec::<OrcOpt<HomoTpl<Expr>>>(call(self.matcher().await, val)).await
}
pub fn keys(&self) -> impl Stream<Item = Sym> {
stream(async |mut h| {
for tk in &self.keys {
h.emit(Sym::from_api(*tk).await).await
}
})
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct MatcherAtom { pub struct MatcherAtom {
/// The names that subresults may be bound to /// The names that subresults may be bound to
pub(super) keys: Vec<Sym>, pub(super) keys: Vec<Sym>,
/// Takes the value-to-be-matched, returns an `option (tuple T1..TN)` of the /// Takes the value-to-be-matched, returns an `option (tuple T1..TN)` of the
/// subresults to be bound to the names returned by [Self::keys] /// subresults to be bound to the names returned by [Self::keys]
pub(super) matcher: Expr, pub(super) matcher: MacTree,
} }
impl Atomic for MatcherAtom { impl Atomic for MatcherAtom {
type Data = MatcherData; type Data = ();
type Variant = OwnedVariant; type Variant = OwnedVariant;
} }
impl OwnedAtom for MatcherAtom { impl OwnedAtom for MatcherAtom {
type Refs = Never; type Refs = Never;
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> { async fn val(&self) -> std::borrow::Cow<'_, Self::Data> { Cow::Owned(()) }
Cow::Owned(MatcherData { }
keys: self.keys.iter().map(|t| t.to_api()).collect(),
matcher: self.matcher.handle().ticket(), pub async fn match_one(
}) h: &mut ExecHandle<'_>,
} mat: impl ToExpr,
value: impl ToExpr,
) -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
h.exec::<OrcOpt<HomoTpl<Expr>>>(call(mat, value)).await
} }
pub async fn gen_match_macro_lib() -> Vec<GenMember> { pub async fn gen_match_macro_lib() -> Vec<GenMember> {
prefix("pattern", [ prefix("pattern", [
fun( fun(true, "match_one", async |mat: Expr, value: Expr, then: Expr, default: Expr| {
true, exec(async move |mut h| match match_one(&mut h, mat, value).await? {
"match_one",
async |mat: TAtom<MatcherAtom>, value: Expr, then: Expr, default: Expr| {
exec(async move |mut h| match mat.run_matcher(&mut h, value).await? {
OrcOpt(Some(values)) => Ok(call_v(then, values.0).await), OrcOpt(Some(values)) => Ok(call_v(then, values.0).await),
OrcOpt(None) => Ok(default.to_gen().await), OrcOpt(None) => Ok(default.to_gen().await),
}) })
.await .await
}, }),
), fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: TAtom<MacTree>| {
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| {
new_atom(MatcherAtom { new_atom(MatcherAtom {
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await, keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
matcher, matcher: matcher.own().await,
}) })
}), }),
build_macro(None, ["match", "match_rule", "_row", "=>"]) build_macro(None, ["match", "match_rule", "_row", "=>"])
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [ .rule(
mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }),
async |mut cx, [value, rules]| { async |mut cx, [value, rules]| {
let rule_lines: HomoTpl<TAtom<MacTree>> = let rule_lines: HomoTpl<TAtom<MacTree>> =
cx.exec(cx.recur(mactree!(macros::common::semi_list "push" rules.clone();))).await?; cx.recur_call(mactree!("macros::common::semi_list" "push" rules.clone())).await?;
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new(); let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, TAtom<MacTree>)>::new();
for line_mac in rule_lines.0.iter() { for line_mac in rule_lines.0.iter() {
let Tpl((matcher, body)) = let Tpl((matcher, body)) =
cx.exec(cx.recur(mactree!(pattern::_row "push" line_mac.own().await ;))).await?; cx.recur_call(mactree!("pattern::_row" "push" line_mac.own().await)).await?;
rule_atoms.push((matcher, body)); rule_atoms.push((matcher, body));
} }
let base_case = lam(async |_| { let base_case = lam(async |_| {
@@ -97,30 +71,39 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
[rules.pos()], [rules.pos()],
)) ))
}) })
.await
.create()
.await; .await;
let match_expr = stream::iter(rule_atoms.into_iter().rev()) let match_chain = stream::iter(rule_atoms.into_iter().rev())
.fold(base_case, |tail, (mat, body)| { .fold(mactree!("Val" base_case;), async |tail, (mat, body)| {
lam(async move |x| { mactree!("l" "pattern::x" (
call(sym!(pattern::match_one), (mat, x, body, call(tail, x))).await "pattern::match_one"
}) "Val" mat.ex();
"pattern::x"
"push" body.own().await;
("push" tail;
"pattern::x")
))
}) })
.await; .await;
Ok(call(match_expr, cx.recur(value))) Ok(mactree!("push" match_chain; "push" value))
}, },
]) )
.rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |cx, [pattern]| { .rule(mactreev!("pattern::match_rule" (( "...$" pattern 0 ))), async |mut cx, [pattern]| {
cx.recur(mactree!(pattern::match_rule "push" pattern; )).to_gen().await Ok(cx.recur(mactree!("pattern::match_rule" "push" pattern)).await)
}]) })
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |_cx, []| { .rule(mactreev!("pattern::match_rule"("macros::common::_")), async |_cx, []| {
Ok(new_atom(MatcherAtom { let matcher = new_atom(MatcherAtom {
keys: Vec::new(), keys: Vec::new(),
matcher: lam(async |_| OrcOpt(Some(Tpl(()))).to_gen().await).await.create().await, matcher: mactree!("l" "pattern::unused" ("Val" OrcOpt(Some(Tpl(()))))),
})) });
}]) Ok(mactree!("Val" matcher))
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [ })
async |mut cx, [pattern, mut value]| -> OrcRes<Tpl<(TAtom<MatcherAtom>, _)>> { .rule(
mactreev!("pattern::_row" ( "...$" pattern 0 "pattern::=>" "...$" value 1 )),
async |mut cx, [pattern, mut value]| {
let Ok(pat): OrcRes<TAtom<MatcherAtom>> = let Ok(pat): OrcRes<TAtom<MatcherAtom>> =
cx.exec(cx.recur(mactree!(pattern::match_rule "push" pattern.clone();))).await cx.recur_call(mactree!("pattern::match_rule" "push" pattern.clone())).await
else { else {
return Err(mk_errv( return Err(mk_errv(
is("Invalid pattern").await, is("Invalid pattern").await,
@@ -128,16 +111,16 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
[pattern.pos()], [pattern.pos()],
)); ));
}; };
value = (pat.keys()) value = stream::iter(pat.own().await.keys.iter().cloned())
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; ))) .fold(value, async |value, name| mactree!("l_" name; ( "push" value )))
.await; .await;
Ok(Tpl((pat, cx.recur(value)))) Ok(mactree!("Val" Tpl((pat, new_atom(cx.recur(value).await)))))
}, },
]) )
.finish(), .finish(),
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))), fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
build_macro(None, ["ref"]) build_macro(None, ["ref"])
.rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |_cx, [name]| { .rule(mactreev!("pattern::match_rule"("pattern::ref" "$" name)), async |_cx, [name]| {
let MacTok::Name(name) = name.tok() else { let MacTok::Name(name) = name.tok() else {
return Err(mk_errv( return Err(mk_errv(
is("pattern 'ref' requires a name to bind to").await, is("pattern 'ref' requires a name to bind to").await,
@@ -149,11 +132,12 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
[name.pos()], [name.pos()],
)); ));
}; };
Ok(new_atom(MatcherAtom { let atom = new_atom(MatcherAtom {
keys: vec![name.clone()], keys: vec![name.clone()],
matcher: sym!(pattern::ref_body).to_expr().await, matcher: mactree!("pattern::ref_body"),
})) });
}]) Ok(mactree!("Val" atom))
})
.finish(), .finish(),
]) ])
} }

View File

@@ -1,5 +1,6 @@
mod instantiate_tpl; mod instantiate_tpl;
mod let_line; mod let_line;
pub mod lower;
mod macro_lib; mod macro_lib;
mod macro_line; mod macro_line;
pub mod macro_system; pub mod macro_system;
@@ -8,7 +9,6 @@ pub mod mactree;
mod mactree_lexer; mod mactree_lexer;
pub mod match_macros; pub mod match_macros;
mod ph_lexer; mod ph_lexer;
pub mod postmac;
mod resolve; mod resolve;
mod rule; mod rule;
pub mod stdlib; pub mod stdlib;

View File

@@ -1,53 +0,0 @@
use std::any::TypeId;
use std::borrow::Cow;
use std::marker::PhantomData;
use never::Never;
use orchid_extension::gen_expr::GExpr;
use orchid_extension::{
Atomic, ClonableToExprDyn, MethodSetBuilder, OwnedAtom, OwnedVariant, ToExpr, ToExprFuture,
};
fn assert_not_gexpr<T: 'static>() {
assert_ne!(
TypeId::of::<T>(),
TypeId::of::<GExpr>(),
"Cannot output GExpr, use PostMac::boxed or PostMac::boxed_clone"
);
}
struct PostMacCx<'a>(PhantomData<&'a ()>);
impl PostMacCx<'_> {
pub fn ex<T: ToExpr>(&self, val: PostMac<T>) -> T { val.0 }
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PostMac<T: ToExpr + 'static>(T);
impl<T: ToExpr + 'static> PostMac<T> {
pub fn new(t: T) -> Self {
assert_not_gexpr::<T>();
Self(t)
}
}
impl<T: Future<Output: ToExpr + Clone + 'static> + Clone + 'static> PostMac<ToExprFuture<T>> {
pub fn with(f: impl for<'a> FnOnce(PostMacCx<'a>) -> T + Clone + 'static) -> Self {
assert_not_gexpr::<T>();
PostMac(ToExprFuture(f(PostMacCx(PhantomData))))
}
}
impl<T: ToExpr + Clone + 'static> PostMac<T> {
pub fn atom(self) -> PostMacAtom { PostMac(Box::new(self.0)) }
}
impl<T: ToExpr + 'static> From<T> for PostMac<T> {
fn from(value: T) -> Self { Self(value) }
}
pub type PostMacAtom = PostMac<Box<dyn ClonableToExprDyn>>;
impl Atomic for PostMacAtom {
type Data = ();
type Variant = OwnedVariant;
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for PostMacAtom {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
}

View File

@@ -1,50 +1,18 @@
use std::borrow::Cow;
use std::collections::VecDeque;
use std::ops::{Add, Range}; use std::ops::{Add, Range};
use std::rc::Rc;
use async_fn_stream::stream; use futures::FutureExt;
use futures::{FutureExt, StreamExt, stream};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use orchid_base::{NameLike, Paren, Pos, VPath, fmt, is, log, mk_errv, report};
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv}; use orchid_extension::gen_expr::{GExpr, call_v, new_atom};
use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call, call_v, new_atom}; use orchid_extension::{ExecHandle, ReflMemKind, TAtom, ToExpr, refl};
use orchid_extension::{
Atomic, ExecHandle, OwnedAtom, OwnedVariant, ReflMemKind, TAtom, ToExpr, ToExprFuture, exec, refl,
};
use subslice_offset::SubsliceOffset; use subslice_offset::SubsliceOffset;
use crate::macros::macro_value::{Macro, Rule}; use crate::macros::macro_value::{Macro, Rule};
use crate::macros::mactree::MacTreeSeq; use crate::macros::mactree::MacTreeSeq;
use crate::macros::postmac::{PostMac, PostMacAtom};
use crate::macros::rule::state::{MatchState, StateEntry}; use crate::macros::rule::state::{MatchState, StateEntry};
use crate::{MacTok, MacTree}; use crate::{MacTok, MacTree};
pub enum ArgStackKind {
End,
Cons(Sym, ArgStack),
}
#[derive(Clone)]
pub struct ArgStack {
kind: Rc<ArgStackKind>,
len: usize,
}
impl ArgStack {
pub fn end() -> Self { ArgStack { kind: Rc::new(ArgStackKind::End), len: 0 } }
}
impl Default for ArgStack {
fn default() -> Self { Self::end() }
}
impl Atomic for ArgStack {
type Data = ();
type Variant = OwnedVariant;
}
impl OwnedAtom for ArgStack {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
}
/// # TODO /// # TODO
/// ///
/// convert macro system to return MacTree or otherwise bring it up to /// convert macro system to return MacTree or otherwise bring it up to
@@ -100,7 +68,7 @@ impl OwnedAtom for ArgStack {
/// The best option probably remains for resolve to process and return MacTree, /// The best option probably remains for resolve to process and return MacTree,
/// and for there to be a separate "lower" function. Nothing as yet suggests /// and for there to be a separate "lower" function. Nothing as yet suggests
/// however that macros can't be allowed to return different types /// however that macros can't be allowed to return different types
pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree, arg_stk: ArgStack) -> PostMacAtom { pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree) -> MacTree {
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await; writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await;
let root = refl(); let root = refl();
let mut macros = HashMap::new(); let mut macros = HashMap::new();
@@ -138,8 +106,9 @@ pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree, arg_stk: ArgStack) ->
} }
} }
let mut rctx = ResolveCtx { exclusive, priod }; let mut rctx = ResolveCtx { exclusive, priod };
let gex = resolve_one(&mut rctx, arg_stk, &val).await; let gex = resolve_one(&mut rctx, &val).await.unwrap_or(val.clone());
writeln!(log("debug"), "Macro-resolution over {}", fmt(&val).await).await; writeln!(log("debug"), "Macro-resolution over {} yielded {}", fmt(&val).await, fmt(&gex).await)
.await;
gex gex
} }
@@ -158,54 +127,25 @@ struct ResolveCtx<'a> {
pub priod: Vec<FilteredMacroRecord<'a>>, pub priod: Vec<FilteredMacroRecord<'a>>,
} }
async fn resolve_one(ctx: &mut ResolveCtx<'_>, arg_stk: ArgStack, value: &MacTree) -> PostMacAtom { async fn resolve_one(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTree> {
eprintln!("Resolving unit {}", fmt(value).await); eprintln!("Resolving unit {}", fmt(value).await);
match value.tok() { match value.tok() {
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"), MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
MacTok::Bottom(err) => PostMac::new(bot(err.clone())).atom(), MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
MacTok::Value(v) => {
eprintln!("Found value {}", fmt(v).await);
PostMac::new(v.clone()).atom()
},
MacTok::Name(n) => {
eprintln!("Looking for {n} among [");
let mut cur = &arg_stk;
let mut counter = 0;
while let ArgStackKind::Cons(name, next) = &*cur.kind {
cur = next;
counter += 1;
eprintln!("{name}, ");
if name == n {
return PostMac::new(GExprKind::Arg(counter).at(value.pos())).atom();
}
}
PostMac::new(n.clone()).atom()
},
MacTok::Lambda(arg, body) => { MacTok::Lambda(arg, body) => {
eprintln!("Found lambda \\{} {}", fmt(arg).await, fmt(body).await); let new_arg = resolve_one(ctx, arg).boxed_local().await;
let MacTok::Name(name) = &*arg.tok else { let new_body = resolve_seq(ctx, body, value.pos()).await;
return PostMac::new(bot(mk_errv( if new_arg.is_none() && new_body.is_none() {
is("Syntax error after macros").await, return None;
"This token ends up as a binding, consider replacing it with a name", }
[arg.pos()], let tok = MacTok::Lambda(
))) new_arg.unwrap_or_else(|| arg.clone()),
.atom(); new_body.unwrap_or_else(|| body.clone()),
}; );
let arg_stk = Some(tok.at(value.pos()))
ArgStack { len: arg_stk.len + 1, kind: Rc::new(ArgStackKind::Cons(name.clone(), arg_stk)) };
let body = resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await;
let body2 = body.clone();
let pos = value.pos();
PostMac::with(async |cx| GExprKind::Lambda(Box::new(cx.ex(body).to_gen().await)).at(pos))
.atom()
}, },
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await, MacTok::S(pty, body) =>
MacTok::S(..) => PostMac::new(bot(mk_errv( resolve_seq(ctx, body, value.pos()).await.map(|body| MacTok::S(*pty, body).at(value.pos())),
is("Leftover [] or {} not matched by macro").await,
format!("{} was not matched by any macro", fmt(value).await),
[value.pos()],
)))
.atom(),
} }
} }
@@ -225,18 +165,11 @@ fn subsection<T>(
async fn resolve_seq( async fn resolve_seq(
ctx: &mut ResolveCtx<'_>, ctx: &mut ResolveCtx<'_>,
arg_stk: ArgStack, val: &MacTreeSeq,
val: MacTreeSeq,
fallback_pos: Pos, fallback_pos: Pos,
) -> PostMacAtom { ) -> Option<MacTreeSeq> {
if val.items.is_empty() { if val.items.is_empty() {
return PostMac::new(bot(mk_errv( return None;
is("Empty sequence").await,
"() or (\\arg ) left after macro execution. \
This is usually caused by an incomplete call to a macro with bad error detection",
[fallback_pos],
)))
.atom();
} }
// A sorted collection of overlapping but non-nested matches to exclusive // A sorted collection of overlapping but non-nested matches to exclusive
// macros // macros
@@ -289,6 +222,7 @@ async fn resolve_seq(
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]); x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
} }
} }
let mut any_match = !x_matches.is_empty();
// apply exclusive matches // apply exclusive matches
if !x_matches.is_empty() { if !x_matches.is_empty() {
// ranges of indices into x_matches which setwise conflict with each other. // ranges of indices into x_matches which setwise conflict with each other.
@@ -319,19 +253,19 @@ async fn resolve_seq(
}) })
.reduce(|l, r| l + r); .reduce(|l, r| l + r);
if let Some(error) = error { if let Some(error) = error {
return PostMac::new(bot(error)).atom(); report(error.clone());
return Some(MacTreeSeq::new([MacTok::Bottom(error).at(fallback_pos)]));
} }
// no conflicts, apply all exclusive matches // no conflicts, apply all exclusive matches
for (range, mac, rule, state) in x_matches.into_iter().rev() { for (range, mac, rule, state) in x_matches.into_iter().rev() {
// backwards so that the non-overlapping ranges remain valid // backwards so that the non-overlapping ranges remain valid
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add)) let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
.expect("All macro rules must contain at least one locally defined name"); .expect("All macro rules must contain at least one locally defined name");
let subex = let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
new_val.splice(range, [MacTok::Value(subex).at(pos)]); new_val.splice(range, [MacTok::Value(subex).at(pos)]);
} }
}; };
// Does this glossary refresh actually pay off? // TODO: Does this glossary refresh actually pay off?
let top_glossary = (new_val.iter()) let top_glossary = (new_val.iter())
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None }) .flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
@@ -342,41 +276,20 @@ async fn resolve_seq(
continue; continue;
} }
let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue }; let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue };
any_match = true;
let range = pre.len()..new_val.len() - suf.len(); let range = pre.len()..new_val.len() - suf.len();
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add)) let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
.expect("All macro rules must contain at least one locally defined name"); .expect("All macro rules must contain at least one locally defined name");
let subex = let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
std::mem::drop(state); std::mem::drop(state);
new_val.splice(range, [MacTok::Value(subex).at(pos)]); new_val.splice(range, [MacTok::Value(subex).at(pos)]);
} }
} }
let mut exprs = stream(async |mut h| { any_match.then_some(MacTreeSeq::new(new_val))
for mt in new_val {
h.emit(resolve_one(ctx, arg_stk.clone(), &mt).await).await
}
})
.collect::<VecDeque<_>>()
.boxed_local()
.await;
let first = exprs.pop_front().expect(
"We checked first that it isn't empty, and named macros get replaced with their results",
);
PostMac::with(async move |cx| {
stream::iter(exprs).fold(cx.ex(first), async |f, x| call(f, cx.ex(x)).await).await
})
.await
.atom()
} }
async fn mk_body_call( async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr {
mac: &Macro, let mut call_args = vec![];
rule: &Rule,
state: &MatchState<'_>,
pos: Pos,
arg_stk: ArgStack,
) -> GExpr {
let mut call_args = vec![new_atom(arg_stk).at(Pos::None)];
for name in rule.ph_names.iter() { for name in rule.ph_names.iter() {
call_args.push(match state.get(name).expect("Missing state entry for placeholder") { call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
StateEntry::Scalar(scal) => new_atom((**scal).clone()), StateEntry::Scalar(scal) => new_atom((**scal).clone()),

View File

@@ -4,8 +4,11 @@ use crate::macros::utils::{build_macro, mactree, mactreev};
pub async fn gen_functional_macro_lib() -> Vec<GenMember> { pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
prefix("std::fn", [build_macro(Some(4), ["|>"]) prefix("std::fn", [build_macro(Some(4), ["|>"])
.rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [ .rule(
async |cx, [lhs, fun, rhs]| cx.recur(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)), mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0),
]) async |mut cx, [lhs, fun, rhs]| {
Ok(cx.recur(mactree!("push" fun ; "push" lhs ; "pushv" rhs)).await)
},
)
.finish()]) .finish()])
} }

View File

@@ -1,19 +1,17 @@
use futures::StreamExt; use orchid_extension::gen_expr::new_atom;
use orchid_base::sym;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Expr, TAtom, ToExpr, exec}; use orchid_extension::{Expr, TAtom, exec};
use crate::macros::match_macros::MatcherAtom; use crate::macros::match_macros::{MatcherAtom, match_one};
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::{OrcOpt, Tpl}; use crate::{OrcOpt, Tpl};
pub async fn gen_option_macro_lib() -> Vec<GenMember> { pub async fn gen_option_macro_lib() -> Vec<GenMember> {
prefix("std::option", [ prefix("std::option", [
fun(false, "is_some_body", |sub: TAtom<MatcherAtom>, val: OrcOpt<Expr>| { fun(false, "is_some_body", |sub: Expr, val: OrcOpt<Expr>| {
exec(async move |mut h| { exec(async move |mut h| {
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) }; let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
sub.run_matcher(&mut h, sub_val).await match_one(&mut h, sub, sub_val).await
}) })
}), }),
fun( fun(
@@ -24,22 +22,25 @@ pub async fn gen_option_macro_lib() -> Vec<GenMember> {
}, },
), ),
build_macro(None, ["some", "none"]) build_macro(None, ["some", "none"])
.rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [ .rule(
mactreev!("pattern::match_rule" ( "std::option::some" "...$" sub_pattern 0)),
async |mut cx, [sub]| { async |mut cx, [sub]| {
let sub: TAtom<MatcherAtom> = let sub: TAtom<MatcherAtom> =
cx.exec(cx.recur(mactree!(pattern::match_rule "push" sub;))).await?; cx.recur_call(mactree!("pattern::match_rule" "push" sub;)).await?;
Ok(new_atom(MatcherAtom { let sub = sub.own().await;
keys: sub.keys().collect().await, let matcher = new_atom(MatcherAtom {
matcher: call(sym!(std::option::is_some_body), sub).await.create().await, keys: sub.keys,
})) matcher: mactree!("std::option::is_some_body" "push" sub.matcher),
});
Ok(mactree!("Val" matcher))
}, },
]) )
.rule(mactreev!(pattern::match_rule(std::option::none)), [async |_cx, []| { .rule(mactreev!("pattern::match_rule"("std::option::none")), async |_cx, []| {
new_atom(MatcherAtom { Ok(mactree!("Val" new_atom(MatcherAtom {
keys: vec![], keys: vec![],
matcher: sym!(std::option::is_none_body).to_expr().await, matcher: mactree!("std::option::is_none_body"),
})))
}) })
}])
.finish(), .finish(),
]) ])
} }

View File

@@ -1,27 +1,31 @@
use orchid_base::sym; use orchid_extension::TAtom;
use orchid_extension::gen_expr::call; use orchid_extension::gen_expr::new_atom;
use orchid_extension::tree::{GenMember, prefix}; use orchid_extension::tree::{GenMember, prefix};
use orchid_extension::{Expr, TAtom, ToExpr};
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::std::string::str_atom::IntStrAtom;
use crate::{HomoTpl, MacTree, Tpl}; use crate::{HomoTpl, MacTree, Tpl};
pub async fn gen_record_macro_lib() -> Vec<GenMember> { pub async fn gen_record_macro_lib() -> Vec<GenMember> {
prefix("std::record", [build_macro(None, ["r", "_row"]) prefix("std::record", [build_macro(None, ["r", "_row"])
.rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |mut cx, [elements]| { .rule(mactreev!("std::record::r"[ "...$" elements 0 ]), async |mut cx, [elements]| {
let tup: HomoTpl<TAtom<MacTree>> = let tup: HomoTpl<TAtom<MacTree>> =
cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?; cx.recur_call(mactree!(("macros::common::comma_list" "push" elements))).await?;
let mut record = sym!(std::record::empty).to_gen().await; let mut record = mactree!("std::record::empty");
for item_exprh in tup.0 { for item_exprh in tup.0 {
let Tpl((key, value)): Tpl<(TAtom<IntStrAtom>, Expr)> = let Tpl((key, value)): Tpl<(TAtom<MacTree>, TAtom<MacTree>)> =
cx.exec(cx.recur(mactree!(std::record::_row "push" item_exprh.own().await ;))).await?; cx.recur_call(mactree!("std::record::_row" "push" item_exprh.own().await)).await?;
record = call(sym!(std::record::set), (record, key, value)).await; record = mactree!("std::record::set"
"push" record;
"push" key.own().await;
"push" value.own().await);
} }
Ok(record) Ok(record)
}]) })
.rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |cx, [name, value]| { .rule(
Ok(Tpl((cx.recur(name), cx.recur(value)))) mactreev!("std::record::_row" ( "$" name "...$" value 1 )),
}]) async |mut cx, [name, value]| {
Ok(mactree!("Val" Tpl((new_atom(cx.recur(name).await), new_atom(cx.recur(value).await)))))
},
)
.finish()]) .finish()])
} }

View File

@@ -1,48 +1,52 @@
use async_fn_stream::stream;
use futures::{StreamExt, stream}; use futures::{StreamExt, stream};
use orchid_base::{OrcRes, sym}; use orchid_base::{OrcRes, sym};
use orchid_extension::gen_expr::{GExpr, call, new_atom}; use orchid_extension::gen_expr::{GExpr, call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Expr, TAtom, ToExpr, exec}; use orchid_extension::{Expr, TAtom, ToExpr, exec};
use crate::macros::match_macros::MatcherAtom; use crate::macros::match_macros::{MatcherAtom, match_one};
use crate::macros::utils::{RuleCtx, build_macro, mactree, mactreev}; use crate::macros::utils::{RuleCtx, build_macro, mactree, mactreev};
use crate::{HomoTpl, MacTree, OrcOpt}; use crate::{HomoTpl, MacTree, OrcOpt};
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> { pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
prefix("std::tuple", [ prefix("std::tuple", [
build_macro(None, ["t"]) build_macro(None, ["t"])
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [async |mut cx, [elements]| { .rule(mactreev!("std::tuple::t" [ "...$" elements 0 ]), async |mut cx, [elements]| {
let tup: HomoTpl<TAtom<MacTree>> = cx let tup: HomoTpl<TAtom<MacTree>> =
.exec(cx.recur( cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
mactree!((macros::common::comma_list "push" elements ;)), let val = stream(async |mut h| {
)) for item in &tup.0[..] {
.await?; h.emit(cx.recur(item.own().await).await).await
let val = stream::iter(&tup.0[..]) }
.fold(sym!(std::tuple::empty).to_gen().await, async |head, new| { })
call(sym!(std::tuple::cat), ( .fold(mactree!("std::tuple::empty"), async |head, new| {
head, mactree!(
call(sym!(std::tuple::one), call( "std::tuple::cat"
sym!(macros::resolve), "push" head;
new.clone(), ("std::tuple::one"
)), "push" new)
)).await )
}) })
.await; .await;
Ok(val) Ok(val)
}]) })
.rule( .rule(
mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::, macros::common::..])), mactreev!("pattern::match_rule"(
[async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!(macros::common::_))).await], "std::tuple::t"[ "...$" elements 0 "macros::common::," "macros::common::.."]
)
.rule(
mactreev!(pattern::match_rule(
std::tuple::t[ "...$" elements 1 macros::common::, macros::common::.. "...$" tail 0]
)), )),
[async |cx, [elements, tail]| parse_tpl(cx, elements, Some(tail)).await], async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!("macros::common::_"))).await,
) )
.rule(mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0])), [ .rule(
mactreev!("pattern::match_rule"(
"std::tuple::t"[ "...$" elements 1 "macros::common::," "macros::common::.." "...$" tail 0]
)),
async |cx, [elements, tail]| parse_tpl(cx, elements, Some(tail)).await,
)
.rule(
mactreev!("pattern::match_rule"("std::tuple::t"[ "...$" elements 0])),
async |cx, [elements]| parse_tpl(cx, elements, None).await, async |cx, [elements]| parse_tpl(cx, elements, None).await,
]) )
.finish(), .finish(),
fun(false, "matcher_body", tuple_matcher_body), fun(false, "matcher_body", tuple_matcher_body),
]) ])
@@ -52,34 +56,38 @@ async fn parse_tpl(
mut cx: RuleCtx<'_>, mut cx: RuleCtx<'_>,
elements: MacTree, elements: MacTree,
tail_matcher: Option<MacTree>, tail_matcher: Option<MacTree>,
) -> OrcRes<GExpr> { ) -> OrcRes<MacTree> {
let tup: HomoTpl<TAtom<MacTree>> = let tup: HomoTpl<TAtom<MacTree>> =
cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?; cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
let mut subs = Vec::with_capacity(tup.0.len()); let mut keys = Vec::new();
let mut sub_matchers = mactree!("std::tuple::empty");
for mac_a in &tup.0[..] { for mac_a in &tup.0[..] {
let sub: TAtom<MatcherAtom> = let sub: TAtom<MatcherAtom> =
cx.exec(cx.recur(mactree!(pattern::match_rule ("push" mac_a.own().await ;)))).await?; cx.recur_call(mactree!("pattern::match_rule" ("push" mac_a.own().await))).await?;
subs.push(sub); let owned = sub.own().await;
keys.extend(owned.keys);
sub_matchers =
mactree!("std::tuple::cat" "push" sub_matchers; ("std::tuple::one" "push" owned.matcher));
} }
let tail_matcher: Option<TAtom<MatcherAtom>> = match tail_matcher { let tail_matcher = match tail_matcher {
Some(mac) => Some(cx.exec(cx.recur(mactree!(pattern::match_rule "push" mac ;))).await?), Some(mac) => {
None => None, let atm: TAtom<MatcherAtom> =
cx.recur_call(mactree!("pattern::match_rule" "push" mac)).await?;
let owned = atm.own().await;
keys.extend(owned.keys);
mactree!("std::option::some" "push" owned.matcher)
},
None => mactree!("std::option::none"),
}; };
Ok(new_atom(MatcherAtom { Ok(mactree!("Val" new_atom(MatcherAtom {
keys: stream::iter(&subs[..]) keys,
.flat_map(|t| t.keys()) matcher: mactree!("std::tuple::matcher_body" "push" sub_matchers; "push" tail_matcher),
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys())) })))
.collect()
.await,
matcher: call(sym!(std::tuple::matcher_body), (HomoTpl(subs), OrcOpt(tail_matcher)))
.to_expr()
.await,
}))
} }
fn tuple_matcher_body( fn tuple_matcher_body(
children: HomoTpl<TAtom<MatcherAtom>>, children: HomoTpl<Expr>,
tail: OrcOpt<TAtom<MatcherAtom>>, tail: OrcOpt<Expr>,
value: HomoTpl<Expr>, value: HomoTpl<Expr>,
) -> impl Future<Output = GExpr> { ) -> impl Future<Output = GExpr> {
exec(async move |mut h| -> OrcRes<OrcOpt<HomoTpl<Expr>>> { exec(async move |mut h| -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
@@ -88,7 +96,7 @@ fn tuple_matcher_body(
} }
let mut binds = Vec::new(); let mut binds = Vec::new();
for (sub_mat, sub_val) in children.0.iter().zip(&value.0) { for (sub_mat, sub_val) in children.0.iter().zip(&value.0) {
match sub_mat.run_matcher(&mut h, sub_val.clone()).await? { match match_one(&mut h, sub_mat.clone(), sub_val.clone()).await? {
OrcOpt(None) => return Ok(OrcOpt(None)), OrcOpt(None) => return Ok(OrcOpt(None)),
OrcOpt(Some(subres)) => binds.extend(subres.0), OrcOpt(Some(subres)) => binds.extend(subres.0),
} }
@@ -102,7 +110,7 @@ fn tuple_matcher_body(
call(sym!(std::tuple::cat), (prefix, new.clone())).await call(sym!(std::tuple::cat), (prefix, new.clone())).await
}) })
.await; .await;
match tail_mat.run_matcher(&mut h, tail_tpl).await? { match match_one(&mut h, tail_mat, tail_tpl).await? {
OrcOpt(Some(tail_binds)) => binds.extend(tail_binds.0), OrcOpt(Some(tail_binds)) => binds.extend(tail_binds.0),
OrcOpt(None) => return Ok(OrcOpt(None)), OrcOpt(None) => return Ok(OrcOpt(None)),
} }

View File

@@ -8,14 +8,16 @@ use itertools::{Itertools, chain};
use never::Never; use never::Never;
use orchid_base::{NameLike, OrcRes, Sym, VPath, is}; use orchid_base::{NameLike, OrcRes, Sym, VPath, is};
use orchid_extension::gen_expr::{GExpr, new_atom}; use orchid_extension::gen_expr::{GExpr, new_atom};
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy}; use orchid_extension::tree::{GenMember, MemKind, lazy};
use orchid_extension::{ use orchid_extension::{
Atomic, ExecHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, ToExprFuture, TryFromExpr, exec, 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::macro_value::{Macro, MacroData, Rule};
use crate::macros::mactree::MacTreeSeq; use crate::macros::mactree::MacTreeSeq;
use crate::macros::resolve::{ArgStack, resolve}; use crate::macros::resolve::resolve;
use crate::macros::rule::matcher::Matcher; use crate::macros::rule::matcher::Matcher;
use crate::{MacTok, MacTree}; use crate::{MacTok, MacTree};
@@ -24,7 +26,6 @@ pub type Args = Vec<MacTree>;
#[derive(Clone)] #[derive(Clone)]
pub struct MacroBodyArgCollector { pub struct MacroBodyArgCollector {
argc: usize, argc: usize,
arg_stack: Option<ArgStack>,
args: Args, args: Args,
cb: Rc<dyn for<'a> Fn(RuleCtx<'a>, Args) -> LocalBoxFuture<'a, GExpr>>, cb: Rc<dyn for<'a> Fn(RuleCtx<'a>, Args) -> LocalBoxFuture<'a, GExpr>>,
} }
@@ -42,20 +43,13 @@ impl OwnedAtom for MacroBodyArgCollector {
self.clone().call(arg).await self.clone().call(arg).await
} }
async fn call(mut self, arg: orchid_extension::Expr) -> GExpr { async fn call(mut self, arg: orchid_extension::Expr) -> GExpr {
if self.arg_stack.is_none() {
let atom = (TAtom::<ArgStack>::downcast(arg.handle()).await).unwrap_or_else(|_| {
panic!("The argstack is an intermediary value, the argument types are known in advance")
});
self.arg_stack = Some(atom.own().await);
} else {
let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| { let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| {
panic!("This is an intermediary value, the argument types are known in advance") panic!("This is an intermediary value, the argument types are known in advance")
}); });
self.args.push(atom.own().await); self.args.push(atom.own().await);
}
if self.argc == self.args.len() { if self.argc == self.args.len() {
exec(async move |handle| { exec(async move |handle| {
let rule_ctx = RuleCtx { arg_stk: self.arg_stack.unwrap(), handle }; let rule_ctx = RuleCtx { handle };
(self.cb)(rule_ctx, self.args).await (self.cb)(rule_ctx, self.args).await
}) })
.await .await
@@ -68,15 +62,26 @@ impl OwnedAtom for MacroBodyArgCollector {
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") } fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
pub struct RuleCtx<'a> { pub struct RuleCtx<'a> {
arg_stk: ArgStack,
handle: ExecHandle<'a>, handle: ExecHandle<'a>,
} }
impl RuleCtx<'_> { impl RuleCtx<'_> {
pub fn recur(&self, mt: MacTree) -> impl ToExpr + use<> { /// Recursively resolve a subexpression
pub async fn recur(&mut self, mt: MacTree) -> MacTree {
eprintln!("Recursion in macro"); eprintln!("Recursion in macro");
let arg_stk = self.arg_stk.clone(); resolve(&mut self.handle, mt).await
ToExprFuture(resolve(mt, arg_stk))
} }
/// 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> { pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
self.handle.exec(val).await self.handle.exec(val).await
} }
@@ -100,28 +105,33 @@ pub(crate) struct MacroBuilder {
body_consts: Vec<GenMember>, body_consts: Vec<GenMember>,
} }
impl MacroBuilder { impl MacroBuilder {
pub(crate) fn rule<const N: usize, R: ToExpr>( pub(crate) fn rule<const N: usize>(
mut self, mut self,
pat: MacTreeSeq, pat: MacTreeSeq,
body: [impl AsyncFn(RuleCtx, [MacTree; N]) -> R + 'static; 1], body: impl AsyncFn(RuleCtx, [MacTree; N]) -> OrcRes<MacTree> + 'static,
) -> Self { ) -> Self {
let [body] = body;
let body = Rc::new(body); let body = Rc::new(body);
let name = &body_name(self.own_kws[0], self.body_consts.len()); let name = &body_name(self.own_kws[0], self.body_consts.len());
self.body_consts.extend(cnst( self.body_consts.extend(lazy(true, name, async |_| {
true, MemKind::Const(if N == 0 {
name, 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 { new_atom(MacroBodyArgCollector {
argc: N, argc: N,
arg_stack: None,
args: Vec::new(), args: Vec::new(),
cb: Rc::new(move |rec, argv| { cb: Rc::new(move |rec, argv| {
let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length"); let arr =
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
let body = body.clone(); let body = body.clone();
Box::pin(async move { body(rec, arr).await.to_gen().await }) Box::pin(async move { body(rec, arr).await.map(new_atom).to_gen().await })
}), }),
}), })
)); })
}));
self.patterns.push(pat); self.patterns.push(pat);
self self
} }
@@ -199,17 +209,26 @@ macro_rules! mactreev_impl {
}).at(orchid_base::Pos::Inherit)); }).at(orchid_base::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $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)*) => { (@RECUR $ret:ident "Val" $arg:expr ; $($tail:tt)*) => {
$ret.push( $ret.push(
$crate::macros::mactree::MacTok::Value($arg) $crate::macros::mactree::MacTok::Value(orchid_extension::ToExpr::to_expr($arg).await)
.at(orchid_base::Pos::Inherit) .at(orchid_base::Pos::Inherit)
); );
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $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)*) => { (@RECUR $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
$ret.push($arg); $ret.push($arg);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $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)*) => { (@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => {
let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else { let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else {
panic!("pushv used with non-vec value") panic!("pushv used with non-vec value")
@@ -226,9 +245,15 @@ macro_rules! mactreev_impl {
).at(orchid_base::Pos::Inherit)); ).at(orchid_base::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
}; };
(@RECUR $ret:ident "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => { (@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( $ret.push(MacTok::Lambda(
MacTok::Name(sym!($argh $(:: $arg)+).await).at(orchid_base::Pos::Inherit), MacTok::Name(sym).at(orchid_base::Pos::Inherit),
$crate::macros::utils::mactreev!($($body)*) $crate::macros::utils::mactreev!($($body)*)
).at(orchid_base::Pos::Inherit)); ).at(orchid_base::Pos::Inherit));
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
@@ -239,12 +264,9 @@ macro_rules! mactreev_impl {
"{} was treated as a name, but it doesn't have a namespace prefix", "{} was treated as a name, but it doesn't have a namespace prefix",
$name $name
); );
let sym = orchid_base::Sym::parse( let sym = orchid_base::Sym::parse($name).await.expect("Empty string in sym literal in Rust");
$name
).await.expect("Empty string in sym literal in Rust");
$ret.push( $ret.push(
$crate::macros::mactree::MacTok::Name(sym) $crate::macros::mactree::MacTok::Name(sym).at(orchid_base::Pos::Inherit)
.at(orchid_base::Pos::Inherit)
); );
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
}; };
@@ -278,20 +300,6 @@ macro_rules! mactreev_impl {
); );
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); $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::Pos::Inherit)
);
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
};
() => { Vec::new() }; () => { Vec::new() };
} }
macro_rules! mactreev { macro_rules! mactreev {