aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/macros.rs114
-rw-r--r--crates/ra_mbe/Cargo.toml3
-rw-r--r--crates/ra_mbe/src/lib.rs9
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs2
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs113
-rw-r--r--crates/ra_tt/Cargo.toml1
-rw-r--r--crates/ra_tt/src/lib.rs4
7 files changed, 128 insertions, 118 deletions
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs
index ffcf1c3f9..7ca34d434 100644
--- a/crates/ra_hir/src/macros.rs
+++ b/crates/ra_hir/src/macros.rs
@@ -11,7 +11,6 @@ use std::sync::Arc;
11 11
12use ra_syntax::{ 12use ra_syntax::{
13 TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr, 13 TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr,
14 SyntaxKind::*,
15 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
16}; 15};
17 16
@@ -196,116 +195,3 @@ pub(crate) fn expand_macro_invocation(
196 let (def, input) = MacroDef::from_call(macro_call)?; 195 let (def, input) = MacroDef::from_call(macro_call)?;
197 def.expand(input).map(Arc::new) 196 def.expand(input).map(Arc::new)
198} 197}
199
200fn macro_call_to_tt(call: &ast::MacroCall) -> Option<tt::Subtree> {
201 let tt = call.token_tree()?;
202 convert_tt(tt.syntax())
203}
204
205fn convert_tt(tt: &SyntaxNode) -> Option<tt::Subtree> {
206 let first_child = tt.first_child()?;
207 let last_child = tt.last_child()?;
208 let delimiter = match (first_child.kind(), last_child.kind()) {
209 (L_PAREN, R_PAREN) => tt::Delimiter::Parenthesis,
210 (L_CURLY, R_CURLY) => tt::Delimiter::Brace,
211 (L_BRACK, R_BRACK) => tt::Delimiter::Bracket,
212 _ => return None,
213 };
214 let mut token_trees = Vec::new();
215 for child in tt.children().skip(1) {
216 if child == first_child || child == last_child || child.kind().is_trivia() {
217 continue;
218 }
219 if child.kind().is_punct() {
220 let mut prev = None;
221 for char in child.leaf_text().unwrap().chars() {
222 if let Some(char) = prev {
223 token_trees.push(
224 tt::Leaf::from(tt::Punct {
225 char,
226 spacing: tt::Spacing::Joint,
227 })
228 .into(),
229 );
230 }
231 prev = Some(char)
232 }
233 if let Some(char) = prev {
234 token_trees.push(
235 tt::Leaf::from(tt::Punct {
236 char,
237 spacing: tt::Spacing::Alone,
238 })
239 .into(),
240 );
241 }
242 } else {
243 let child: tt::TokenTree = if child.kind() == TOKEN_TREE {
244 convert_tt(child)?.into()
245 } else if child.kind().is_keyword() || child.kind() == IDENT {
246 let text = child.leaf_text().unwrap().clone();
247 tt::Leaf::from(tt::Ident { text }).into()
248 } else if child.kind().is_literal() {
249 tt::Leaf::from(tt::Literal {
250 text: child.leaf_text().unwrap().clone(),
251 })
252 .into()
253 } else {
254 log::error!("unknown kind: {:?}", child);
255 return None;
256 };
257 token_trees.push(child)
258 }
259 }
260
261 let res = tt::Subtree {
262 delimiter,
263 token_trees,
264 };
265 Some(res)
266}
267
268#[test]
269fn test_convert_tt() {
270 let macro_definition = r#"
271macro_rules! impl_froms {
272 ($e:ident: $($v:ident),*) => {
273 $(
274 impl From<$v> for $e {
275 fn from(it: $v) -> $e {
276 $e::$v(it)
277 }
278 }
279 )*
280 }
281}
282"#;
283
284 let macro_invocation = r#"
285impl_froms!(TokenTree: Leaf, Subtree);
286"#;
287
288 let source_file = ast::SourceFile::parse(macro_definition);
289 let macro_definition = source_file
290 .syntax()
291 .descendants()
292 .find_map(ast::MacroCall::cast)
293 .unwrap();
294
295 let source_file = ast::SourceFile::parse(macro_invocation);
296 let macro_invocation = source_file
297 .syntax()
298 .descendants()
299 .find_map(ast::MacroCall::cast)
300 .unwrap();
301
302 let definition_tt = macro_call_to_tt(macro_definition).unwrap();
303 let invocation_tt = macro_call_to_tt(macro_invocation).unwrap();
304 let mbe = mbe::parse(&definition_tt).unwrap();
305 let expansion = mbe::exapnd(&mbe, &invocation_tt).unwrap();
306 assert_eq!(
307 expansion.to_string(),
308 "{(impl From < Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree :: Leaf (it)}}) \
309 (impl From < Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree :: Subtree (it)}})}"
310 )
311}
diff --git a/crates/ra_mbe/Cargo.toml b/crates/ra_mbe/Cargo.toml
index b7f03cd38..68dc85eb9 100644
--- a/crates/ra_mbe/Cargo.toml
+++ b/crates/ra_mbe/Cargo.toml
@@ -5,6 +5,7 @@ version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8ra_syntax = { path = "../ra_syntax" }
8tt = { path = "../ra_tt", package = "ra_tt" } 9tt = { path = "../ra_tt", package = "ra_tt" }
10
9rustc-hash = "1.0.0" 11rustc-hash = "1.0.0"
10smol_str = "0.1.9"
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index 38bf3431a..c7be33b19 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -1,3 +1,8 @@
1/// `mbe` (short for Macro By Example) crate contains code for handling
2/// `macro_rules` macros. It uses `TokenTree` (from `ra_tt` package) as the
3/// interface, although it contains some code to bridge `SyntaxNode`s and
4/// `TokenTree`s as well!
5
1macro_rules! impl_froms { 6macro_rules! impl_froms {
2 ($e:ident: $($v:ident), *) => { 7 ($e:ident: $($v:ident), *) => {
3 $( 8 $(
@@ -13,14 +18,16 @@ macro_rules! impl_froms {
13mod tt_cursor; 18mod tt_cursor;
14mod mbe_parser; 19mod mbe_parser;
15mod mbe_expander; 20mod mbe_expander;
21mod syntax_bridge;
16 22
17use smol_str::SmolStr; 23use ra_syntax::SmolStr;
18 24
19pub use tt::{Delimiter, Punct}; 25pub use tt::{Delimiter, Punct};
20 26
21pub use crate::{ 27pub use crate::{
22 mbe_parser::parse, 28 mbe_parser::parse,
23 mbe_expander::exapnd, 29 mbe_expander::exapnd,
30 syntax_bridge::macro_call_to_tt,
24}; 31};
25 32
26#[derive(Debug)] 33#[derive(Debug)]
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs
index 92ad26889..21c1552ce 100644
--- a/crates/ra_mbe/src/mbe_expander.rs
+++ b/crates/ra_mbe/src/mbe_expander.rs
@@ -1,5 +1,5 @@
1use rustc_hash::FxHashMap; 1use rustc_hash::FxHashMap;
2use smol_str::SmolStr; 2use ra_syntax::SmolStr;
3 3
4use crate::{self as mbe, tt_cursor::TtCursor}; 4use crate::{self as mbe, tt_cursor::TtCursor};
5 5
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
new file mode 100644
index 000000000..aad5f24b7
--- /dev/null
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -0,0 +1,113 @@
1use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxKind::*};
2
3pub fn macro_call_to_tt(call: &ast::MacroCall) -> Option<tt::Subtree> {
4 let tt = call.token_tree()?;
5 convert_tt(tt.syntax())
6}
7
8fn convert_tt(tt: &SyntaxNode) -> Option<tt::Subtree> {
9 let first_child = tt.first_child()?;
10 let last_child = tt.last_child()?;
11 let delimiter = match (first_child.kind(), last_child.kind()) {
12 (L_PAREN, R_PAREN) => tt::Delimiter::Parenthesis,
13 (L_CURLY, R_CURLY) => tt::Delimiter::Brace,
14 (L_BRACK, R_BRACK) => tt::Delimiter::Bracket,
15 _ => return None,
16 };
17 let mut token_trees = Vec::new();
18 for child in tt.children().skip(1) {
19 if child == first_child || child == last_child || child.kind().is_trivia() {
20 continue;
21 }
22 if child.kind().is_punct() {
23 let mut prev = None;
24 for char in child.leaf_text().unwrap().chars() {
25 if let Some(char) = prev {
26 token_trees.push(
27 tt::Leaf::from(tt::Punct {
28 char,
29 spacing: tt::Spacing::Joint,
30 })
31 .into(),
32 );
33 }
34 prev = Some(char)
35 }
36 if let Some(char) = prev {
37 token_trees.push(
38 tt::Leaf::from(tt::Punct {
39 char,
40 spacing: tt::Spacing::Alone,
41 })
42 .into(),
43 );
44 }
45 } else {
46 let child: tt::TokenTree = if child.kind() == TOKEN_TREE {
47 convert_tt(child)?.into()
48 } else if child.kind().is_keyword() || child.kind() == IDENT {
49 let text = child.leaf_text().unwrap().clone();
50 tt::Leaf::from(tt::Ident { text }).into()
51 } else if child.kind().is_literal() {
52 tt::Leaf::from(tt::Literal {
53 text: child.leaf_text().unwrap().clone(),
54 })
55 .into()
56 } else {
57 return None;
58 };
59 token_trees.push(child)
60 }
61 }
62
63 let res = tt::Subtree {
64 delimiter,
65 token_trees,
66 };
67 Some(res)
68}
69
70#[test]
71fn test_convert_tt() {
72 let macro_definition = r#"
73macro_rules! impl_froms {
74 ($e:ident: $($v:ident),*) => {
75 $(
76 impl From<$v> for $e {
77 fn from(it: $v) -> $e {
78 $e::$v(it)
79 }
80 }
81 )*
82 }
83}
84"#;
85
86 let macro_invocation = r#"
87impl_froms!(TokenTree: Leaf, Subtree);
88"#;
89
90 let source_file = ast::SourceFile::parse(macro_definition);
91 let macro_definition = source_file
92 .syntax()
93 .descendants()
94 .find_map(ast::MacroCall::cast)
95 .unwrap();
96
97 let source_file = ast::SourceFile::parse(macro_invocation);
98 let macro_invocation = source_file
99 .syntax()
100 .descendants()
101 .find_map(ast::MacroCall::cast)
102 .unwrap();
103
104 let definition_tt = macro_call_to_tt(macro_definition).unwrap();
105 let invocation_tt = macro_call_to_tt(macro_invocation).unwrap();
106 let mbe = crate::parse(&definition_tt).unwrap();
107 let expansion = crate::exapnd(&mbe, &invocation_tt).unwrap();
108 assert_eq!(
109 expansion.to_string(),
110 "{(impl From < Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree :: Leaf (it)}}) \
111 (impl From < Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree :: Subtree (it)}})}"
112 )
113}
diff --git a/crates/ra_tt/Cargo.toml b/crates/ra_tt/Cargo.toml
index 357a5c5a3..02accd404 100644
--- a/crates/ra_tt/Cargo.toml
+++ b/crates/ra_tt/Cargo.toml
@@ -5,5 +5,4 @@ version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8rustc-hash = "1.0.0"
9smol_str = "0.1.9" 8smol_str = "0.1.9"
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index d7c3c62bf..043417abc 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -1,3 +1,7 @@
1/// `tt` crate defines a `TokenTree` datastructure: this is the interface (both
2/// input and output) of macros. It closely mirrors `proc_macro` crate's
3/// `TokenTree`.
4
1macro_rules! impl_froms { 5macro_rules! impl_froms {
2 ($e:ident: $($v:ident), *) => { 6 ($e:ident: $($v:ident), *) => {
3 $( 7 $(