aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_tools
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-08-18 21:11:08 +0100
committerAleksey Kladov <[email protected]>2019-08-18 21:11:08 +0100
commit8cefdb5527d011d7d5ca2902791b7c3da0276fec (patch)
treeb40d906a23b8ffa37b40edd4916a13fd9e6f4ab0 /crates/ra_tools
parentd545a5c75cb181758dd745b031eacfd7fc8a6929 (diff)
use quote! macro to generate grammar
We already use syn&quote elsewhere (transitively), so it make sense to cut down on the number of technologies and get rid of tera
Diffstat (limited to 'crates/ra_tools')
-rw-r--r--crates/ra_tools/Cargo.toml1
-rw-r--r--crates/ra_tools/src/codegen.rs165
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"
12clap = "2.32.0" 12clap = "2.32.0"
13quote = "1.0.2" 13quote = "1.0.2"
14ron = "0.5.1" 14ron = "0.5.1"
15heck = "0.3.0"
15serde = { version = "1.0.0", features = ["derive"] } 16serde = { 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 @@
1use std::{collections::BTreeMap, fs, path::Path}; 1use std::{
2 collections::BTreeMap,
3 fs,
4 io::Write,
5 path::Path,
6 process::{Command, Stdio},
7};
2 8
3use quote::quote; 9use heck::{ShoutySnakeCase, SnakeCase};
10use quote::{format_ident, quote};
4use ron; 11use ron;
5use serde::Deserialize; 12use serde::Deserialize;
6 13
7use crate::{project_root, Mode, Result, AST, GRAMMAR}; 14use crate::{project_root, Mode, Result, AST, GRAMMAR, SYNTAX_KINDS};
8 15
9pub fn generate(mode: Mode) -> Result<()> { 16pub 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
16fn 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
31fn 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
147fn 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)]
26struct Grammar { 161struct 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)]
37struct AstNode { 172struct 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}