First steps for the macro system

This commit is contained in:
2025-08-01 18:32:55 +02:00
parent f87185ef88
commit 051b5e666f
18 changed files with 356 additions and 166 deletions

View File

@@ -1,5 +1,9 @@
use futures::FutureExt;
use futures::future::join_all;
use itertools::Itertools;
use orchid_base::interner::Tok;
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};
@@ -28,24 +32,31 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
}
#[must_use]
pub fn mk_any(pattern: &[MacTree]) -> AnyMatcher {
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
let left_split = scal_cnt(pattern.iter());
if pattern.len() <= left_split {
return AnyMatcher::Scalar(mk_scalv(pattern));
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);
AnyMatcher::Vec { left: mk_scalv(left), mid: mk_vec(mid), right: mk_scalv(right) }
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
#[must_use]
fn mk_scalv(pattern: &[MacTree]) -> Vec<ScalMatcher> { pattern.iter().map(mk_scalar).collect() }
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()
}
/// Pattern MUST start and end with a vectorial placeholder
#[must_use]
pub fn mk_vec(pattern: &[MacTree]) -> VecMatcher {
pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> 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");
@@ -57,39 +68,57 @@ pub fn mk_vec(pattern: &[MacTree]) -> VecMatcher {
let (l_side, l_sep) = left.split_at(left.len() - l_sep_size);
let main = VecMatcher::Placeh { key: key.clone(), nonzero };
match (left, right) {
(&[], &[]) => VecMatcher::Placeh { key, nonzero },
(&[], _) => VecMatcher::Scan {
direction: Side::Left,
left: Box::new(main),
sep: mk_scalv(r_sep),
right: Box::new(mk_vec(r_side)),
(&[], &[]) => 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),
})
},
(_, &[]) => VecMatcher::Scan {
direction: Side::Right,
left: Box::new(mk_vec(l_side)),
sep: mk_scalv(l_sep),
right: Box::new(main),
(_, &[]) => {
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::<Vec<_>>();
key_order.sort_by_key(|(_, prio, _)| -(*prio as i64));
VecMatcher::Middle {
left: Box::new(mk_vec(l_side)),
left_sep: mk_scalv(l_sep),
mid: Box::new(main),
right_sep: mk_scalv(r_sep),
right: Box::new(mk_vec(r_side)),
key_order: key_order.into_iter().map(|(n, ..)| n).collect(),
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
#[must_use]
fn mk_scalar(pattern: &MacTree) -> ScalMatcher {
match &*pattern.tok {
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
Ok(match &*pattern.tok {
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
MacTok::Ph(Ph { name, kind }) => match kind {
PhKind::Vector { .. } => {
@@ -97,10 +126,16 @@ fn mk_scalar(pattern: &MacTree) -> ScalMatcher {
},
PhKind::Scalar => ScalMatcher::Placeh { key: name.clone() },
},
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body))),
MacTok::Lambda(arg, body) => ScalMatcher::Lambda(Box::new(mk_any(arg)), Box::new(mk_any(body))),
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)]
@@ -150,7 +185,7 @@ mod test {
}))
.await,
];
let matcher = mk_any(&pattern);
let matcher = mk_any(&pattern, &i).await.expect("This matcher isn't broken");
println!("{matcher}");
})
}

View File

@@ -2,6 +2,7 @@ use std::fmt;
use std::rc::Rc;
use itertools::Itertools;
use orchid_base::error::OrcRes;
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use orchid_base::name::Sym;
@@ -20,21 +21,21 @@ pub fn last_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.last().unwra
pub struct NamedMatcher(AnyMatcher);
impl NamedMatcher {
pub async fn new(pattern: &[MacTree], i: &Interner) -> Self {
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
assert!(
matches!(pattern.first().map(|tree| &*tree.tok), Some(MacTok::Name(_))),
"Named matchers must begin with a name"
);
match last_is_vec(pattern) {
true => Self(mk_any(pattern)),
Ok(Self(match last_is_vec(pattern) {
true => mk_any(pattern, i).await,
false => {
let kind = PhKind::Vector { priority: 0, at_least_one: false };
let tok = MacTok::Ph(Ph { name: i.i("::after").await, kind });
let suffix = [MacTree { pos: Pos::None, tok: Rc::new(tok) }];
Self(mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec()))
mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec(), i).await
},
}
}?))
}
/// Also returns the tail, if any, which should be matched further
/// Note that due to how priod works below, the main usable information from
@@ -62,12 +63,12 @@ impl fmt::Debug for NamedMatcher {
pub struct PriodMatcher(VecMatcher);
impl PriodMatcher {
pub fn new(pattern: &[MacTree]) -> Self {
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",
);
Self(mk_vec(pattern))
Ok(Self(mk_vec(pattern, i).await?))
}
/// tokens before the offset always match the prefix
pub fn apply<'a>(

View File

@@ -20,8 +20,6 @@ pub fn scal_match<'a>(
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),
(ScalMatcher::Lambda(arg_mat, b_mat), MacTok::Lambda(arg, body)) =>
Some(any_match(arg_mat, arg, save_loc)?.combine(any_match(b_mat, body, save_loc)?)),
_ => None,
}
}

View File

@@ -11,7 +11,6 @@ use orchid_base::tokens::{PARENS, Paren};
pub enum ScalMatcher {
Name(Sym),
S(Paren, Box<AnyMatcher>),
Lambda(Box<AnyMatcher>, Box<AnyMatcher>),
Placeh { key: Tok<String> },
}
@@ -62,7 +61,6 @@ impl fmt::Display for ScalMatcher {
let (l, r, _) = PARENS.iter().find(|r| r.2 == *t).unwrap();
write!(f, "{l}{body}{r}")
},
Self::Lambda(arg, body) => write!(f, "\\{arg}.{body}"),
}
}
}