New macro system and stdlib additions
This commit is contained in:
@@ -2,9 +2,10 @@ 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::interner::Tok;
|
||||
use orchid_base::join_ok;
|
||||
use orchid_base::side::Side;
|
||||
use orchid_extension::context::i;
|
||||
|
||||
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
|
||||
use super::vec_attrs::vec_attrs;
|
||||
@@ -31,29 +32,29 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
|
||||
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
|
||||
}
|
||||
|
||||
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
|
||||
pub async fn mk_any(pattern: &[MacTree]) -> OrcRes<AnyMatcher> {
|
||||
let left_split = scal_cnt(pattern.iter());
|
||||
if pattern.len() <= left_split {
|
||||
return Ok(AnyMatcher::Scalar(mk_scalv(pattern, i).await?));
|
||||
return Ok(AnyMatcher::Scalar(mk_scalv(pattern).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;
|
||||
left = mk_scalv(left).await;
|
||||
mid = mk_vec(mid).await;
|
||||
right = mk_scalv(right).await;
|
||||
}
|
||||
Ok(AnyMatcher::Vec { left, mid, right })
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT contain vectorial placeholders
|
||||
async fn mk_scalv(pattern: &[MacTree], i: &Interner) -> OrcRes<Vec<ScalMatcher>> {
|
||||
join_all(pattern.iter().map(|pat| mk_scalar(pat, i))).await.into_iter().collect()
|
||||
async fn mk_scalv(pattern: &[MacTree]) -> OrcRes<Vec<ScalMatcher>> {
|
||||
join_all(pattern.iter().map(mk_scalar)).await.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Pattern MUST start and end with a vectorial placeholder
|
||||
pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
pub async fn mk_vec(pattern: &[MacTree]) -> OrcRes<VecMatcher> {
|
||||
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");
|
||||
@@ -68,8 +69,8 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
(&[], &[]) => Ok(VecMatcher::Placeh { key, nonzero }),
|
||||
(&[], _) => {
|
||||
join_ok! {
|
||||
sep = mk_scalv(r_sep, i).await;
|
||||
right = mk_vec(r_side, i).boxed_local().await;
|
||||
sep = mk_scalv(r_sep).await;
|
||||
right = mk_vec(r_side).boxed_local().await;
|
||||
}
|
||||
Ok(VecMatcher::Scan {
|
||||
direction: Side::Left,
|
||||
@@ -80,8 +81,8 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
},
|
||||
(_, &[]) => {
|
||||
join_ok! {
|
||||
left = mk_vec(l_side, i).boxed_local().await;
|
||||
sep = mk_scalv(l_sep, i).await;
|
||||
left = mk_vec(l_side).boxed_local().await;
|
||||
sep = mk_scalv(l_sep).await;
|
||||
}
|
||||
Ok(VecMatcher::Scan {
|
||||
direction: Side::Right,
|
||||
@@ -95,10 +96,10 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
l_side.iter().chain(r_side.iter()).filter_map(vec_attrs).collect::<Vec<_>>();
|
||||
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;
|
||||
left = mk_vec(l_side).boxed_local().await;
|
||||
left_sep = mk_scalv(l_sep).await;
|
||||
right_sep = mk_scalv(r_sep).await;
|
||||
right = mk_vec(r_side).boxed_local().await;
|
||||
}
|
||||
Ok(VecMatcher::Middle {
|
||||
left: Box::new(left),
|
||||
@@ -113,7 +114,7 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT be a vectorial placeholder
|
||||
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
||||
Ok(match &*pattern.tok {
|
||||
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
|
||||
MacTok::Ph(Ph { name, kind }) => match kind {
|
||||
@@ -122,10 +123,10 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
},
|
||||
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
|
||||
},
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body, i).boxed_local().await?)),
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
|
||||
MacTok::Lambda(..) =>
|
||||
return Err(mk_errv(
|
||||
i.i("Lambda in matcher").await,
|
||||
i().i("Lambda in matcher").await,
|
||||
"Lambdas can't be matched for, only generated in templates",
|
||||
[pattern.pos()],
|
||||
)),
|
||||
@@ -136,50 +137,52 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use orchid_base::interner::Interner;
|
||||
use orchid_base::location::SrcRange;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tokens::Paren;
|
||||
use orchid_extension::context::{i, mock_ctx, with_ctx};
|
||||
use test_executors::spin_on;
|
||||
|
||||
use super::mk_any;
|
||||
use crate::macros::MacTok;
|
||||
use crate::macros::mactree::{Ph, PhKind};
|
||||
use crate::macros::mactree::{MacTreeSeq, 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()) };
|
||||
spin_on(with_ctx(mock_ctx(), async {
|
||||
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,
|
||||
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,
|
||||
]))
|
||||
ex(MacTok::Name(sym!(prelude::do; i()))).await,
|
||||
ex(MacTok::S(
|
||||
Paren::Round,
|
||||
MacTreeSeq::new([
|
||||
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,
|
||||
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,
|
||||
name: i().i("::suffix").await,
|
||||
}))
|
||||
.await,
|
||||
];
|
||||
let matcher = mk_any(&pattern, &i).await.expect("This matcher isn't broken");
|
||||
let matcher = mk_any(&pattern).await.expect("This matcher isn't broken");
|
||||
println!("{matcher}");
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,61 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_extension::context::i;
|
||||
|
||||
use super::any_match::any_match;
|
||||
use super::build::{mk_any, mk_vec};
|
||||
use super::shared::{AnyMatcher, VecMatcher};
|
||||
use super::build::mk_any;
|
||||
use super::shared::AnyMatcher;
|
||||
use super::state::{MatchState, StateEntry};
|
||||
use super::vec_attrs::vec_attrs;
|
||||
use super::vec_match::vec_match;
|
||||
use crate::macros::mactree::{Ph, PhKind};
|
||||
use crate::macros::mactree::{MacTreeSeq, Ph, PhKind};
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
pub struct NamedMatcher {
|
||||
pub struct Matcher {
|
||||
inner: AnyMatcher,
|
||||
head: Sym,
|
||||
after_tok: Tok<String>,
|
||||
}
|
||||
impl NamedMatcher {
|
||||
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
||||
let head = match pattern.first().map(|tree| tree.tok()) {
|
||||
Some(MacTok::Name(name)) => name.clone(),
|
||||
_ => panic!("Named matchers must begin with a name"),
|
||||
};
|
||||
let after_tok = i.i("::after").await;
|
||||
let inner = match pattern.last().and_then(vec_attrs).is_some() {
|
||||
true => mk_any(pattern, i).await?,
|
||||
false => {
|
||||
let kind = PhKind::Vector { priority: 0, at_least_one: false };
|
||||
let suffix = [MacTok::Ph(Ph { name: after_tok.clone(), kind }).at(Pos::None)];
|
||||
mk_any(&pattern.iter().cloned().chain(suffix).collect_vec(), i).await?
|
||||
},
|
||||
};
|
||||
Ok(Self { after_tok, inner, head })
|
||||
}
|
||||
pub fn head(&self) -> Sym { self.head.clone() }
|
||||
/// Also returns the tail, if any, which should be matched further
|
||||
/// Note that due to how priod works below, the main usable information from
|
||||
/// the tail is its length
|
||||
pub fn apply<'a>(
|
||||
&self,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: impl Fn(Sym) -> bool,
|
||||
) -> Option<(MatchState<'a>, &'a [MacTree])> {
|
||||
let mut state = any_match(&self.inner, seq, &save_loc)?;
|
||||
match state.remove(self.after_tok.clone()) {
|
||||
Some(StateEntry::Scalar(_)) => panic!("{} can never be a scalar entry!", self.after_tok),
|
||||
Some(StateEntry::Vec(v)) => Some((state, v)),
|
||||
None => Some((state, &[][..])),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for NamedMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }
|
||||
}
|
||||
impl fmt::Debug for NamedMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
||||
}
|
||||
|
||||
pub struct PriodMatcher(VecMatcher);
|
||||
impl PriodMatcher {
|
||||
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
||||
assert!(
|
||||
pattern.first().and_then(vec_attrs).is_some() && pattern.last().and_then(vec_attrs).is_some(),
|
||||
"Prioritized matchers must start and end with a vectorial",
|
||||
);
|
||||
Ok(Self(mk_vec(pattern, i).await?))
|
||||
impl Matcher {
|
||||
pub async fn new(pattern: MacTreeSeq) -> OrcRes<Self> {
|
||||
let mut pattern = Rc::unwrap_or_clone(pattern.items);
|
||||
let kind = PhKind::Vector { at_least_one: false, priority: 0 };
|
||||
let first = pattern.first().expect("Empty pattern is not allowed");
|
||||
if vec_attrs(first).is_none() {
|
||||
let pos = first.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: i().i("::before").await, kind }).at(pos));
|
||||
}
|
||||
let last = pattern.last().expect("first returned Some above");
|
||||
if vec_attrs(last).is_none() {
|
||||
let pos = last.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: i().i("::after").await, kind }).at(pos));
|
||||
}
|
||||
Ok(Matcher { inner: mk_any(&pattern).await? })
|
||||
}
|
||||
/// tokens before the offset always match the prefix
|
||||
pub fn apply<'a>(
|
||||
/// Also returns the head and tail, which should be matched by overarching
|
||||
/// matchers attempted later.
|
||||
pub async fn apply<'a>(
|
||||
&self,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
vec_match(&self.0, seq, &save_loc)
|
||||
save_loc: &dyn Fn(Sym) -> bool,
|
||||
) -> Option<(&'a [MacTree], MatchState<'a>, &'a [MacTree])> {
|
||||
let mut result = any_match(&self.inner, seq, &save_loc)?;
|
||||
async fn remove_frame<'a>(result: &mut MatchState<'a>, key: &str) -> &'a [MacTree] {
|
||||
match result.remove(i().i(key).await) {
|
||||
Some(StateEntry::Scalar(_)) => panic!("{key} is defined in the constructor as a Vec"),
|
||||
Some(StateEntry::Vec(v)) => v,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
let before = remove_frame(&mut result, "::before").await;
|
||||
let after = remove_frame(&mut result, "::after").await;
|
||||
Some((before, result, after))
|
||||
}
|
||||
}
|
||||
impl fmt::Display for PriodMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
|
||||
impl fmt::Display for Matcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }
|
||||
}
|
||||
impl fmt::Debug for PriodMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PriodMatcher({self})") }
|
||||
impl fmt::Debug for Matcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn scal_match<'a>(
|
||||
(ScalMatcher::Placeh { key }, _) =>
|
||||
Some(MatchState::from_ph(key.clone(), StateEntry::Scalar(expr))),
|
||||
(ScalMatcher::S(c1, b_mat), MacTok::S(c2, body)) if c1 == c2 =>
|
||||
any_match(b_mat, &body[..], save_loc),
|
||||
any_match(b_mat, &body.items, save_loc),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ impl<'a> MatchState<'a> {
|
||||
pub fn from_name(name: Sym, location: Pos) -> Self {
|
||||
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
|
||||
}
|
||||
pub fn names(&self) -> impl Iterator<Item = (Sym, &[Pos])> {
|
||||
self.name_posv.iter().map(|(sym, vec)| (sym.clone(), &vec[..]))
|
||||
}
|
||||
pub fn get(&self, key: &Tok<String>) -> Option<&StateEntry<'a>> { self.placeholders.get(key) }
|
||||
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
|
||||
self.placeholders.remove(&name)
|
||||
|
||||
Reference in New Issue
Block a user