diff options
Diffstat (limited to 'crates/ra_tools')
-rw-r--r-- | crates/ra_tools/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_tools/src/codegen.rs | 165 |
2 files changed, 153 insertions, 13 deletions
diff --git a/crates/ra_tools/Cargo.toml b/crates/ra_tools/Cargo.toml index ab9fa5d86..1bb6fb71c 100644 --- a/crates/ra_tools/Cargo.toml +++ b/crates/ra_tools/Cargo.toml | |||
@@ -12,4 +12,5 @@ itertools = "0.8.0" | |||
12 | clap = "2.32.0" | 12 | clap = "2.32.0" |
13 | quote = "1.0.2" | 13 | quote = "1.0.2" |
14 | ron = "0.5.1" | 14 | ron = "0.5.1" |
15 | heck = "0.3.0" | ||
15 | serde = { version = "1.0.0", features = ["derive"] } | 16 | serde = { version = "1.0.0", features = ["derive"] } |
diff --git a/crates/ra_tools/src/codegen.rs b/crates/ra_tools/src/codegen.rs index f0a54808a..e14092704 100644 --- a/crates/ra_tools/src/codegen.rs +++ b/crates/ra_tools/src/codegen.rs | |||
@@ -1,27 +1,162 @@ | |||
1 | use std::{collections::BTreeMap, fs, path::Path}; | 1 | use std::{ |
2 | collections::BTreeMap, | ||
3 | fs, | ||
4 | io::Write, | ||
5 | path::Path, | ||
6 | process::{Command, Stdio}, | ||
7 | }; | ||
2 | 8 | ||
3 | use quote::quote; | 9 | use heck::{ShoutySnakeCase, SnakeCase}; |
10 | use quote::{format_ident, quote}; | ||
4 | use ron; | 11 | use ron; |
5 | use serde::Deserialize; | 12 | use serde::Deserialize; |
6 | 13 | ||
7 | use crate::{project_root, Mode, Result, AST, GRAMMAR}; | 14 | use crate::{project_root, Mode, Result, AST, GRAMMAR, SYNTAX_KINDS}; |
8 | 15 | ||
9 | pub fn generate(mode: Mode) -> Result<()> { | 16 | pub fn generate(mode: Mode) -> Result<()> { |
10 | let grammar = project_root().join(GRAMMAR); | 17 | let grammar = project_root().join(GRAMMAR); |
11 | // let syntax_kinds = project_root().join(SYNTAX_KINDS); | 18 | let grammar: Grammar = { |
12 | let ast = project_root().join(AST); | 19 | let text = fs::read_to_string(grammar)?; |
13 | generate_ast(&grammar, &ast, mode) | ||
14 | } | ||
15 | |||
16 | fn generate_ast(grammar_src: &Path, dst: &Path, mode: Mode) -> Result<()> { | ||
17 | let src: Grammar = { | ||
18 | let text = fs::read_to_string(grammar_src)?; | ||
19 | ron::de::from_str(&text)? | 20 | ron::de::from_str(&text)? |
20 | }; | 21 | }; |
21 | eprintln!("{:#?}", src); | 22 | |
23 | let _syntax_kinds = project_root().join(SYNTAX_KINDS); | ||
24 | let _ast = project_root().join(AST); | ||
25 | |||
26 | let ast = generate_ast(&grammar)?; | ||
27 | println!("{}", ast); | ||
22 | Ok(()) | 28 | Ok(()) |
23 | } | 29 | } |
24 | 30 | ||
31 | fn generate_ast(grammar: &Grammar) -> Result<String> { | ||
32 | let nodes = grammar.ast.iter().map(|(name, ast_node)| { | ||
33 | let variants = | ||
34 | ast_node.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>(); | ||
35 | let name = format_ident!("{}", name); | ||
36 | |||
37 | let kinds = if variants.is_empty() { vec![name.clone()] } else { variants.clone() } | ||
38 | .into_iter() | ||
39 | .map(|name| format_ident!("{}", name.to_string().to_shouty_snake_case())) | ||
40 | .collect::<Vec<_>>(); | ||
41 | |||
42 | let variants = if variants.is_empty() { | ||
43 | None | ||
44 | } else { | ||
45 | let kind_enum = format_ident!("{}Kind", name); | ||
46 | Some(quote!( | ||
47 | pub enum #kind_enum { | ||
48 | #(#variants(#variants),)* | ||
49 | } | ||
50 | |||
51 | #( | ||
52 | impl From<#variants> for #name { | ||
53 | fn from(node: #variants) -> #name { | ||
54 | #name { syntax: node.syntax } | ||
55 | } | ||
56 | } | ||
57 | )* | ||
58 | |||
59 | impl #name { | ||
60 | pub fn kind(&self) -> #kind_enum { | ||
61 | let syntax = self.syntax.clone(); | ||
62 | match syntax.kind() { | ||
63 | #( | ||
64 | #kinds => | ||
65 | #kind_enum::#variants(#variants { syntax }), | ||
66 | )* | ||
67 | _ => unreachable!(), | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | )) | ||
72 | }; | ||
73 | |||
74 | let traits = ast_node.traits.iter().map(|trait_name| { | ||
75 | let trait_name = format_ident!("{}", trait_name); | ||
76 | quote!(impl ast::#trait_name for #name {}) | ||
77 | }); | ||
78 | |||
79 | let collections = ast_node.collections.iter().map(|(name, kind)| { | ||
80 | let method_name = format_ident!("{}", name); | ||
81 | let kind = format_ident!("{}", kind); | ||
82 | quote! { | ||
83 | pub fn #method_name(&self) -> AstChildren<#kind> { | ||
84 | AstChildren::new(&self.syntax) | ||
85 | } | ||
86 | } | ||
87 | }); | ||
88 | |||
89 | let options = ast_node.options.iter().map(|attr| { | ||
90 | let method_name = match attr { | ||
91 | Attr::Type(t) => format_ident!("{}", t.to_snake_case()), | ||
92 | Attr::NameType(n, _) => format_ident!("{}", n), | ||
93 | }; | ||
94 | let ty = match attr { | ||
95 | Attr::Type(t) | Attr::NameType(_, t) => format_ident!("{}", t), | ||
96 | }; | ||
97 | quote! { | ||
98 | pub fn #method_name(&self) -> Option<#ty> { | ||
99 | AstChildren::new(&self.syntax).next() | ||
100 | } | ||
101 | } | ||
102 | }); | ||
103 | |||
104 | quote! { | ||
105 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
106 | pub struct #name { | ||
107 | pub(crate) syntax: SyntaxNode, | ||
108 | } | ||
109 | |||
110 | impl AstNode for #name { | ||
111 | fn can_cast(kind: SyntaxKind) -> bool { | ||
112 | match kind { | ||
113 | #(#kinds)|* => true, | ||
114 | _ => false, | ||
115 | } | ||
116 | } | ||
117 | fn cast(syntax: SyntaxNode) -> Option<Self> { | ||
118 | if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } | ||
119 | } | ||
120 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | ||
121 | } | ||
122 | |||
123 | #variants | ||
124 | |||
125 | #(#traits)* | ||
126 | |||
127 | impl #name { | ||
128 | #(#collections)* | ||
129 | #(#options)* | ||
130 | } | ||
131 | } | ||
132 | }); | ||
133 | |||
134 | let ast = quote! { | ||
135 | use crate::{ | ||
136 | SyntaxNode, SyntaxKind::{self, *}, | ||
137 | ast::{self, AstNode, AstChildren}, | ||
138 | }; | ||
139 | |||
140 | #(#nodes)* | ||
141 | }; | ||
142 | |||
143 | let pretty = reformat(ast)?; | ||
144 | Ok(pretty) | ||
145 | } | ||
146 | |||
147 | fn reformat(text: impl std::fmt::Display) -> Result<String> { | ||
148 | let mut rustfmt = Command::new("rustfmt") | ||
149 | .arg("--config-path") | ||
150 | .arg(project_root().join("rustfmt.toml")) | ||
151 | .stdin(Stdio::piped()) | ||
152 | .stdout(Stdio::piped()) | ||
153 | .spawn()?; | ||
154 | write!(rustfmt.stdin.take().unwrap(), "{}", text)?; | ||
155 | let output = rustfmt.wait_with_output()?; | ||
156 | let stdout = String::from_utf8(output.stdout)?; | ||
157 | Ok(stdout) | ||
158 | } | ||
159 | |||
25 | #[derive(Deserialize, Debug)] | 160 | #[derive(Deserialize, Debug)] |
26 | struct Grammar { | 161 | struct Grammar { |
27 | single_byte_tokens: Vec<(String, String)>, | 162 | single_byte_tokens: Vec<(String, String)>, |
@@ -36,9 +171,13 @@ struct Grammar { | |||
36 | #[derive(Deserialize, Debug)] | 171 | #[derive(Deserialize, Debug)] |
37 | struct AstNode { | 172 | struct AstNode { |
38 | #[serde(default)] | 173 | #[serde(default)] |
174 | #[serde(rename = "enum")] | ||
175 | variants: Vec<String>, | ||
176 | |||
177 | #[serde(default)] | ||
39 | traits: Vec<String>, | 178 | traits: Vec<String>, |
40 | #[serde(default)] | 179 | #[serde(default)] |
41 | collections: Vec<Attr>, | 180 | collections: Vec<(String, String)>, |
42 | #[serde(default)] | 181 | #[serde(default)] |
43 | options: Vec<Attr>, | 182 | options: Vec<Attr>, |
44 | } | 183 | } |