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, match_one}; use crate::macros::utils::{RuleCtx, build_macro, mactree, mactreev}; use crate::{HomoTpl, MacTree, OrcOpt}; pub async fn gen_tuple_macro_lib() -> Vec { prefix("std::tuple", [ build_macro(None, ["t"]) .rule(mactreev!("std::tuple::t" [ "...$" elements 0 ]), async |mut cx, [elements]| { let tup: HomoTpl> = 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, ) -> OrcRes { let tup: HomoTpl> = 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 = 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 = match tail_matcher { Some(mac) => { let atm: TAtom = 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(mactree!("Val" new_atom(MatcherAtom { keys, matcher: mactree!("std::tuple::matcher_body" "push" sub_matchers; "push" tail_matcher), }))) } fn tuple_matcher_body( children: HomoTpl, tail: OrcOpt, value: HomoTpl, ) -> impl Future { exec(async move |mut h| -> OrcRes>> { if value.0.len() < children.0.len() { return Ok(OrcOpt(None)); } let mut binds = Vec::new(); for (sub_mat, sub_val) in children.0.iter().zip(&value.0) { 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), } } match tail.0 { None if children.0.len() < value.0.len() => return Ok(OrcOpt(None)), None => (), Some(tail_mat) => { let tail_tpl = stream::iter(&value.0[children.0.len()..]) .fold(sym!(std::tuple::empty).to_gen().await, async |prefix, new| { call(sym!(std::tuple::cat), (prefix, new.clone())).await }) .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)), } }, }; Ok(OrcOpt(Some(HomoTpl(binds)))) }) }