1use sqparse::{parse, tokenize, Flavor};
2use std::path::{Path, PathBuf};
3use std::time::Instant;
4
5fn main() {
6 let mut args = std::env::args();
7 let exe = args.next().unwrap();
8
9 let base_path = match args.next() {
10 Some(arg) => PathBuf::from(arg),
11 None => {
12 eprintln!("Usage: {exe} [path]");
13 eprintln!();
14 eprintln!("Provide a path to a file to parse that file, or a path to a directory to");
15 eprintln!("recursively parse all .nut and .gnut files in the directory");
16 std::process::exit(1);
17 }
18 };
19
20 let mut total_size_bytes = 0;
21 let mut total_lex_secs = 0.;
22 let mut total_parse_secs = 0.;
23
24 visit(&base_path, &mut |path| {
25 let extension = path.extension().and_then(|val| val.to_str());
26 if !matches!(extension, Some("nut") | Some("gnut")) {
27 return;
28 }
29
30 println!("{}", path.display());
31
32 let file_text = match std::fs::read_to_string(path) {
33 Ok(text) => text,
34 Err(err) => {
35 println!(" could not read: {err}");
36 return;
37 }
38 };
39
40 let lex_start = Instant::now();
41 let tokens = match tokenize(&file_text, Flavor::SquirrelRespawn) {
42 Ok(tokens) => tokens,
43 Err(err) => {
44 eprintln!("{}", err.display(&file_text, path.to_str()));
45 std::process::exit(1);
46 }
47 };
48 let lex_secs = lex_start.elapsed().as_secs_f64();
49 println!(" tokenize: {lex_secs}s");
50
51 let parse_start = Instant::now();
52 if let Err(err) = parse(&tokens) {
53 eprintln!("{}", err.display(&file_text, &tokens, path.to_str()));
54 std::process::exit(1);
55 }
56 let parse_secs = parse_start.elapsed().as_secs_f64();
57 println!(" parse: {parse_secs}s");
58
59 total_size_bytes += file_text.bytes().len();
60 total_lex_secs += lex_secs;
61 total_parse_secs += parse_secs;
62 });
63
64 let total_mb = total_size_bytes as f64 / 1048576.;
65 println!("Finished!");
66 println!(
67 "Tokenize: {:.4}s, {:.2} MB/s",
68 total_lex_secs,
69 total_mb / total_lex_secs
70 );
71 println!(
72 "Parse: {:.4}s, {:.2} MB/s",
73 total_parse_secs,
74 total_mb / total_parse_secs
75 );
76}
77
78fn visit<F: FnMut(&Path)>(path: &Path, cb: &mut F) {
79 if path.is_file() {
80 cb(path);
81 } else if path.is_dir() {
82 for entry in std::fs::read_dir(path).unwrap() {
83 let entry = entry.unwrap();
84 let path = entry.path();
85 visit(&path, cb);
86 }
87 }
88}