New macro system and stdlib additions
This commit is contained in:
178
orchid-std/src/macros/match_macros.rs
Normal file
178
orchid-std/src/macros/match_macros.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use futures::future::join_all;
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use never::Never;
|
||||
use orchid_api::ExprTicket;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::fmt;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::sym;
|
||||
use orchid_extension::atom::{Atomic, TAtom};
|
||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||
use orchid_extension::context::i;
|
||||
use orchid_extension::conv::ToExpr;
|
||||
use orchid_extension::coroutine_exec::{ExecHandle, exec};
|
||||
use orchid_extension::expr::{Expr, ExprHandle};
|
||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
|
||||
use crate::macros::resolve::resolve;
|
||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||
use crate::std::reflection::sym_atom::SymAtom;
|
||||
use crate::std::tuple::Tuple;
|
||||
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api};
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
pub struct MatcherData {
|
||||
keys: Vec<api::TStrv>,
|
||||
matcher: 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.to_gen().await, [val.to_gen().await]))
|
||||
.await
|
||||
}
|
||||
pub fn keys(&self) -> impl Stream<Item = Sym> {
|
||||
stream(async |mut h| {
|
||||
for tk in &self.keys {
|
||||
h.emit(Sym::from_api(*tk, &i()).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,
|
||||
}
|
||||
impl Atomic for MatcherAtom {
|
||||
type Data = MatcherData;
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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(then.to_gen().await, join_all(values.0.into_iter().map(|x| x.to_gen())).await)),
|
||||
OrcOpt(None) => Ok(default.to_gen().await),
|
||||
})
|
||||
.await
|
||||
},
|
||||
),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| MatcherAtom {
|
||||
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0, &i()).await)).await,
|
||||
matcher,
|
||||
}),
|
||||
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
||||
.rule(mactreev!("pattern::match" { "..$" rules 0 }), [async |[rules]| {
|
||||
exec(async move |mut h| {
|
||||
let rule_lines = h
|
||||
.exec::<TAtom<Tuple>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
|
||||
for line_exprh in rule_lines.iter() {
|
||||
let line_mac = h
|
||||
.exec::<TAtom<MacTree>>(Expr::from_handle(ExprHandle::from_ticket(*line_exprh).await))
|
||||
.await?;
|
||||
let Tpl((matcher, body)) = h
|
||||
.exec(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::_row "push" own(&line_mac).await ;).to_gen().await,
|
||||
]))
|
||||
.await?;
|
||||
rule_atoms.push((matcher, body));
|
||||
}
|
||||
let base_case = lambda(0, [bot(mk_errv(
|
||||
i().i("No branches match").await,
|
||||
"None of the pattern provided matches this value",
|
||||
[rules.pos()],
|
||||
))]);
|
||||
let match_expr = stream::iter(rule_atoms.into_iter().rev())
|
||||
.fold(base_case, async |tail, (mat, body)| {
|
||||
lambda(0, [call(sym_ref(sym!(pattern::match_one; i())), [
|
||||
mat.to_gen().await,
|
||||
arg(0),
|
||||
body.to_gen().await,
|
||||
call(tail, [arg(0)]),
|
||||
])])
|
||||
})
|
||||
.await;
|
||||
Ok(match_expr)
|
||||
})
|
||||
.await
|
||||
}])
|
||||
.rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |[pattern]| {
|
||||
resolve(mactree!(pattern::match_rule "push" pattern; )).await
|
||||
}])
|
||||
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
|
||||
async |[pattern, mut value]| {
|
||||
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
|
||||
let Ok(pat) = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
mactree!(pattern::match_rule "push" pattern.clone();).to_gen().await,
|
||||
]))
|
||||
.await
|
||||
else {
|
||||
return Err(mk_errv(
|
||||
i().i("Invalid pattern").await,
|
||||
format!("Could not parse {} as a match pattern", fmt(&pattern, &i()).await),
|
||||
[pattern.pos()],
|
||||
));
|
||||
};
|
||||
value = (pat.keys())
|
||||
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
|
||||
.await;
|
||||
Ok(Tpl((pat, resolve(value).await)))
|
||||
})
|
||||
.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 |[name]| {
|
||||
let MacTok::Name(name) = name.tok() else {
|
||||
return Err(mk_errv(
|
||||
i().i("pattern 'ref' requires a name to bind to").await,
|
||||
format!(
|
||||
"'ref' was interpreted as a binding matcher, \
|
||||
but it was followed by {} instead of a name",
|
||||
fmt(&name, &i()).await
|
||||
),
|
||||
[name.pos()],
|
||||
));
|
||||
};
|
||||
Ok(MatcherAtom {
|
||||
keys: vec![name.clone()],
|
||||
matcher: sym_ref(sym!(pattern::ref_body; i())).to_expr().await,
|
||||
})
|
||||
}])
|
||||
.finish(),
|
||||
])
|
||||
}
|
||||
Reference in New Issue
Block a user