aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_mbe/src/mbe_expander.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs
new file mode 100644
index 000000000..92ad26889
--- /dev/null
+++ b/crates/ra_mbe/src/mbe_expander.rs
@@ -0,0 +1,169 @@
1use rustc_hash::FxHashMap;
2use smol_str::SmolStr;
3
4use crate::{self as mbe, tt_cursor::TtCursor};
5
6pub fn exapnd(rules: &mbe::MacroRules, input: &tt::Subtree) -> Option<tt::Subtree> {
7 rules.rules.iter().find_map(|it| expand_rule(it, input))
8}
9
10fn expand_rule(rule: &mbe::Rule, input: &tt::Subtree) -> Option<tt::Subtree> {
11 let mut input = TtCursor::new(input);
12 let bindings = match_lhs(&rule.lhs, &mut input)?;
13 expand_subtree(&rule.rhs, &bindings, &mut Vec::new())
14}
15
16#[derive(Debug, Default)]
17struct Bindings {
18 inner: FxHashMap<SmolStr, Binding>,
19}
20
21#[derive(Debug)]
22enum Binding {
23 Simple(tt::TokenTree),
24 Nested(Vec<Binding>),
25}
26
27impl Bindings {
28 fn get(&self, name: &SmolStr, nesting: &[usize]) -> Option<&tt::TokenTree> {
29 let mut b = self.inner.get(name)?;
30 for &idx in nesting.iter() {
31 b = match b {
32 Binding::Simple(_) => break,
33 Binding::Nested(bs) => bs.get(idx)?,
34 };
35 }
36 match b {
37 Binding::Simple(it) => Some(it),
38 Binding::Nested(_) => None,
39 }
40 }
41 fn push_nested(&mut self, nested: Bindings) -> Option<()> {
42 for (key, value) in nested.inner {
43 if !self.inner.contains_key(&key) {
44 self.inner.insert(key.clone(), Binding::Nested(Vec::new()));
45 }
46 match self.inner.get_mut(&key) {
47 Some(Binding::Nested(it)) => it.push(value),
48 _ => return None,
49 }
50 }
51 Some(())
52 }
53}
54
55fn match_lhs(pattern: &mbe::Subtree, input: &mut TtCursor) -> Option<Bindings> {
56 let mut res = Bindings::default();
57 for pat in pattern.token_trees.iter() {
58 match pat {
59 mbe::TokenTree::Leaf(leaf) => match leaf {
60 mbe::Leaf::Var(mbe::Var { text, kind }) => {
61 let kind = kind.clone()?;
62 match kind.as_str() {
63 "ident" => {
64 let ident = input.eat_ident()?.clone();
65 res.inner.insert(
66 text.clone(),
67 Binding::Simple(tt::Leaf::from(ident).into()),
68 );
69 }
70 _ => return None,
71 }
72 }
73 mbe::Leaf::Punct(punct) => {
74 if input.eat_punct()? != punct {
75 return None;
76 }
77 }
78 _ => return None,
79 },
80 mbe::TokenTree::Repeat(mbe::Repeat {
81 subtree,
82 kind: _,
83 separator,
84 }) => {
85 while let Some(nested) = match_lhs(subtree, input) {
86 res.push_nested(nested)?;
87 if separator.is_some() && !input.is_eof() {
88 input.eat_punct()?;
89 }
90 }
91 }
92 _ => {}
93 }
94 }
95 Some(res)
96}
97
98/*
99
100macro_rules! impl_froms {
101 ($e:ident: $($v:ident),*) => {
102 $(
103 impl From<$v> for $e {
104 fn from(it: $v) -> $e {
105 $e::$v(it)
106 }
107 }
108 )*
109 }
110}
111
112impl_froms! (Foo: Bar, Baz)
113
114*/
115
116fn expand_subtree(
117 template: &mbe::Subtree,
118 bindings: &Bindings,
119 nesting: &mut Vec<usize>,
120) -> Option<tt::Subtree> {
121 let token_trees = template
122 .token_trees
123 .iter()
124 .map(|it| expand_tt(it, bindings, nesting))
125 .collect::<Option<Vec<_>>>()?;
126
127 Some(tt::Subtree {
128 token_trees,
129 delimiter: template.delimiter,
130 })
131}
132
133fn expand_tt(
134 template: &mbe::TokenTree,
135 bindings: &Bindings,
136 nesting: &mut Vec<usize>,
137) -> Option<tt::TokenTree> {
138 let res: tt::TokenTree = match template {
139 mbe::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(),
140 mbe::TokenTree::Repeat(repeat) => {
141 let mut token_trees = Vec::new();
142 nesting.push(0);
143 while let Some(t) = expand_subtree(&repeat.subtree, bindings, nesting) {
144 let idx = nesting.pop().unwrap();
145 nesting.push(idx + 1);
146 token_trees.push(t.into())
147 }
148 nesting.pop().unwrap();
149 tt::Subtree {
150 token_trees,
151 delimiter: tt::Delimiter::None,
152 }
153 .into()
154 }
155 mbe::TokenTree::Leaf(leaf) => match leaf {
156 mbe::Leaf::Ident(ident) => tt::Leaf::from(tt::Ident {
157 text: ident.text.clone(),
158 })
159 .into(),
160 mbe::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(),
161 mbe::Leaf::Var(v) => bindings.get(&v.text, nesting)?.clone(),
162 mbe::Leaf::Literal(l) => tt::Leaf::from(tt::Literal {
163 text: l.text.clone(),
164 })
165 .into(),
166 },
167 };
168 Some(res)
169}