aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_mbe/src/syntax_bridge.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_mbe/src/syntax_bridge.rs')
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs113
1 files changed, 113 insertions, 0 deletions
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}