use futures::FutureExt; use futures::future::join_all; use itertools::Itertools; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::interner::{Interner, Tok}; use orchid_base::join_ok; use orchid_base::side::Side; use super::shared::{AnyMatcher, ScalMatcher, VecMatcher}; use super::vec_attrs::vec_attrs; use crate::macros::mactree::{Ph, PhKind}; use crate::macros::{MacTok, MacTree}; pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok, u8, bool), &'a [MacTree]); /// Derive the details of the central vectorial and the two sides from a /// slice of Expr's #[must_use] fn split_at_max_vec(pattern: &'_ [MacTree]) -> Option> { let rngidx = pattern .iter() .position_max_by_key(|expr| vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1))?; let (left, not_left) = pattern.split_at(rngidx); let (placeh, right) = not_left.split_first().expect("The index of the greatest element must be less than the length"); vec_attrs(placeh).map(|attrs| (left, attrs, right)) } #[must_use] fn scal_cnt<'a>(iter: impl Iterator) -> usize { iter.take_while(|expr| vec_attrs(expr).is_none()).count() } pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes { let left_split = scal_cnt(pattern.iter()); if pattern.len() <= left_split { return Ok(AnyMatcher::Scalar(mk_scalv(pattern, i).await?)); } let (left, not_left) = pattern.split_at(left_split); let right_split = not_left.len() - scal_cnt(pattern.iter().rev()); let (mid, right) = not_left.split_at(right_split); join_ok! { left = mk_scalv(left, i).await; mid = mk_vec(mid, i).await; right = mk_scalv(right, i).await; } Ok(AnyMatcher::Vec { left, mid, right }) } /// Pattern MUST NOT contain vectorial placeholders async fn mk_scalv(pattern: &[MacTree], i: &Interner) -> OrcRes> { join_all(pattern.iter().map(|pat| mk_scalar(pat, i))).await.into_iter().collect() } /// Pattern MUST start and end with a vectorial placeholder pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes { debug_assert!(!pattern.is_empty(), "pattern cannot be empty"); debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial"); debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial"); let (left, (key, _, nonzero), right) = split_at_max_vec(pattern) .expect("pattern must have vectorial placeholders at least at either end"); let r_sep_size = scal_cnt(right.iter()); let (r_sep, r_side) = right.split_at(r_sep_size); let l_sep_size = scal_cnt(left.iter().rev()); let (l_side, l_sep) = left.split_at(left.len() - l_sep_size); let main = VecMatcher::Placeh { key: key.clone(), nonzero }; match (left, right) { (&[], &[]) => Ok(VecMatcher::Placeh { key, nonzero }), (&[], _) => { join_ok! { sep = mk_scalv(r_sep, i).await; right = mk_vec(r_side, i).boxed_local().await; } Ok(VecMatcher::Scan { direction: Side::Left, left: Box::new(main), sep, right: Box::new(right), }) }, (_, &[]) => { join_ok! { left = mk_vec(l_side, i).boxed_local().await; sep = mk_scalv(l_sep, i).await; } Ok(VecMatcher::Scan { direction: Side::Right, left: Box::new(left), sep, right: Box::new(main), }) }, (..) => { let mut key_order = l_side.iter().chain(r_side.iter()).filter_map(vec_attrs).collect::>(); key_order.sort_by_key(|(_, prio, _)| -(*prio as i64)); join_ok! { left = mk_vec(l_side, i).boxed_local().await; left_sep = mk_scalv(l_sep, i).await; right_sep = mk_scalv(r_sep, i).await; right = mk_vec(r_side, i).boxed_local().await; } Ok(VecMatcher::Middle { left: Box::new(left), left_sep, mid: Box::new(main), right_sep, right: Box::new(right), key_order: key_order.into_iter().map(|(n, ..)| n).collect(), }) }, } } /// Pattern MUST NOT be a vectorial placeholder async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes { Ok(match &*pattern.tok { MacTok::Name(n) => ScalMatcher::Name(n.clone()), MacTok::Ph(Ph { name, kind }) => match kind { PhKind::Vector { .. } => { panic!("Scalar matcher cannot be built from vector pattern") }, PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() }, }, MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body, i).boxed_local().await?)), MacTok::Lambda(..) => return Err(mk_errv( i.i("Lambda in matcher").await, "Lambdas can't be matched for, only generated in templates", [pattern.pos()], )), MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"), MacTok::Bottom(errv) => return Err(errv.clone()), }) } #[cfg(test)] mod test { use orchid_base::interner::Interner; use orchid_base::location::SrcRange; use orchid_base::sym; use orchid_base::tokens::Paren; use test_executors::spin_on; use super::mk_any; use crate::macros::MacTok; use crate::macros::mactree::{Ph, PhKind}; #[test] fn test_scan() { spin_on(async { let i = Interner::new_master(); let ex = |tok: MacTok| async { tok.at(SrcRange::mock(&i).await.pos()) }; let pattern = vec![ ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, name: i.i("::prefix").await, })) .await, ex(MacTok::Name(sym!(prelude::do; i).await)).await, ex(MacTok::S(Paren::Round, vec![ ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, name: i.i("expr").await, })) .await, ex(MacTok::Name(sym!(prelude::; ; i).await)).await, ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 1, at_least_one: false }, name: i.i("rest").await, })) .await, ])) .await, ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, name: i.i("::suffix").await, })) .await, ]; let matcher = mk_any(&pattern, &i).await.expect("This matcher isn't broken"); println!("{matcher}"); }) } }