aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_mbe
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_mbe')
-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
4 files changed, 124 insertions, 3 deletions
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}