Macro system done in theory

too afraid to begin debugging, resting for a moment
This commit is contained in:
2025-09-03 16:05:26 +02:00
parent 051b5e666f
commit 7031f3a7d8
51 changed files with 1463 additions and 458 deletions

View File

@@ -16,7 +16,7 @@ pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, 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<MaxVecSplit> {
fn split_at_max_vec(pattern: &'_ [MacTree]) -> Option<MaxVecSplit<'_>> {
let rngidx = pattern
.iter()
.position_max_by_key(|expr| vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1))?;
@@ -31,7 +31,6 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
}
#[must_use]
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
let left_split = scal_cnt(pattern.iter());
if pattern.len() <= left_split {
@@ -49,13 +48,11 @@ pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
}
/// Pattern MUST NOT contain vectorial placeholders
#[must_use]
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 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");
@@ -116,7 +113,6 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
}
/// Pattern MUST NOT be a vectorial placeholder
#[must_use]
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
Ok(match &*pattern.tok {
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
@@ -140,8 +136,6 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
#[cfg(test)]
mod test {
use std::rc::Rc;
use orchid_base::interner::Interner;
use orchid_base::location::SrcRange;
use orchid_base::sym;
@@ -149,15 +143,14 @@ mod test {
use test_executors::spin_on;
use super::mk_any;
use crate::macros::MacTok;
use crate::macros::mactree::{Ph, PhKind};
use crate::macros::{MacTok, MacTree};
#[test]
fn test_scan() {
spin_on(async {
let i = Interner::new_master();
let ex =
|tok: MacTok| async { MacTree { tok: Rc::new(tok), pos: SrcRange::mock(&i).await.pos() } };
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 },

View File

@@ -1,9 +1,8 @@
use std::fmt;
use std::rc::Rc;
use itertools::Itertools;
use orchid_base::error::OrcRes;
use orchid_base::interner::Interner;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
@@ -16,46 +15,47 @@ use super::vec_match::vec_match;
use crate::macros::mactree::{Ph, PhKind};
use crate::macros::{MacTok, MacTree};
pub fn first_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.first().unwrap()).is_some() }
pub fn last_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.last().unwrap()).is_some() }
pub struct NamedMatcher(AnyMatcher);
pub struct NamedMatcher {
inner: AnyMatcher,
head: Sym,
after_tok: Tok<String>,
}
impl NamedMatcher {
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"
);
Ok(Self(match last_is_vec(pattern) {
true => mk_any(pattern, i).await,
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 tok = MacTok::Ph(Ph { name: i.i("::after").await, kind });
let suffix = [MacTree { pos: Pos::None, tok: Rc::new(tok) }];
mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec(), i).await
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 async fn apply<'a>(
pub fn apply<'a>(
&self,
seq: &'a [MacTree],
i: &Interner,
save_loc: impl Fn(Sym) -> bool,
) -> Option<(MatchState<'a>, &'a [MacTree])> {
let mut state = any_match(&self.0, seq, &save_loc)?;
match state.remove(i.i("::after").await) {
Some(StateEntry::Scalar(_)) => panic!("::after can never be a scalar entry!"),
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.0.fmt(f) }
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})") }

View File

@@ -54,6 +54,7 @@ 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 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)
}