Updated macro system to process and return mactree and then lower it separately to gexpr
Some checks failed
Rust / build (push) Has been cancelled
Some checks failed
Rust / build (push) Has been cancelled
This commit is contained in:
@@ -11,9 +11,10 @@ use orchid_extension::{
|
||||
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
|
||||
};
|
||||
|
||||
use crate::macros::lower::lower;
|
||||
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||
use crate::macros::ph_lexer::PhAtom;
|
||||
use crate::macros::resolve::{ArgStack, resolve};
|
||||
use crate::macros::resolve::resolve;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LetLine;
|
||||
@@ -36,20 +37,20 @@ impl Parser for LetLine {
|
||||
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
|
||||
let aliased = parse_tokv(tail).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 =
|
||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
||||
eprintln!("Dealiased tokv: {}", fmt(¯o_input).await);
|
||||
let gx = resolve(macro_input, ArgStack::end()).await;
|
||||
eprintln!("resolves to: {}", fmt(&gx).await);
|
||||
let x = gx.create().await;
|
||||
let resolved = resolve(ctx.handle(), macro_input).await;
|
||||
eprintln!("resolves to: {}", fmt(&resolved).await);
|
||||
let x = lower(&resolved, substack::Substack::Bottom).await;
|
||||
eprintln!("created as: {}", fmt(&x).await);
|
||||
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 mut names: HashMap<_, _> = HashMap::new();
|
||||
let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys)));
|
||||
|
||||
80
orchid-std/src/macros/lower.rs
Normal file
80
orchid-std/src/macros/lower.rs
Normal 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
|
||||
}
|
||||
@@ -1,79 +1,118 @@
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::TAtom;
|
||||
use orchid_extension::gen_expr::{call, new_atom};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use orchid_extension::{TAtom, exec};
|
||||
|
||||
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::{HomoTpl, UntypedTuple};
|
||||
|
||||
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||
prefix("macros", [
|
||||
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", [
|
||||
build_macro(None, ["..", "_", "="]).finish(),
|
||||
build_macro(Some(1), ["+"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::add::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(Some(1), ["-"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::sub::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(Some(2), ["*"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::mul::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(Some(2), ["/"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::div::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(Some(2), ["%"])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::mod::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(Some(3), ["."])
|
||||
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |cx, [lhs, rhs]| {
|
||||
call(sym!(std::ops::get::resolve), (cx.recur(lhs), cx.recur(rhs))).await
|
||||
}])
|
||||
.rule(
|
||||
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(),
|
||||
build_macro(None, ["comma_list", ","])
|
||||
.rule(
|
||||
mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)),
|
||||
[async |mut cx, [head, tail]| {
|
||||
mactreev!("macros::common::comma_list" (
|
||||
"...$" head 0 "macros::common::," "...$" tail 1
|
||||
)),
|
||||
async |mut cx, [head, tail]| {
|
||||
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?);
|
||||
Ok(tail)
|
||||
}],
|
||||
Ok(mactree!("Val" tail;))
|
||||
},
|
||||
)
|
||||
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [
|
||||
async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]),
|
||||
])
|
||||
.rule(mactreev!(macros::common::comma_list()), [async |_cx, []| UntypedTuple(Vec::new())])
|
||||
.rule(
|
||||
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, []| {
|
||||
Ok(mactree!("Val" UntypedTuple(Vec::new());))
|
||||
})
|
||||
.finish(),
|
||||
build_macro(None, ["semi_list", ";"])
|
||||
.rule(
|
||||
mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)),
|
||||
[async |mut cx, [head, tail]| {
|
||||
mactreev!("macros::common::semi_list" (
|
||||
"...$" head 0 "macros::common::;" "...$" tail 1
|
||||
)),
|
||||
async |mut cx, [head, tail]| {
|
||||
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?);
|
||||
Ok(tail)
|
||||
}],
|
||||
Ok(mactree!("Val" tail))
|
||||
},
|
||||
)
|
||||
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [
|
||||
async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]),
|
||||
])
|
||||
.rule(mactreev!(macros::common::semi_list()), [async |_cx, []| UntypedTuple(Vec::new())])
|
||||
.rule(
|
||||
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, []| {
|
||||
Ok(mactree!("Val" UntypedTuple(Vec::new())))
|
||||
})
|
||||
.finish(),
|
||||
]),
|
||||
])
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::macros::macro_value::Macro;
|
||||
use crate::macros::mactree_lexer::MacTreeLexer;
|
||||
use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib};
|
||||
use crate::macros::ph_lexer::{PhAtom, PhLexer};
|
||||
use crate::macros::resolve::ArgStack;
|
||||
use crate::macros::stdlib::gen_std_macro_lib;
|
||||
use crate::macros::utils::MacroBodyArgCollector;
|
||||
use crate::{MacTree, StdSystem};
|
||||
@@ -39,7 +38,6 @@ impl SystemCard for MacroSystem {
|
||||
Some(PhAtom::ops()),
|
||||
Some(MacroBodyArgCollector::ops()),
|
||||
Some(MatcherAtom::ops()),
|
||||
Some(ArgStack::ops()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +1,67 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::future::join_all;
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use futures::{StreamExt, stream};
|
||||
use never::Never;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym};
|
||||
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv};
|
||||
use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use orchid_extension::{
|
||||
Atomic, ExecHandle, Expr, ExprHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec,
|
||||
};
|
||||
use orchid_extension::{Atomic, ExecHandle, Expr, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
|
||||
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
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)]
|
||||
pub struct MatcherAtom {
|
||||
/// The names that subresults may be bound to
|
||||
pub(super) keys: Vec<Sym>,
|
||||
/// 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]
|
||||
pub(super) matcher: Expr,
|
||||
pub(super) matcher: MacTree,
|
||||
}
|
||||
impl Atomic for MatcherAtom {
|
||||
type Data = MatcherData;
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for MatcherAtom {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> {
|
||||
Cow::Owned(MatcherData {
|
||||
keys: self.keys.iter().map(|t| t.to_api()).collect(),
|
||||
matcher: self.matcher.handle().ticket(),
|
||||
})
|
||||
}
|
||||
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
|
||||
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> {
|
||||
prefix("pattern", [
|
||||
fun(
|
||||
true,
|
||||
"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(None) => Ok(default.to_gen().await),
|
||||
})
|
||||
.await
|
||||
},
|
||||
),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| {
|
||||
fun(true, "match_one", async |mat: Expr, value: Expr, then: Expr, default: Expr| {
|
||||
exec(async move |mut h| match match_one(&mut h, mat, value).await? {
|
||||
OrcOpt(Some(values)) => Ok(call_v(then, values.0).await),
|
||||
OrcOpt(None) => Ok(default.to_gen().await),
|
||||
})
|
||||
.await
|
||||
}),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: TAtom<MacTree>| {
|
||||
new_atom(MatcherAtom {
|
||||
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", "=>"])
|
||||
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [
|
||||
.rule(
|
||||
mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }),
|
||||
async |mut cx, [value, rules]| {
|
||||
let rule_lines: HomoTpl<TAtom<MacTree>> =
|
||||
cx.exec(cx.recur(mactree!(macros::common::semi_list "push" rules.clone();))).await?;
|
||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
|
||||
cx.recur_call(mactree!("macros::common::semi_list" "push" rules.clone())).await?;
|
||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, TAtom<MacTree>)>::new();
|
||||
for line_mac in rule_lines.0.iter() {
|
||||
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));
|
||||
}
|
||||
let base_case = lam(async |_| {
|
||||
@@ -97,30 +71,39 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
[rules.pos()],
|
||||
))
|
||||
})
|
||||
.await
|
||||
.create()
|
||||
.await;
|
||||
let match_expr = stream::iter(rule_atoms.into_iter().rev())
|
||||
.fold(base_case, |tail, (mat, body)| {
|
||||
lam(async move |x| {
|
||||
call(sym!(pattern::match_one), (mat, x, body, call(tail, x))).await
|
||||
})
|
||||
let match_chain = stream::iter(rule_atoms.into_iter().rev())
|
||||
.fold(mactree!("Val" base_case;), async |tail, (mat, body)| {
|
||||
mactree!("l" "pattern::x" (
|
||||
"pattern::match_one"
|
||||
"Val" mat.ex();
|
||||
"pattern::x"
|
||||
"push" body.own().await;
|
||||
("push" tail;
|
||||
"pattern::x")
|
||||
))
|
||||
})
|
||||
.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]| {
|
||||
cx.recur(mactree!(pattern::match_rule "push" pattern; )).to_gen().await
|
||||
}])
|
||||
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |_cx, []| {
|
||||
Ok(new_atom(MatcherAtom {
|
||||
)
|
||||
.rule(mactreev!("pattern::match_rule" (( "...$" pattern 0 ))), async |mut cx, [pattern]| {
|
||||
Ok(cx.recur(mactree!("pattern::match_rule" "push" pattern)).await)
|
||||
})
|
||||
.rule(mactreev!("pattern::match_rule"("macros::common::_")), async |_cx, []| {
|
||||
let matcher = new_atom(MatcherAtom {
|
||||
keys: Vec::new(),
|
||||
matcher: lam(async |_| OrcOpt(Some(Tpl(()))).to_gen().await).await.create().await,
|
||||
}))
|
||||
}])
|
||||
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
|
||||
async |mut cx, [pattern, mut value]| -> OrcRes<Tpl<(TAtom<MatcherAtom>, _)>> {
|
||||
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]| {
|
||||
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 {
|
||||
return Err(mk_errv(
|
||||
is("Invalid pattern").await,
|
||||
@@ -128,16 +111,16 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
[pattern.pos()],
|
||||
));
|
||||
};
|
||||
value = (pat.keys())
|
||||
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
|
||||
value = stream::iter(pat.own().await.keys.iter().cloned())
|
||||
.fold(value, async |value, name| mactree!("l_" name; ( "push" value )))
|
||||
.await;
|
||||
Ok(Tpl((pat, cx.recur(value))))
|
||||
Ok(mactree!("Val" Tpl((pat, new_atom(cx.recur(value).await)))))
|
||||
},
|
||||
])
|
||||
)
|
||||
.finish(),
|
||||
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
|
||||
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 {
|
||||
return Err(mk_errv(
|
||||
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()],
|
||||
));
|
||||
};
|
||||
Ok(new_atom(MatcherAtom {
|
||||
let atom = new_atom(MatcherAtom {
|
||||
keys: vec![name.clone()],
|
||||
matcher: sym!(pattern::ref_body).to_expr().await,
|
||||
}))
|
||||
}])
|
||||
matcher: mactree!("pattern::ref_body"),
|
||||
});
|
||||
Ok(mactree!("Val" atom))
|
||||
})
|
||||
.finish(),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod instantiate_tpl;
|
||||
mod let_line;
|
||||
pub mod lower;
|
||||
mod macro_lib;
|
||||
mod macro_line;
|
||||
pub mod macro_system;
|
||||
@@ -8,7 +9,6 @@ pub mod mactree;
|
||||
mod mactree_lexer;
|
||||
pub mod match_macros;
|
||||
mod ph_lexer;
|
||||
pub mod postmac;
|
||||
mod resolve;
|
||||
mod rule;
|
||||
pub mod stdlib;
|
||||
|
||||
@@ -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(()) }
|
||||
}
|
||||
@@ -1,50 +1,18 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Add, Range};
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::{FutureExt, StreamExt, stream};
|
||||
use futures::FutureExt;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv};
|
||||
use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call, call_v, new_atom};
|
||||
use orchid_extension::{
|
||||
Atomic, ExecHandle, OwnedAtom, OwnedVariant, ReflMemKind, TAtom, ToExpr, ToExprFuture, exec, refl,
|
||||
};
|
||||
use orchid_base::{NameLike, Paren, Pos, VPath, fmt, is, log, mk_errv, report};
|
||||
use orchid_extension::gen_expr::{GExpr, call_v, new_atom};
|
||||
use orchid_extension::{ExecHandle, ReflMemKind, TAtom, ToExpr, refl};
|
||||
use subslice_offset::SubsliceOffset;
|
||||
|
||||
use crate::macros::macro_value::{Macro, Rule};
|
||||
use crate::macros::mactree::MacTreeSeq;
|
||||
use crate::macros::postmac::{PostMac, PostMacAtom};
|
||||
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||
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
|
||||
///
|
||||
/// 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,
|
||||
/// and for there to be a separate "lower" function. Nothing as yet suggests
|
||||
/// 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;
|
||||
let root = refl();
|
||||
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 gex = resolve_one(&mut rctx, arg_stk, &val).await;
|
||||
writeln!(log("debug"), "Macro-resolution over {}", fmt(&val).await).await;
|
||||
let gex = resolve_one(&mut rctx, &val).await.unwrap_or(val.clone());
|
||||
writeln!(log("debug"), "Macro-resolution over {} yielded {}", fmt(&val).await, fmt(&gex).await)
|
||||
.await;
|
||||
gex
|
||||
}
|
||||
|
||||
@@ -158,54 +127,25 @@ struct ResolveCtx<'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);
|
||||
match value.tok() {
|
||||
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
||||
MacTok::Bottom(err) => PostMac::new(bot(err.clone())).atom(),
|
||||
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::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
|
||||
MacTok::Lambda(arg, body) => {
|
||||
eprintln!("Found lambda \\{} {}", fmt(arg).await, fmt(body).await);
|
||||
let MacTok::Name(name) = &*arg.tok else {
|
||||
return PostMac::new(bot(mk_errv(
|
||||
is("Syntax error after macros").await,
|
||||
"This token ends up as a binding, consider replacing it with a name",
|
||||
[arg.pos()],
|
||||
)))
|
||||
.atom();
|
||||
};
|
||||
let arg_stk =
|
||||
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()
|
||||
let new_arg = resolve_one(ctx, arg).boxed_local().await;
|
||||
let new_body = resolve_seq(ctx, body, value.pos()).await;
|
||||
if new_arg.is_none() && new_body.is_none() {
|
||||
return None;
|
||||
}
|
||||
let tok = MacTok::Lambda(
|
||||
new_arg.unwrap_or_else(|| arg.clone()),
|
||||
new_body.unwrap_or_else(|| body.clone()),
|
||||
);
|
||||
Some(tok.at(value.pos()))
|
||||
},
|
||||
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await,
|
||||
MacTok::S(..) => PostMac::new(bot(mk_errv(
|
||||
is("Leftover [] or {} not matched by macro").await,
|
||||
format!("{} was not matched by any macro", fmt(value).await),
|
||||
[value.pos()],
|
||||
)))
|
||||
.atom(),
|
||||
MacTok::S(pty, body) =>
|
||||
resolve_seq(ctx, body, value.pos()).await.map(|body| MacTok::S(*pty, body).at(value.pos())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,18 +165,11 @@ fn subsection<T>(
|
||||
|
||||
async fn resolve_seq(
|
||||
ctx: &mut ResolveCtx<'_>,
|
||||
arg_stk: ArgStack,
|
||||
val: MacTreeSeq,
|
||||
val: &MacTreeSeq,
|
||||
fallback_pos: Pos,
|
||||
) -> PostMacAtom {
|
||||
) -> Option<MacTreeSeq> {
|
||||
if val.items.is_empty() {
|
||||
return PostMac::new(bot(mk_errv(
|
||||
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();
|
||||
return None;
|
||||
}
|
||||
// A sorted collection of overlapping but non-nested matches to exclusive
|
||||
// macros
|
||||
@@ -289,6 +222,7 @@ async fn resolve_seq(
|
||||
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
|
||||
}
|
||||
}
|
||||
let mut any_match = !x_matches.is_empty();
|
||||
// apply exclusive matches
|
||||
if !x_matches.is_empty() {
|
||||
// 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);
|
||||
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
|
||||
for (range, mac, rule, state) in x_matches.into_iter().rev() {
|
||||
// backwards so that the non-overlapping ranges remain valid
|
||||
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");
|
||||
let subex =
|
||||
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
|
||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
||||
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())
|
||||
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
|
||||
.collect::<HashSet<_>>();
|
||||
@@ -342,41 +276,20 @@ async fn resolve_seq(
|
||||
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 pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
|
||||
.expect("All macro rules must contain at least one locally defined name");
|
||||
let subex =
|
||||
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
|
||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
||||
std::mem::drop(state);
|
||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
||||
}
|
||||
}
|
||||
let mut exprs = stream(async |mut h| {
|
||||
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()
|
||||
any_match.then_some(MacTreeSeq::new(new_val))
|
||||
}
|
||||
|
||||
async fn mk_body_call(
|
||||
mac: &Macro,
|
||||
rule: &Rule,
|
||||
state: &MatchState<'_>,
|
||||
pos: Pos,
|
||||
arg_stk: ArgStack,
|
||||
) -> GExpr {
|
||||
let mut call_args = vec![new_atom(arg_stk).at(Pos::None)];
|
||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr {
|
||||
let mut call_args = vec![];
|
||||
for name in rule.ph_names.iter() {
|
||||
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||
StateEntry::Scalar(scal) => new_atom((**scal).clone()),
|
||||
|
||||
@@ -4,8 +4,11 @@ use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
|
||||
pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::fn", [build_macro(Some(4), ["|>"])
|
||||
.rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [
|
||||
async |cx, [lhs, fun, rhs]| cx.recur(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)),
|
||||
])
|
||||
.rule(
|
||||
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()])
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
use futures::StreamExt;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::gen_expr::{call, new_atom};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
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::{OrcOpt, Tpl};
|
||||
|
||||
pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
||||
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| {
|
||||
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(
|
||||
@@ -24,22 +22,25 @@ pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
||||
},
|
||||
),
|
||||
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]| {
|
||||
let sub: TAtom<MatcherAtom> =
|
||||
cx.exec(cx.recur(mactree!(pattern::match_rule "push" sub;))).await?;
|
||||
Ok(new_atom(MatcherAtom {
|
||||
keys: sub.keys().collect().await,
|
||||
matcher: call(sym!(std::option::is_some_body), sub).await.create().await,
|
||||
}))
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" sub;)).await?;
|
||||
let sub = sub.own().await;
|
||||
let matcher = new_atom(MatcherAtom {
|
||||
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, []| {
|
||||
new_atom(MatcherAtom {
|
||||
)
|
||||
.rule(mactreev!("pattern::match_rule"("std::option::none")), async |_cx, []| {
|
||||
Ok(mactree!("Val" new_atom(MatcherAtom {
|
||||
keys: vec![],
|
||||
matcher: sym!(std::option::is_none_body).to_expr().await,
|
||||
})
|
||||
}])
|
||||
matcher: mactree!("std::option::is_none_body"),
|
||||
})))
|
||||
})
|
||||
.finish(),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::gen_expr::call;
|
||||
use orchid_extension::TAtom;
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::tree::{GenMember, prefix};
|
||||
use orchid_extension::{Expr, TAtom, ToExpr};
|
||||
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::std::string::str_atom::IntStrAtom;
|
||||
use crate::{HomoTpl, MacTree, Tpl};
|
||||
|
||||
pub async fn gen_record_macro_lib() -> Vec<GenMember> {
|
||||
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>> =
|
||||
cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?;
|
||||
let mut record = sym!(std::record::empty).to_gen().await;
|
||||
cx.recur_call(mactree!(("macros::common::comma_list" "push" elements))).await?;
|
||||
let mut record = mactree!("std::record::empty");
|
||||
for item_exprh in tup.0 {
|
||||
let Tpl((key, value)): Tpl<(TAtom<IntStrAtom>, Expr)> =
|
||||
cx.exec(cx.recur(mactree!(std::record::_row "push" item_exprh.own().await ;))).await?;
|
||||
record = call(sym!(std::record::set), (record, key, value)).await;
|
||||
let Tpl((key, value)): Tpl<(TAtom<MacTree>, TAtom<MacTree>)> =
|
||||
cx.recur_call(mactree!("std::record::_row" "push" item_exprh.own().await)).await?;
|
||||
record = mactree!("std::record::set"
|
||||
"push" record;
|
||||
"push" key.own().await;
|
||||
"push" value.own().await);
|
||||
}
|
||||
Ok(record)
|
||||
}])
|
||||
.rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |cx, [name, value]| {
|
||||
Ok(Tpl((cx.recur(name), cx.recur(value))))
|
||||
}])
|
||||
})
|
||||
.rule(
|
||||
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()])
|
||||
}
|
||||
|
||||
@@ -1,85 +1,93 @@
|
||||
use async_fn_stream::stream;
|
||||
use futures::{StreamExt, stream};
|
||||
use orchid_base::{OrcRes, sym};
|
||||
use orchid_extension::gen_expr::{GExpr, call, new_atom};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
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::{HomoTpl, MacTree, OrcOpt};
|
||||
|
||||
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::tuple", [
|
||||
build_macro(None, ["t"])
|
||||
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [async |mut cx, [elements]| {
|
||||
let tup: HomoTpl<TAtom<MacTree>> = cx
|
||||
.exec(cx.recur(
|
||||
mactree!((macros::common::comma_list "push" elements ;)),
|
||||
))
|
||||
.await?;
|
||||
let val = stream::iter(&tup.0[..])
|
||||
.fold(sym!(std::tuple::empty).to_gen().await, async |head, new| {
|
||||
call(sym!(std::tuple::cat), (
|
||||
head,
|
||||
call(sym!(std::tuple::one), call(
|
||||
sym!(macros::resolve),
|
||||
new.clone(),
|
||||
)),
|
||||
)).await
|
||||
})
|
||||
.await;
|
||||
Ok(val)
|
||||
}])
|
||||
.rule(
|
||||
mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::, macros::common::..])),
|
||||
[async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!(macros::common::_))).await],
|
||||
)
|
||||
.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,
|
||||
])
|
||||
.finish(),
|
||||
fun(false, "matcher_body", tuple_matcher_body),
|
||||
])
|
||||
build_macro(None, ["t"])
|
||||
.rule(mactreev!("std::tuple::t" [ "...$" elements 0 ]), async |mut cx, [elements]| {
|
||||
let tup: HomoTpl<TAtom<MacTree>> =
|
||||
cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
|
||||
let val = stream(async |mut h| {
|
||||
for item in &tup.0[..] {
|
||||
h.emit(cx.recur(item.own().await).await).await
|
||||
}
|
||||
})
|
||||
.fold(mactree!("std::tuple::empty"), async |head, new| {
|
||||
mactree!(
|
||||
"std::tuple::cat"
|
||||
"push" head;
|
||||
("std::tuple::one"
|
||||
"push" new)
|
||||
)
|
||||
})
|
||||
.await;
|
||||
Ok(val)
|
||||
})
|
||||
.rule(
|
||||
mactreev!("pattern::match_rule"(
|
||||
"std::tuple::t"[ "...$" elements 0 "macros::common::," "macros::common::.."]
|
||||
)),
|
||||
async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!("macros::common::_"))).await,
|
||||
)
|
||||
.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,
|
||||
)
|
||||
.finish(),
|
||||
fun(false, "matcher_body", tuple_matcher_body),
|
||||
])
|
||||
}
|
||||
|
||||
async fn parse_tpl(
|
||||
mut cx: RuleCtx<'_>,
|
||||
elements: MacTree,
|
||||
tail_matcher: Option<MacTree>,
|
||||
) -> OrcRes<GExpr> {
|
||||
) -> OrcRes<MacTree> {
|
||||
let tup: HomoTpl<TAtom<MacTree>> =
|
||||
cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?;
|
||||
let mut subs = Vec::with_capacity(tup.0.len());
|
||||
cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
|
||||
let mut keys = Vec::new();
|
||||
let mut sub_matchers = mactree!("std::tuple::empty");
|
||||
for mac_a in &tup.0[..] {
|
||||
let sub: TAtom<MatcherAtom> =
|
||||
cx.exec(cx.recur(mactree!(pattern::match_rule ("push" mac_a.own().await ;)))).await?;
|
||||
subs.push(sub);
|
||||
cx.recur_call(mactree!("pattern::match_rule" ("push" mac_a.own().await))).await?;
|
||||
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 {
|
||||
Some(mac) => Some(cx.exec(cx.recur(mactree!(pattern::match_rule "push" mac ;))).await?),
|
||||
None => None,
|
||||
let tail_matcher = match tail_matcher {
|
||||
Some(mac) => {
|
||||
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 {
|
||||
keys: stream::iter(&subs[..])
|
||||
.flat_map(|t| t.keys())
|
||||
.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,
|
||||
}))
|
||||
Ok(mactree!("Val" new_atom(MatcherAtom {
|
||||
keys,
|
||||
matcher: mactree!("std::tuple::matcher_body" "push" sub_matchers; "push" tail_matcher),
|
||||
})))
|
||||
}
|
||||
|
||||
fn tuple_matcher_body(
|
||||
children: HomoTpl<TAtom<MatcherAtom>>,
|
||||
tail: OrcOpt<TAtom<MatcherAtom>>,
|
||||
children: HomoTpl<Expr>,
|
||||
tail: OrcOpt<Expr>,
|
||||
value: HomoTpl<Expr>,
|
||||
) -> impl Future<Output = GExpr> {
|
||||
exec(async move |mut h| -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
||||
@@ -88,7 +96,7 @@ fn tuple_matcher_body(
|
||||
}
|
||||
let mut binds = Vec::new();
|
||||
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(Some(subres)) => binds.extend(subres.0),
|
||||
}
|
||||
@@ -102,7 +110,7 @@ fn tuple_matcher_body(
|
||||
call(sym!(std::tuple::cat), (prefix, new.clone())).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(None) => return Ok(OrcOpt(None)),
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@ 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, cnst, lazy};
|
||||
use orchid_extension::tree::{GenMember, MemKind, lazy};
|
||||
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::mactree::MacTreeSeq;
|
||||
use crate::macros::resolve::{ArgStack, resolve};
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::rule::matcher::Matcher;
|
||||
use crate::{MacTok, MacTree};
|
||||
|
||||
@@ -24,7 +26,6 @@ pub type Args = Vec<MacTree>;
|
||||
#[derive(Clone)]
|
||||
pub struct MacroBodyArgCollector {
|
||||
argc: usize,
|
||||
arg_stack: Option<ArgStack>,
|
||||
args: Args,
|
||||
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
|
||||
}
|
||||
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(|_| {
|
||||
panic!("This is an intermediary value, the argument types are known in advance")
|
||||
});
|
||||
self.args.push(atom.own().await);
|
||||
}
|
||||
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 { arg_stk: self.arg_stack.unwrap(), handle };
|
||||
let rule_ctx = RuleCtx { handle };
|
||||
(self.cb)(rule_ctx, self.args).await
|
||||
})
|
||||
.await
|
||||
@@ -68,15 +62,26 @@ impl OwnedAtom for MacroBodyArgCollector {
|
||||
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
|
||||
|
||||
pub struct RuleCtx<'a> {
|
||||
arg_stk: ArgStack,
|
||||
handle: ExecHandle<'a>,
|
||||
}
|
||||
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");
|
||||
let arg_stk = self.arg_stk.clone();
|
||||
ToExprFuture(resolve(mt, arg_stk))
|
||||
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
|
||||
}
|
||||
@@ -100,28 +105,33 @@ pub(crate) struct MacroBuilder {
|
||||
body_consts: Vec<GenMember>,
|
||||
}
|
||||
impl MacroBuilder {
|
||||
pub(crate) fn rule<const N: usize, R: ToExpr>(
|
||||
pub(crate) fn rule<const N: usize>(
|
||||
mut self,
|
||||
pat: MacTreeSeq,
|
||||
body: [impl AsyncFn(RuleCtx, [MacTree; N]) -> R + 'static; 1],
|
||||
body: impl AsyncFn(RuleCtx, [MacTree; N]) -> OrcRes<MacTree> + 'static,
|
||||
) -> 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(cnst(
|
||||
true,
|
||||
name,
|
||||
new_atom(MacroBodyArgCollector {
|
||||
argc: N,
|
||||
arg_stack: None,
|
||||
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.to_gen().await })
|
||||
}),
|
||||
}),
|
||||
));
|
||||
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
|
||||
}
|
||||
@@ -199,17 +209,26 @@ macro_rules! mactreev_impl {
|
||||
}).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($arg)
|
||||
$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")
|
||||
@@ -226,9 +245,15 @@ macro_rules! mactreev_impl {
|
||||
).at(orchid_base::Pos::Inherit));
|
||||
$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(
|
||||
MacTok::Name(sym!($argh $(:: $arg)+).await).at(orchid_base::Pos::Inherit),
|
||||
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)*);
|
||||
@@ -239,12 +264,9 @@ macro_rules! mactreev_impl {
|
||||
"{} 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");
|
||||
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::mactree::MacTok::Name(sym).at(orchid_base::Pos::Inherit)
|
||||
);
|
||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||
};
|
||||
@@ -278,20 +300,6 @@ macro_rules! mactreev_impl {
|
||||
);
|
||||
$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() };
|
||||
}
|
||||
macro_rules! mactreev {
|
||||
|
||||
Reference in New Issue
Block a user