aboutsummaryrefslogtreecommitdiff
path: root/crates/tools/src
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-10 20:33:29 +0100
committerAleksey Kladov <[email protected]>2018-08-10 20:33:29 +0100
commit7c67612b8a894187fa3b64725531a5459f9211bf (patch)
tree9e2a536efa0c880d921fd8d4d74423afc9451fd4 /crates/tools/src
parent26262aaf05983c5b7f41cc438e287523268fe1eb (diff)
organizize
Diffstat (limited to 'crates/tools/src')
-rw-r--r--crates/tools/src/lib.rs43
-rw-r--r--crates/tools/src/main.rs216
2 files changed, 259 insertions, 0 deletions
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
new file mode 100644
index 000000000..97a56a31f
--- /dev/null
+++ b/crates/tools/src/lib.rs
@@ -0,0 +1,43 @@
1extern crate itertools;
2
3use itertools::Itertools;
4
5#[derive(Debug)]
6pub struct Test {
7 pub name: String,
8 pub text: String,
9}
10
11pub fn collect_tests(s: &str) -> Vec<(usize, Test)> {
12 let mut res = vec![];
13 let prefix = "// ";
14 let comment_blocks = s
15 .lines()
16 .map(str::trim_left)
17 .enumerate()
18 .group_by(|(_idx, line)| line.starts_with(prefix));
19
20 'outer: for (is_comment, block) in comment_blocks.into_iter() {
21 if !is_comment {
22 continue;
23 }
24 let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..]));
25
26 let (start_line, name) = loop {
27 match block.next() {
28 Some((idx, line)) if line.starts_with("test ") => {
29 break (idx, line["test ".len()..].to_string())
30 }
31 Some(_) => (),
32 None => continue 'outer,
33 }
34 };
35 let text: String = itertools::join(
36 block.map(|(_, line)| line).chain(::std::iter::once("")),
37 "\n",
38 );
39 assert!(!text.trim().is_empty() && text.ends_with("\n"));
40 res.push((start_line, Test { name, text }))
41 }
42 res
43}
diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs
new file mode 100644
index 000000000..d42d3ecb7
--- /dev/null
+++ b/crates/tools/src/main.rs
@@ -0,0 +1,216 @@
1extern crate clap;
2#[macro_use]
3extern crate failure;
4extern crate ron;
5extern crate tera;
6extern crate tools;
7extern crate walkdir;
8#[macro_use]
9extern crate commandspec;
10
11use clap::{App, Arg, SubCommand};
12use std::{
13 collections::HashMap,
14 fs,
15 path::{Path, PathBuf},
16};
17use tools::{collect_tests, Test};
18
19type Result<T> = ::std::result::Result<T, failure::Error>;
20
21const GRAMMAR_DIR: &str = "./crates/libsyntax2/src/grammar";
22const INLINE_TESTS_DIR: &str = "./crates/libsyntax2/tests/data/parser/inline";
23const GRAMMAR: &str = "./crates/libsyntax2/src/grammar.ron";
24const SYNTAX_KINDS: &str = "./crates/libsyntax2/src/syntax_kinds/generated.rs";
25const SYNTAX_KINDS_TEMPLATE: &str = "./crates/libsyntax2/src/syntax_kinds/generated.rs.tera";
26const AST: &str = "./crates/libsyntax2/src/ast/generated.rs";
27const AST_TEMPLATE: &str = "./crates/libsyntax2/src/ast/generated.rs.tera";
28
29fn main() -> Result<()> {
30 let matches = App::new("tasks")
31 .setting(clap::AppSettings::SubcommandRequiredElseHelp)
32 .arg(
33 Arg::with_name("verify")
34 .long("--verify")
35 .help("Verify that generated code is up-to-date")
36 .global(true),
37 )
38 .subcommand(SubCommand::with_name("gen-kinds"))
39 .subcommand(SubCommand::with_name("gen-tests"))
40 .subcommand(SubCommand::with_name("install-code"))
41 .get_matches();
42 match matches.subcommand() {
43 ("install-code", _) => install_code_extension()?,
44 (name, Some(matches)) => run_gen_command(name, matches.is_present("verify"))?,
45 _ => unreachable!(),
46 }
47 Ok(())
48}
49
50fn run_gen_command(name: &str, verify: bool) -> Result<()> {
51 match name {
52 "gen-kinds" => {
53 update(Path::new(SYNTAX_KINDS), &render_template(SYNTAX_KINDS_TEMPLATE)?, verify)?;
54 update(Path::new(AST), &render_template(AST_TEMPLATE)?, verify)?;
55 },
56 "gen-tests" => {
57 gen_tests(verify)?
58 },
59 _ => unreachable!(),
60 }
61 Ok(())
62}
63
64fn update(path: &Path, contents: &str, verify: bool) -> Result<()> {
65 match fs::read_to_string(path) {
66 Ok(ref old_contents) if old_contents == contents => {
67 return Ok(());
68 }
69 _ => (),
70 }
71 if verify {
72 bail!("`{}` is not up-to-date", path.display());
73 }
74 eprintln!("updating {}", path.display());
75 fs::write(path, contents)?;
76 Ok(())
77}
78
79fn render_template(template: &str) -> Result<String> {
80 let grammar: ron::value::Value = {
81 let text = fs::read_to_string(GRAMMAR)?;
82 ron::de::from_str(&text)?
83 };
84 let template = fs::read_to_string(template)?;
85 let mut tera = tera::Tera::default();
86 tera.add_raw_template("grammar", &template)
87 .map_err(|e| format_err!("template error: {:?}", e))?;
88 tera.register_global_function("concat", Box::new(concat));
89 tera.register_filter("camel", |arg, _| {
90 Ok(arg.as_str().unwrap()
91 .split("_")
92 .flat_map(|word| {
93 word.chars()
94 .next().unwrap()
95 .to_uppercase()
96 .chain(
97 word.chars().skip(1).flat_map(|c| c.to_lowercase())
98 )
99 })
100 .collect::<String>()
101 .into())
102 });
103 let ret = tera
104 .render("grammar", &grammar)
105 .map_err(|e| format_err!("template error: {:?}", e))?;
106 return Ok(ret);
107
108 fn concat(args: HashMap<String, tera::Value>) -> tera::Result<tera::Value> {
109 let mut elements = Vec::new();
110 for &key in ["a", "b", "c"].iter() {
111 let val = match args.get(key) {
112 Some(val) => val,
113 None => continue,
114 };
115 let val = val.as_array().unwrap();
116 elements.extend(val.iter().cloned());
117 }
118 Ok(tera::Value::Array(elements))
119 }
120}
121
122fn gen_tests(verify: bool) -> Result<()> {
123 let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?;
124
125 let inline_tests_dir = Path::new(INLINE_TESTS_DIR);
126 if !inline_tests_dir.is_dir() {
127 fs::create_dir_all(inline_tests_dir)?;
128 }
129 let existing = existing_tests(inline_tests_dir)?;
130
131 for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
132 panic!("Test is deleted: {}", t);
133 }
134
135 let mut new_idx = existing.len() + 2;
136 for (name, test) in tests {
137 let path = match existing.get(&name) {
138 Some((path, _test)) => path.clone(),
139 None => {
140 let file_name = format!("{:04}_{}.rs", new_idx, name);
141 new_idx += 1;
142 inline_tests_dir.join(file_name)
143 }
144 };
145 update(&path, &test.text, verify)?;
146 }
147 Ok(())
148}
149
150fn tests_from_dir(dir: &Path) -> Result<HashMap<String, Test>> {
151 let mut res = HashMap::new();
152 for entry in ::walkdir::WalkDir::new(dir) {
153 let entry = entry.unwrap();
154 if !entry.file_type().is_file() {
155 continue;
156 }
157 if entry.path().extension().unwrap_or_default() != "rs" {
158 continue;
159 }
160 let text = fs::read_to_string(entry.path())?;
161
162 for (_, test) in collect_tests(&text) {
163 if let Some(old_test) = res.insert(test.name.clone(), test) {
164 bail!("Duplicate test: {}", old_test.name)
165 }
166 }
167 }
168 Ok(res)
169}
170
171fn existing_tests(dir: &Path) -> Result<HashMap<String, (PathBuf, Test)>> {
172 let mut res = HashMap::new();
173 for file in fs::read_dir(dir)? {
174 let file = file?;
175 let path = file.path();
176 if path.extension().unwrap_or_default() != "rs" {
177 continue;
178 }
179 let name = {
180 let file_name = path.file_name().unwrap().to_str().unwrap();
181 file_name[5..file_name.len() - 3].to_string()
182 };
183 let text = fs::read_to_string(&path)?;
184 let test = Test {
185 name: name.clone(),
186 text,
187 };
188 match res.insert(name, (path, test)) {
189 Some(old) => println!("Duplicate test: {:?}", old),
190 None => (),
191 }
192 }
193 Ok(res)
194}
195
196fn install_code_extension() -> Result<()> {
197 execute!(
198 r"
199cd code
200npm install
201 "
202 )?;
203 execute!(
204 r"
205cd code
206./node_modules/vsce/out/vsce package
207 "
208 )?;
209 execute!(
210 r"
211cd code
212code --install-extension ./libsyntax-rust-0.0.1.vsix
213 "
214 )?;
215 Ok(())
216}