diff options
Diffstat (limited to 'crates/ra_macros/src')
-rw-r--r-- | crates/ra_macros/src/lib.rs | 14 | ||||
-rw-r--r-- | crates/ra_macros/src/mbe.rs | 249 | ||||
-rw-r--r-- | crates/ra_macros/src/tt.rs | 45 |
3 files changed, 308 insertions, 0 deletions
diff --git a/crates/ra_macros/src/lib.rs b/crates/ra_macros/src/lib.rs new file mode 100644 index 000000000..6063c06c2 --- /dev/null +++ b/crates/ra_macros/src/lib.rs | |||
@@ -0,0 +1,14 @@ | |||
1 | macro_rules! impl_froms { | ||
2 | ($e:ident: $($v:ident), *) => { | ||
3 | $( | ||
4 | impl From<$v> for $e { | ||
5 | fn from(it: $v) -> $e { | ||
6 | $e::$v(it) | ||
7 | } | ||
8 | } | ||
9 | )* | ||
10 | } | ||
11 | } | ||
12 | |||
13 | pub mod tt; | ||
14 | pub mod mbe; | ||
diff --git a/crates/ra_macros/src/mbe.rs b/crates/ra_macros/src/mbe.rs new file mode 100644 index 000000000..1aa6be736 --- /dev/null +++ b/crates/ra_macros/src/mbe.rs | |||
@@ -0,0 +1,249 @@ | |||
1 | use smol_str::SmolStr; | ||
2 | |||
3 | use crate::tt::{self, Delimiter}; | ||
4 | |||
5 | #[derive(Debug)] | ||
6 | pub struct MacroRules { | ||
7 | rules: Vec<Rule>, | ||
8 | } | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | struct Rule { | ||
12 | lhs: Subtree, | ||
13 | rhs: Subtree, | ||
14 | } | ||
15 | |||
16 | #[derive(Debug)] | ||
17 | enum TokenTree { | ||
18 | Leaf(Leaf), | ||
19 | Subtree(Subtree), | ||
20 | Repeat(Repeat), | ||
21 | } | ||
22 | impl_froms!(TokenTree: Leaf, Subtree, Repeat); | ||
23 | |||
24 | #[derive(Debug)] | ||
25 | enum Leaf { | ||
26 | Literal(Literal), | ||
27 | Punct(Punct), | ||
28 | Ident(Ident), | ||
29 | Var(Var), | ||
30 | } | ||
31 | impl_froms!(Leaf: Literal, Punct, Ident, Var); | ||
32 | |||
33 | #[derive(Debug)] | ||
34 | struct Subtree { | ||
35 | delimiter: Delimiter, | ||
36 | token_trees: Vec<TokenTree>, | ||
37 | } | ||
38 | |||
39 | #[derive(Debug)] | ||
40 | struct Repeat { | ||
41 | subtree: Subtree, | ||
42 | kind: RepeatKind, | ||
43 | separator: Option<Punct>, | ||
44 | } | ||
45 | |||
46 | #[derive(Debug)] | ||
47 | enum RepeatKind { | ||
48 | ZeroOrMore, | ||
49 | OneOrMore, | ||
50 | ZeroOrOne, | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | struct Literal { | ||
55 | text: SmolStr, | ||
56 | } | ||
57 | |||
58 | #[derive(Debug)] | ||
59 | struct Punct { | ||
60 | char: char, | ||
61 | } | ||
62 | |||
63 | #[derive(Debug)] | ||
64 | struct Ident { | ||
65 | text: SmolStr, | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
69 | struct Var { | ||
70 | text: SmolStr, | ||
71 | kind: Option<SmolStr>, | ||
72 | } | ||
73 | |||
74 | pub fn parse(tt: &tt::Subtree) -> Option<MacroRules> { | ||
75 | let mut parser = RulesParser::new(tt); | ||
76 | let mut rules = Vec::new(); | ||
77 | while !parser.is_eof() { | ||
78 | rules.push(parse_rule(&mut parser)?) | ||
79 | } | ||
80 | Some(MacroRules { rules }) | ||
81 | } | ||
82 | |||
83 | fn parse_rule(p: &mut RulesParser) -> Option<Rule> { | ||
84 | let lhs = parse_subtree(p.eat_subtree()?)?; | ||
85 | p.expect_char('=')?; | ||
86 | p.expect_char('>')?; | ||
87 | let rhs = parse_subtree(p.eat_subtree()?)?; | ||
88 | Some(Rule { lhs, rhs }) | ||
89 | } | ||
90 | |||
91 | fn parse_subtree(tt: &tt::Subtree) -> Option<Subtree> { | ||
92 | let mut token_trees = Vec::new(); | ||
93 | let mut p = RulesParser::new(tt); | ||
94 | while let Some(tt) = p.eat() { | ||
95 | let child: TokenTree = match tt { | ||
96 | tt::TokenTree::Leaf(leaf) => match leaf { | ||
97 | tt::Leaf::Punct(tt::Punct { char: '$' }) => { | ||
98 | if p.at_ident().is_some() { | ||
99 | Leaf::from(parse_var(&mut p)?).into() | ||
100 | } else { | ||
101 | parse_repeat(&mut p)?.into() | ||
102 | } | ||
103 | } | ||
104 | tt::Leaf::Punct(tt::Punct { char }) => Leaf::from(Punct { char: *char }).into(), | ||
105 | tt::Leaf::Ident(tt::Ident { text }) => { | ||
106 | Leaf::from(Ident { text: text.clone() }).into() | ||
107 | } | ||
108 | tt::Leaf::Literal(tt::Literal { text }) => { | ||
109 | Leaf::from(Literal { text: text.clone() }).into() | ||
110 | } | ||
111 | }, | ||
112 | tt::TokenTree::Subtree(subtree) => parse_subtree(subtree)?.into(), | ||
113 | }; | ||
114 | token_trees.push(child); | ||
115 | } | ||
116 | Some(Subtree { | ||
117 | token_trees, | ||
118 | delimiter: tt.delimiter, | ||
119 | }) | ||
120 | } | ||
121 | |||
122 | fn parse_var(p: &mut RulesParser) -> Option<Var> { | ||
123 | let ident = p.eat_ident().unwrap(); | ||
124 | let text = ident.text.clone(); | ||
125 | let kind = if p.at_char(':') { | ||
126 | p.bump(); | ||
127 | if let Some(ident) = p.eat_ident() { | ||
128 | Some(ident.text.clone()) | ||
129 | } else { | ||
130 | // ugly as hell :( | ||
131 | p.pos -= 1; | ||
132 | None | ||
133 | } | ||
134 | } else { | ||
135 | None | ||
136 | }; | ||
137 | Some(Var { text, kind }) | ||
138 | } | ||
139 | |||
140 | fn parse_repeat(p: &mut RulesParser) -> Option<Repeat> { | ||
141 | let subtree = p.eat_subtree().unwrap(); | ||
142 | let subtree = parse_subtree(subtree)?; | ||
143 | let sep = p.eat_punct()?; | ||
144 | let (separator, rep) = match sep.char { | ||
145 | '*' | '+' | '?' => (None, sep.char), | ||
146 | char => (Some(Punct { char }), p.eat_punct()?.char), | ||
147 | }; | ||
148 | |||
149 | let kind = match rep { | ||
150 | '*' => RepeatKind::ZeroOrMore, | ||
151 | '+' => RepeatKind::OneOrMore, | ||
152 | '?' => RepeatKind::ZeroOrMore, | ||
153 | _ => return None, | ||
154 | }; | ||
155 | p.bump(); | ||
156 | Some(Repeat { | ||
157 | subtree, | ||
158 | kind, | ||
159 | separator, | ||
160 | }) | ||
161 | } | ||
162 | |||
163 | struct RulesParser<'a> { | ||
164 | subtree: &'a tt::Subtree, | ||
165 | pos: usize, | ||
166 | } | ||
167 | |||
168 | impl<'a> RulesParser<'a> { | ||
169 | fn new(subtree: &'a tt::Subtree) -> RulesParser<'a> { | ||
170 | RulesParser { subtree, pos: 0 } | ||
171 | } | ||
172 | |||
173 | fn is_eof(&self) -> bool { | ||
174 | self.pos == self.subtree.token_trees.len() | ||
175 | } | ||
176 | |||
177 | fn current(&self) -> Option<&'a tt::TokenTree> { | ||
178 | self.subtree.token_trees.get(self.pos) | ||
179 | } | ||
180 | |||
181 | fn at_punct(&self) -> Option<&'a tt::Punct> { | ||
182 | match self.current() { | ||
183 | Some(tt::TokenTree::Leaf(tt::Leaf::Punct(it))) => Some(it), | ||
184 | _ => None, | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn at_char(&self, char: char) -> bool { | ||
189 | match self.at_punct() { | ||
190 | Some(tt::Punct { char: c }) if *c == char => true, | ||
191 | _ => false, | ||
192 | } | ||
193 | } | ||
194 | |||
195 | fn at_ident(&mut self) -> Option<&'a tt::Ident> { | ||
196 | match self.current() { | ||
197 | Some(tt::TokenTree::Leaf(tt::Leaf::Ident(i))) => Some(i), | ||
198 | _ => None, | ||
199 | } | ||
200 | } | ||
201 | |||
202 | fn bump(&mut self) { | ||
203 | self.pos += 1; | ||
204 | } | ||
205 | |||
206 | fn eat(&mut self) -> Option<&'a tt::TokenTree> { | ||
207 | match self.current() { | ||
208 | Some(it) => { | ||
209 | self.bump(); | ||
210 | Some(it) | ||
211 | } | ||
212 | None => None, | ||
213 | } | ||
214 | } | ||
215 | |||
216 | fn eat_subtree(&mut self) -> Option<&'a tt::Subtree> { | ||
217 | match self.current()? { | ||
218 | tt::TokenTree::Subtree(sub) => { | ||
219 | self.bump(); | ||
220 | Some(sub) | ||
221 | } | ||
222 | _ => return None, | ||
223 | } | ||
224 | } | ||
225 | |||
226 | fn eat_punct(&mut self) -> Option<&'a tt::Punct> { | ||
227 | if let Some(it) = self.at_punct() { | ||
228 | self.bump(); | ||
229 | return Some(it); | ||
230 | } | ||
231 | None | ||
232 | } | ||
233 | |||
234 | fn eat_ident(&mut self) -> Option<&'a tt::Ident> { | ||
235 | if let Some(i) = self.at_ident() { | ||
236 | self.bump(); | ||
237 | return Some(i); | ||
238 | } | ||
239 | None | ||
240 | } | ||
241 | |||
242 | fn expect_char(&mut self, char: char) -> Option<()> { | ||
243 | if self.at_char(char) { | ||
244 | self.bump(); | ||
245 | return Some(()); | ||
246 | } | ||
247 | None | ||
248 | } | ||
249 | } | ||
diff --git a/crates/ra_macros/src/tt.rs b/crates/ra_macros/src/tt.rs new file mode 100644 index 000000000..364eed9e6 --- /dev/null +++ b/crates/ra_macros/src/tt.rs | |||
@@ -0,0 +1,45 @@ | |||
1 | use smol_str::SmolStr; | ||
2 | |||
3 | #[derive(Debug)] | ||
4 | pub enum TokenTree { | ||
5 | Leaf(Leaf), | ||
6 | Subtree(Subtree), | ||
7 | } | ||
8 | impl_froms!(TokenTree: Leaf, Subtree); | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | pub enum Leaf { | ||
12 | Literal(Literal), | ||
13 | Punct(Punct), | ||
14 | Ident(Ident), | ||
15 | } | ||
16 | impl_froms!(Leaf: Literal, Punct, Ident); | ||
17 | |||
18 | #[derive(Debug)] | ||
19 | pub struct Subtree { | ||
20 | pub delimiter: Delimiter, | ||
21 | pub token_trees: Vec<TokenTree>, | ||
22 | } | ||
23 | |||
24 | #[derive(Clone, Copy, Debug)] | ||
25 | pub enum Delimiter { | ||
26 | Parenthesis, | ||
27 | Brace, | ||
28 | Bracket, | ||
29 | None, | ||
30 | } | ||
31 | |||
32 | #[derive(Debug)] | ||
33 | pub struct Literal { | ||
34 | pub text: SmolStr, | ||
35 | } | ||
36 | |||
37 | #[derive(Debug)] | ||
38 | pub struct Punct { | ||
39 | pub char: char, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug)] | ||
43 | pub struct Ident { | ||
44 | pub text: SmolStr, | ||
45 | } | ||