summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs')
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs914
1 files changed, 914 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
new file mode 100644
index 000000000..5020e9aba
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -0,0 +1,914 @@
+//! An NFA-based parser, which is porting from rustc mbe parsing code
+//!
+//! See <https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs>
+//! Here is a quick intro to how the parser works, copied from rustc:
+//!
+//! A 'position' is a dot in the middle of a matcher, usually represented as a
+//! dot. For example `· a $( a )* a b` is a position, as is `a $( · a )* a b`.
+//!
+//! The parser walks through the input a character at a time, maintaining a list
+//! of threads consistent with the current position in the input string: `cur_items`.
+//!
+//! As it processes them, it fills up `eof_items` with threads that would be valid if
+//! the macro invocation is now over, `bb_items` with threads that are waiting on
+//! a Rust non-terminal like `$e:expr`, and `next_items` with threads that are waiting
+//! on a particular token. Most of the logic concerns moving the · through the
+//! repetitions indicated by Kleene stars. The rules for moving the · without
+//! consuming any input are called epsilon transitions. It only advances or calls
+//! out to the real Rust parser when no `cur_items` threads remain.
+//!
+//! Example:
+//!
+//! ```text, ignore
+//! Start parsing a a a a b against [· a $( a )* a b].
+//!
+//! Remaining input: a a a a b
+//! next: [· a $( a )* a b]
+//!
+//! - - - Advance over an a. - - -
+//!
+//! Remaining input: a a a b
+//! cur: [a · $( a )* a b]
+//! Descend/Skip (first item).
+//! next: [a $( · a )* a b] [a $( a )* · a b].
+//!
+//! - - - Advance over an a. - - -
+//!
+//! Remaining input: a a b
+//! cur: [a $( a · )* a b] [a $( a )* a · b]
+//! Follow epsilon transition: Finish/Repeat (first item)
+//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
+//!
+//! - - - Advance over an a. - - - (this looks exactly like the last step)
+//!
+//! Remaining input: a b
+//! cur: [a $( a · )* a b] [a $( a )* a · b]
+//! Follow epsilon transition: Finish/Repeat (first item)
+//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
+//!
+//! - - - Advance over an a. - - - (this looks exactly like the last step)
+//!
+//! Remaining input: b
+//! cur: [a $( a · )* a b] [a $( a )* a · b]
+//! Follow epsilon transition: Finish/Repeat (first item)
+//! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
+//!
+//! - - - Advance over a b. - - -
+//!
+//! Remaining input: ''
+//! eof: [a $( a )* a b ·]
+//! ```
+
+use std::rc::Rc;
+
+use smallvec::{smallvec, SmallVec};
+use syntax::SmolStr;
+
+use crate::{
+ expander::{Binding, Bindings, ExpandResult, Fragment},
+ parser::{Op, RepeatKind, Separator},
+ tt_iter::TtIter,
+ ExpandError, MetaTemplate,
+};
+
+impl Bindings {
+ fn push_optional(&mut self, name: &SmolStr) {
+ // FIXME: Do we have a better way to represent an empty token ?
+ // Insert an empty subtree for empty token
+ let tt = tt::Subtree::default().into();
+ self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt)));
+ }
+
+ fn push_empty(&mut self, name: &SmolStr) {
+ self.inner.insert(name.clone(), Binding::Empty);
+ }
+
+ fn bindings(&self) -> impl Iterator<Item = &Binding> {
+ self.inner.values()
+ }
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub(super) struct Match {
+ pub(super) bindings: Bindings,
+ /// We currently just keep the first error and count the rest to compare matches.
+ pub(super) err: Option<ExpandError>,
+ pub(super) err_count: usize,
+ /// How many top-level token trees were left to match.
+ pub(super) unmatched_tts: usize,
+ /// The number of bound variables
+ pub(super) bound_count: usize,
+}
+
+impl Match {
+ fn add_err(&mut self, err: ExpandError) {
+ let prev_err = self.err.take();
+ self.err = prev_err.or(Some(err));
+ self.err_count += 1;
+ }
+}
+
+/// Matching errors are added to the `Match`.
+pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
+ let mut res = match_loop(pattern, input);
+ res.bound_count = count(res.bindings.bindings());
+ return res;
+
+ fn count<'a>(bindings: impl Iterator<Item = &'a Binding>) -> usize {
+ bindings
+ .map(|it| match it {
+ Binding::Fragment(_) => 1,
+ Binding::Empty => 1,
+ Binding::Nested(it) => count(it.iter()),
+ })
+ .sum()
+ }
+}
+
+#[derive(Debug, Clone)]
+enum BindingKind {
+ Empty(SmolStr),
+ Optional(SmolStr),
+ Fragment(SmolStr, Fragment),
+ Nested(usize, usize),
+}
+
+#[derive(Debug, Clone)]
+struct BindingsIdx(usize, usize);
+
+#[derive(Debug, Clone)]
+enum LinkNode<T> {
+ Node(T),
+ Parent { idx: usize, len: usize },
+}
+
+#[derive(Default)]
+struct BindingsBuilder {
+ nodes: Vec<Vec<LinkNode<Rc<BindingKind>>>>,
+ nested: Vec<Vec<LinkNode<usize>>>,
+}
+
+impl BindingsBuilder {
+ fn alloc(&mut self) -> BindingsIdx {
+ let idx = self.nodes.len();
+ self.nodes.push(Vec::new());
+ let nidx = self.nested.len();
+ self.nested.push(Vec::new());
+ BindingsIdx(idx, nidx)
+ }
+
+ fn copy(&mut self, bindings: &BindingsIdx) -> BindingsIdx {
+ let idx = copy_parent(bindings.0, &mut self.nodes);
+ let nidx = copy_parent(bindings.1, &mut self.nested);
+ return BindingsIdx(idx, nidx);
+
+ fn copy_parent<T>(idx: usize, target: &mut Vec<Vec<LinkNode<T>>>) -> usize
+ where
+ T: Clone,
+ {
+ let new_idx = target.len();
+ let len = target[idx].len();
+ if len < 4 {
+ target.push(target[idx].clone())
+ } else {
+ target.push(vec![LinkNode::Parent { idx, len }]);
+ }
+ new_idx
+ }
+ }
+
+ fn push_empty(&mut self, idx: &mut BindingsIdx, var: &SmolStr) {
+ self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Empty(var.clone()))));
+ }
+
+ fn push_optional(&mut self, idx: &mut BindingsIdx, var: &SmolStr) {
+ self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone()))));
+ }
+
+ fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) {
+ self.nodes[idx.0]
+ .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
+ }
+
+ fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
+ let BindingsIdx(idx, nidx) = self.copy(child);
+ self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
+ }
+
+ fn push_default(&mut self, idx: &mut BindingsIdx) {
+ self.nested[idx.1].push(LinkNode::Node(idx.0));
+ let new_idx = self.nodes.len();
+ self.nodes.push(Vec::new());
+ idx.0 = new_idx;
+ }
+
+ fn build(self, idx: &BindingsIdx) -> Bindings {
+ let mut bindings = Bindings::default();
+ self.build_inner(&mut bindings, &self.nodes[idx.0]);
+ bindings
+ }
+
+ fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) {
+ let mut nodes = Vec::new();
+ self.collect_nodes(link_nodes, &mut nodes);
+
+ for cmd in nodes {
+ match &**cmd {
+ BindingKind::Empty(name) => {
+ bindings.push_empty(name);
+ }
+ BindingKind::Optional(name) => {
+ bindings.push_optional(name);
+ }
+ BindingKind::Fragment(name, fragment) => {
+ bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone()));
+ }
+ BindingKind::Nested(idx, nested_idx) => {
+ let mut nested_nodes = Vec::new();
+ self.collect_nested(*idx, *nested_idx, &mut nested_nodes);
+
+ for (idx, iter) in nested_nodes.into_iter().enumerate() {
+ for (key, value) in &iter.inner {
+ let bindings = bindings
+ .inner
+ .entry(key.clone())
+ .or_insert_with(|| Binding::Nested(Vec::new()));
+
+ if let Binding::Nested(it) = bindings {
+ // insert empty nested bindings before this one
+ while it.len() < idx {
+ it.push(Binding::Nested(Vec::new()));
+ }
+ it.push(value.clone());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn collect_nested_ref<'a>(
+ &'a self,
+ id: usize,
+ len: usize,
+ nested_refs: &mut Vec<&'a Vec<LinkNode<Rc<BindingKind>>>>,
+ ) {
+ self.nested[id].iter().take(len).for_each(|it| match it {
+ LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]),
+ LinkNode::Parent { idx, len } => self.collect_nested_ref(*idx, *len, nested_refs),
+ });
+ }
+
+ fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec<Bindings>) {
+ let last = &self.nodes[idx];
+ let mut nested_refs = Vec::new();
+ self.nested[nested_idx].iter().for_each(|it| match *it {
+ LinkNode::Node(idx) => nested_refs.push(&self.nodes[idx]),
+ LinkNode::Parent { idx, len } => self.collect_nested_ref(idx, len, &mut nested_refs),
+ });
+ nested_refs.push(last);
+
+ nested_refs.into_iter().for_each(|iter| {
+ let mut child_bindings = Bindings::default();
+ self.build_inner(&mut child_bindings, iter);
+ nested.push(child_bindings)
+ })
+ }
+
+ fn collect_nodes_ref<'a>(
+ &'a self,
+ id: usize,
+ len: usize,
+ nodes: &mut Vec<&'a Rc<BindingKind>>,
+ ) {
+ self.nodes[id].iter().take(len).for_each(|it| match it {
+ LinkNode::Node(it) => nodes.push(it),
+ LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes),
+ });
+ }
+
+ fn collect_nodes<'a>(
+ &'a self,
+ link_nodes: &'a [LinkNode<Rc<BindingKind>>],
+ nodes: &mut Vec<&'a Rc<BindingKind>>,
+ ) {
+ link_nodes.iter().for_each(|it| match it {
+ LinkNode::Node(it) => nodes.push(it),
+ LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes),
+ });
+ }
+}
+
+#[derive(Debug, Clone)]
+struct MatchState<'t> {
+ /// The position of the "dot" in this matcher
+ dot: OpDelimitedIter<'t>,
+
+ /// Token subtree stack
+ /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. )
+ /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does
+ /// that where the bottom of the stack is the outermost matcher.
+ stack: SmallVec<[OpDelimitedIter<'t>; 4]>,
+
+ /// The "parent" matcher position if we are in a repetition. That is, the matcher position just
+ /// before we enter the repetition.
+ up: Option<Box<MatchState<'t>>>,
+
+ /// The separator if we are in a repetition.
+ sep: Option<Separator>,
+
+ /// The KleeneOp of this sequence if we are in a repetition.
+ sep_kind: Option<RepeatKind>,
+
+ /// Number of tokens of seperator parsed
+ sep_parsed: Option<usize>,
+
+ /// Matched meta variables bindings
+ bindings: BindingsIdx,
+
+ /// Cached result of meta variable parsing
+ meta_result: Option<(TtIter<'t>, ExpandResult<Option<Fragment>>)>,
+
+ /// Is error occuried in this state, will `poised` to "parent"
+ is_error: bool,
+}
+
+/// Process the matcher positions of `cur_items` until it is empty. In the process, this will
+/// produce more items in `next_items`, `eof_items`, and `bb_items`.
+///
+/// For more info about the how this happens, see the module-level doc comments and the inline
+/// comments of this function.
+///
+/// # Parameters
+///
+/// - `src`: the current token of the parser.
+/// - `stack`: the "parent" frames of the token tree
+/// - `res`: the match result to store errors
+/// - `cur_items`: the set of current items to be processed. This should be empty by the end of a
+/// successful execution of this function.
+/// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in
+/// the function `parse`.
+/// - `eof_items`: the set of items that would be valid if this was the EOF.
+/// - `bb_items`: the set of items that are waiting for the black-box parser.
+/// - `error_items`: the set of items in errors, used for error-resilient parsing
+fn match_loop_inner<'t>(
+ src: TtIter<'t>,
+ stack: &[TtIter<'t>],
+ res: &mut Match,
+ bindings_builder: &mut BindingsBuilder,
+ cur_items: &mut SmallVec<[MatchState<'t>; 1]>,
+ bb_items: &mut SmallVec<[MatchState<'t>; 1]>,
+ next_items: &mut Vec<MatchState<'t>>,
+ eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
+ error_items: &mut SmallVec<[MatchState<'t>; 1]>,
+) {
+ macro_rules! try_push {
+ ($items: expr, $it:expr) => {
+ if $it.is_error {
+ error_items.push($it);
+ } else {
+ $items.push($it);
+ }
+ };
+ }
+
+ while let Some(mut item) = cur_items.pop() {
+ while item.dot.is_eof() {
+ match item.stack.pop() {
+ Some(frame) => {
+ item.dot = frame;
+ item.dot.next();
+ }
+ None => break,
+ }
+ }
+ let op = match item.dot.peek() {
+ None => {
+ // We are at or past the end of the matcher of `item`.
+ if item.up.is_some() {
+ if item.sep_parsed.is_none() {
+ // Get the `up` matcher
+ let mut new_pos = *item.up.clone().unwrap();
+ new_pos.bindings = bindings_builder.copy(&new_pos.bindings);
+ // Add matches from this repetition to the `matches` of `up`
+ bindings_builder.push_nested(&mut new_pos.bindings, &item.bindings);
+
+ // Move the "dot" past the repetition in `up`
+ new_pos.dot.next();
+ new_pos.is_error = new_pos.is_error || item.is_error;
+ cur_items.push(new_pos);
+ }
+
+ // Check if we need a separator.
+ // We check the separator one by one
+ let sep_idx = *item.sep_parsed.as_ref().unwrap_or(&0);
+ let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
+ if item.sep.is_some() && sep_idx != sep_len {
+ let sep = item.sep.as_ref().unwrap();
+ if src.clone().expect_separator(sep, sep_idx) {
+ item.dot.next();
+ item.sep_parsed = Some(sep_idx + 1);
+ try_push!(next_items, item);
+ }
+ }
+ // We don't need a separator. Move the "dot" back to the beginning of the matcher
+ // and try to match again UNLESS we are only allowed to have _one_ repetition.
+ else if item.sep_kind != Some(RepeatKind::ZeroOrOne) {
+ item.dot = item.dot.reset();
+ item.sep_parsed = None;
+ bindings_builder.push_default(&mut item.bindings);
+ cur_items.push(item);
+ }
+ } else {
+ // If we are not in a repetition, then being at the end of a matcher means that we have
+ // reached the potential end of the input.
+ try_push!(eof_items, item);
+ }
+ continue;
+ }
+ Some(it) => it,
+ };
+
+ // We are in the middle of a matcher.
+ match op {
+ OpDelimited::Op(Op::Repeat { tokens, kind, separator }) => {
+ if matches!(kind, RepeatKind::ZeroOrMore | RepeatKind::ZeroOrOne) {
+ let mut new_item = item.clone();
+ new_item.bindings = bindings_builder.copy(&new_item.bindings);
+ new_item.dot.next();
+ collect_vars(
+ &mut |s| {
+ bindings_builder.push_empty(&mut new_item.bindings, &s);
+ },
+ tokens,
+ );
+ cur_items.push(new_item);
+ }
+ cur_items.push(MatchState {
+ dot: tokens.iter_delimited(None),
+ stack: Default::default(),
+ up: Some(Box::new(item)),
+ sep: separator.clone(),
+ sep_kind: Some(*kind),
+ sep_parsed: None,
+ bindings: bindings_builder.alloc(),
+ meta_result: None,
+ is_error: false,
+ })
+ }
+ OpDelimited::Op(Op::Subtree { tokens, delimiter }) => {
+ if let Ok(subtree) = src.clone().expect_subtree() {
+ if subtree.delimiter_kind() == delimiter.map(|it| it.kind) {
+ item.stack.push(item.dot);
+ item.dot = tokens.iter_delimited(delimiter.as_ref());
+ cur_items.push(item);
+ }
+ }
+ }
+ OpDelimited::Op(Op::Var { kind, name, .. }) => {
+ if let Some(kind) = kind {
+ let mut fork = src.clone();
+ let match_res = match_meta_var(kind.as_str(), &mut fork);
+ match match_res.err {
+ None => {
+ // Some meta variables are optional (e.g. vis)
+ if match_res.value.is_some() {
+ item.meta_result = Some((fork, match_res));
+ try_push!(bb_items, item);
+ } else {
+ bindings_builder.push_optional(&mut item.bindings, name);
+ item.dot.next();
+ cur_items.push(item);
+ }
+ }
+ Some(err) => {
+ res.add_err(err);
+ if let Some(fragment) = match_res.value {
+ bindings_builder.push_fragment(&mut item.bindings, name, fragment);
+ }
+ item.is_error = true;
+ error_items.push(item);
+ }
+ }
+ }
+ }
+ OpDelimited::Op(Op::Leaf(leaf)) => {
+ if let Err(err) = match_leaf(leaf, &mut src.clone()) {
+ res.add_err(err);
+ item.is_error = true;
+ } else {
+ item.dot.next();
+ }
+ try_push!(next_items, item);
+ }
+ OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {}
+ OpDelimited::Open => {
+ if matches!(src.clone().next(), Some(tt::TokenTree::Subtree(..))) {
+ item.dot.next();
+ try_push!(next_items, item);
+ }
+ }
+ OpDelimited::Close => {
+ let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty();
+ if is_delim_closed {
+ item.dot.next();
+ try_push!(next_items, item);
+ }
+ }
+ }
+ }
+}
+
+fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
+ let mut src = TtIter::new(src);
+ let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
+ let mut res = Match::default();
+ let mut error_recover_item = None;
+
+ let mut bindings_builder = BindingsBuilder::default();
+
+ let mut cur_items = smallvec![MatchState {
+ dot: pattern.iter_delimited(None),
+ stack: Default::default(),
+ up: None,
+ sep: None,
+ sep_kind: None,
+ sep_parsed: None,
+ bindings: bindings_builder.alloc(),
+ is_error: false,
+ meta_result: None,
+ }];
+
+ let mut next_items = vec![];
+
+ loop {
+ let mut bb_items = SmallVec::new();
+ let mut eof_items = SmallVec::new();
+ let mut error_items = SmallVec::new();
+
+ stdx::always!(next_items.is_empty());
+
+ match_loop_inner(
+ src.clone(),
+ &stack,
+ &mut res,
+ &mut bindings_builder,
+ &mut cur_items,
+ &mut bb_items,
+ &mut next_items,
+ &mut eof_items,
+ &mut error_items,
+ );
+ stdx::always!(cur_items.is_empty());
+
+ if !error_items.is_empty() {
+ error_recover_item = error_items.pop().map(|it| it.bindings);
+ } else if let [state, ..] = &*eof_items {
+ error_recover_item = Some(state.bindings.clone());
+ }
+
+ // We need to do some post processing after the `match_loop_inner`.
+ // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
+ // either the parse is ambiguous (which should never happen) or there is a syntax error.
+ if src.peek_n(0).is_none() && stack.is_empty() {
+ if let [state] = &*eof_items {
+ // remove all errors, because it is the correct answer !
+ res = Match::default();
+ res.bindings = bindings_builder.build(&state.bindings);
+ } else {
+ // Error recovery
+ if let Some(item) = error_recover_item {
+ res.bindings = bindings_builder.build(&item);
+ }
+ res.add_err(ExpandError::UnexpectedToken);
+ }
+ return res;
+ }
+
+ // If there are no possible next positions AND we aren't waiting for the black-box parser,
+ // then there is a syntax error.
+ //
+ // Another possibility is that we need to call out to parse some rust nonterminal
+ // (black-box) parser. However, if there is not EXACTLY ONE of these, something is wrong.
+ let has_leftover_tokens = (bb_items.is_empty() && next_items.is_empty())
+ || !(bb_items.is_empty() || next_items.is_empty())
+ || bb_items.len() > 1;
+ if has_leftover_tokens {
+ res.unmatched_tts += src.len();
+ while let Some(it) = stack.pop() {
+ src = it;
+ res.unmatched_tts += src.len();
+ }
+ res.add_err(ExpandError::LeftoverTokens);
+
+ if let Some(error_reover_item) = error_recover_item {
+ res.bindings = bindings_builder.build(&error_reover_item);
+ }
+ return res;
+ }
+ // Dump all possible `next_items` into `cur_items` for the next iteration.
+ else if !next_items.is_empty() {
+ // Now process the next token
+ cur_items.extend(next_items.drain(..));
+
+ match src.next() {
+ Some(tt::TokenTree::Subtree(subtree)) => {
+ stack.push(src.clone());
+ src = TtIter::new(subtree);
+ }
+ None => {
+ if let Some(iter) = stack.pop() {
+ src = iter;
+ }
+ }
+ _ => (),
+ }
+ }
+ // Finally, we have the case where we need to call the black-box parser to get some
+ // nonterminal.
+ else {
+ stdx::always!(bb_items.len() == 1);
+ let mut item = bb_items.pop().unwrap();
+
+ if let Some(OpDelimited::Op(Op::Var { name, .. })) = item.dot.peek() {
+ let (iter, match_res) = item.meta_result.take().unwrap();
+ match match_res.value {
+ Some(fragment) => {
+ bindings_builder.push_fragment(&mut item.bindings, name, fragment);
+ }
+ None if match_res.err.is_none() => {
+ bindings_builder.push_optional(&mut item.bindings, name);
+ }
+ None => {}
+ }
+ if let Some(err) = match_res.err {
+ res.add_err(err);
+ }
+ src = iter.clone();
+ item.dot.next();
+ } else {
+ unreachable!()
+ }
+ cur_items.push(item);
+ }
+ stdx::always!(!cur_items.is_empty());
+ }
+}
+
+fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter<'_>) -> Result<(), ExpandError> {
+ let rhs = src
+ .expect_leaf()
+ .map_err(|()| ExpandError::binding_error(format!("expected leaf: `{lhs}`")))?;
+ match (lhs, rhs) {
+ (
+ tt::Leaf::Punct(tt::Punct { char: lhs, .. }),
+ tt::Leaf::Punct(tt::Punct { char: rhs, .. }),
+ ) if lhs == rhs => Ok(()),
+ (
+ tt::Leaf::Ident(tt::Ident { text: lhs, .. }),
+ tt::Leaf::Ident(tt::Ident { text: rhs, .. }),
+ ) if lhs == rhs => Ok(()),
+ (
+ tt::Leaf::Literal(tt::Literal { text: lhs, .. }),
+ tt::Leaf::Literal(tt::Literal { text: rhs, .. }),
+ ) if lhs == rhs => Ok(()),
+ _ => Err(ExpandError::UnexpectedToken),
+ }
+}
+
+fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
+ let fragment = match kind {
+ "path" => parser::PrefixEntryPoint::Path,
+ "ty" => parser::PrefixEntryPoint::Ty,
+ // FIXME: These two should actually behave differently depending on the edition.
+ //
+ // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
+ "pat" | "pat_param" => parser::PrefixEntryPoint::Pat,
+ "stmt" => parser::PrefixEntryPoint::Stmt,
+ "block" => parser::PrefixEntryPoint::Block,
+ "meta" => parser::PrefixEntryPoint::MetaItem,
+ "item" => parser::PrefixEntryPoint::Item,
+ "vis" => parser::PrefixEntryPoint::Vis,
+ "expr" => {
+ // `expr` should not match underscores.
+ // HACK: Macro expansion should not be done using "rollback and try another alternative".
+ // rustc [explicitly checks the next token][0].
+ // [0]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576
+ match input.peek_n(0) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) if it.text == "_" => {
+ return ExpandResult::only_err(ExpandError::NoMatchingRule)
+ }
+ _ => {}
+ };
+ return input
+ .expect_fragment(parser::PrefixEntryPoint::Expr)
+ .map(|tt| tt.map(Fragment::Expr));
+ }
+ _ => {
+ let tt_result = match kind {
+ "ident" => input
+ .expect_ident()
+ .map(|ident| tt::Leaf::from(ident.clone()).into())
+ .map_err(|()| ExpandError::binding_error("expected ident")),
+ "tt" => input
+ .expect_tt()
+ .map_err(|()| ExpandError::binding_error("expected token tree")),
+ "lifetime" => input
+ .expect_lifetime()
+ .map_err(|()| ExpandError::binding_error("expected lifetime")),
+ "literal" => {
+ let neg = input.eat_char('-');
+ input
+ .expect_literal()
+ .map(|literal| {
+ let lit = literal.clone();
+ match neg {
+ None => lit.into(),
+ Some(neg) => tt::TokenTree::Subtree(tt::Subtree {
+ delimiter: None,
+ token_trees: vec![neg, lit.into()],
+ }),
+ }
+ })
+ .map_err(|()| ExpandError::binding_error("expected literal"))
+ }
+ _ => Err(ExpandError::UnexpectedToken),
+ };
+ return tt_result.map(|it| Some(Fragment::Tokens(it))).into();
+ }
+ };
+ input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens))
+}
+
+fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) {
+ for op in pattern.iter() {
+ match op {
+ Op::Var { name, .. } => collector_fun(name.clone()),
+ Op::Leaf(_) => (),
+ Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
+ Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
+ Op::Ignore { .. } | Op::Index { .. } => {}
+ }
+ }
+}
+impl MetaTemplate {
+ fn iter_delimited<'a>(&'a self, delimited: Option<&'a tt::Delimiter>) -> OpDelimitedIter<'a> {
+ OpDelimitedIter { inner: &self.0, idx: 0, delimited }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum OpDelimited<'a> {
+ Op(&'a Op),
+ Open,
+ Close,
+}
+
+#[derive(Debug, Clone, Copy)]
+struct OpDelimitedIter<'a> {
+ inner: &'a [Op],
+ delimited: Option<&'a tt::Delimiter>,
+ idx: usize,
+}
+
+impl<'a> OpDelimitedIter<'a> {
+ fn is_eof(&self) -> bool {
+ let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
+ self.idx >= len
+ }
+
+ fn peek(&self) -> Option<OpDelimited<'a>> {
+ match self.delimited {
+ None => self.inner.get(self.idx).map(OpDelimited::Op),
+ Some(_) => match self.idx {
+ 0 => Some(OpDelimited::Open),
+ i if i == self.inner.len() + 1 => Some(OpDelimited::Close),
+ i => self.inner.get(i - 1).map(OpDelimited::Op),
+ },
+ }
+ }
+
+ fn reset(&self) -> Self {
+ Self { inner: self.inner, idx: 0, delimited: self.delimited }
+ }
+}
+
+impl<'a> Iterator for OpDelimitedIter<'a> {
+ type Item = OpDelimited<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let res = self.peek();
+ self.idx += 1;
+ res
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
+ let remain = len.saturating_sub(self.idx);
+ (remain, Some(remain))
+ }
+}
+
+impl<'a> TtIter<'a> {
+ fn expect_separator(&mut self, separator: &Separator, idx: usize) -> bool {
+ let mut fork = self.clone();
+ let ok = match separator {
+ Separator::Ident(lhs) if idx == 0 => match fork.expect_ident_or_underscore() {
+ Ok(rhs) => rhs.text == lhs.text,
+ Err(_) => false,
+ },
+ Separator::Literal(lhs) if idx == 0 => match fork.expect_literal() {
+ Ok(rhs) => match rhs {
+ tt::Leaf::Literal(rhs) => rhs.text == lhs.text,
+ tt::Leaf::Ident(rhs) => rhs.text == lhs.text,
+ tt::Leaf::Punct(_) => false,
+ },
+ Err(_) => false,
+ },
+ Separator::Puncts(lhss) if idx < lhss.len() => match fork.expect_punct() {
+ Ok(rhs) => rhs.char == lhss[idx].char,
+ Err(_) => false,
+ },
+ _ => false,
+ };
+ if ok {
+ *self = fork;
+ }
+ ok
+ }
+
+ fn expect_tt(&mut self) -> Result<tt::TokenTree, ()> {
+ match self.peek_n(0) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '\'' => {
+ return self.expect_lifetime();
+ }
+ _ => (),
+ }
+
+ let tt = self.next().ok_or(())?.clone();
+ let punct = match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => {
+ punct
+ }
+ _ => return Ok(tt),
+ };
+
+ let (second, third) = match (self.peek_n(0), self.peek_n(1)) {
+ (
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p2))),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p3))),
+ ) if p2.spacing == tt::Spacing::Joint => (p2.char, Some(p3.char)),
+ (Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p2))), _) => (p2.char, None),
+ _ => return Ok(tt),
+ };
+
+ match (punct.char, second, third) {
+ ('.', '.', Some('.' | '=')) | ('<', '<', Some('=')) | ('>', '>', Some('=')) => {
+ let tt2 = self.next().unwrap().clone();
+ let tt3 = self.next().unwrap().clone();
+ Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2, tt3] }.into())
+ }
+ ('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _)
+ | ('-' | '=' | '>', '>', _)
+ | (':', ':', _)
+ | ('.', '.', _)
+ | ('&', '&', _)
+ | ('<', '<', _)
+ | ('|', '|', _) => {
+ let tt2 = self.next().unwrap().clone();
+ Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into())
+ }
+ _ => Ok(tt),
+ }
+ }
+
+ fn expect_lifetime(&mut self) -> Result<tt::TokenTree, ()> {
+ let punct = self.expect_punct()?;
+ if punct.char != '\'' {
+ return Err(());
+ }
+ let ident = self.expect_ident_or_underscore()?;
+
+ Ok(tt::Subtree {
+ delimiter: None,
+ token_trees: vec![
+ tt::Leaf::Punct(*punct).into(),
+ tt::Leaf::Ident(ident.clone()).into(),
+ ],
+ }
+ .into())
+ }
+
+ fn eat_char(&mut self, c: char) -> Option<tt::TokenTree> {
+ let mut fork = self.clone();
+ match fork.expect_char(c) {
+ Ok(_) => {
+ let tt = self.next().cloned();
+ *self = fork;
+ tt
+ }
+ Err(_) => None,
+ }
+ }
+}