mod matcher;
mod transcriber;
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::Span;
use crate::{
ExpandError, ExpandErrorKind, ExpandResult, MacroCallStyle, MatchedArmIndex,
parser::MetaVarKind,
};
pub(crate) fn expand_rules(
db: &dyn salsa::Database,
rules: &[crate::Rule],
input: &tt::TopSubtree<Span>,
marker: impl Fn(&mut Span) + Copy,
call_style: MacroCallStyle,
call_site: Span,
) -> ExpandResult<(tt::TopSubtree<Span>, MatchedArmIndex)> {
let mut match_: Option<(matcher::Match<'_>, &crate::Rule, usize)> = None;
for (idx, rule) in rules.iter().enumerate() {
if call_style != rule.style {
continue;
}
let new_match = matcher::match_(db, &rule.lhs, input);
if new_match.err.is_none() {
let ExpandResult { value, err: transcribe_err } =
transcriber::transcribe(&rule.rhs, &new_match.bindings, marker, call_site);
if transcribe_err.is_none() {
return ExpandResult::ok((value, Some(idx as u32)));
}
}
if let Some((prev_match, _, _)) = &match_ {
if (new_match.unmatched_tts, -(new_match.bound_count as i32))
< (prev_match.unmatched_tts, -(prev_match.bound_count as i32))
{
match_ = Some((new_match, rule, idx));
}
} else {
match_ = Some((new_match, rule, idx));
}
}
if let Some((match_, rule, idx)) = match_ {
let ExpandResult { value, err: transcribe_err } =
transcriber::transcribe(&rule.rhs, &match_.bindings, marker, call_site);
ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) }
} else {
ExpandResult::new(
(tt::TopSubtree::empty(tt::DelimSpan::from_single(call_site)), None),
ExpandError::new(call_site, ExpandErrorKind::NoMatchingRule),
)
}
}
#[derive(Debug, Default, Clone)]
struct Bindings<'a> {
inner: FxHashMap<Symbol, Binding<'a>>,
}
#[derive(Debug, Clone)]
enum Binding<'a> {
Fragment(Fragment<'a>),
Nested(Vec<Binding<'a>>),
Empty,
Missing(MetaVarKind),
}
#[derive(Debug, Default, Clone)]
enum Fragment<'a> {
#[default]
Empty,
Tokens {
tree: tt::TokenTreesView<'a, Span>,
origin: TokensOrigin,
},
Expr(tt::TokenTreesView<'a, Span>),
Path(tt::TokenTreesView<'a, Span>),
TokensOwned(tt::TopSubtree<Span>),
}
impl Fragment<'_> {
fn is_empty(&self) -> bool {
match self {
Fragment::Empty => true,
Fragment::Tokens { tree, .. } => tree.len() == 0,
Fragment::Expr(it) => it.len() == 0,
Fragment::Path(it) => it.len() == 0,
Fragment::TokensOwned(it) => it.0.is_empty(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TokensOrigin {
Raw,
Ast,
}