1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! PDL parser and analyzer.
16 
17 use argh::FromArgs;
18 use codespan_reporting::term::{self, termcolor};
19 
20 mod analyzer;
21 mod ast;
22 mod backends;
23 mod lint;
24 mod parser;
25 #[cfg(test)]
26 mod test_utils;
27 mod utils;
28 
29 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
30 enum OutputFormat {
31     JSON,
32     Rust,
33     RustNoAlloc,
34     RustNoAllocTest,
35 }
36 
37 impl std::str::FromStr for OutputFormat {
38     type Err = String;
39 
from_str(input: &str) -> Result<Self, Self::Err>40     fn from_str(input: &str) -> Result<Self, Self::Err> {
41         match input.to_lowercase().as_str() {
42             "json" => Ok(Self::JSON),
43             "rust" => Ok(Self::Rust),
44             "rust_no_alloc" => Ok(Self::RustNoAlloc),
45             "rust_no_alloc_test" => Ok(Self::RustNoAllocTest),
46             _ => Err(format!("could not parse {:?}, valid option are 'json', 'rust', 'rust_no_alloc', and 'rust_no_alloc_test'.", input)),
47         }
48     }
49 }
50 
51 #[derive(FromArgs, Debug)]
52 /// PDL analyzer and generator.
53 struct Opt {
54     #[argh(switch)]
55     /// print tool version and exit.
56     version: bool,
57 
58     #[argh(option, default = "OutputFormat::JSON")]
59     /// generate output in this format ("json", "rust", "rust_no_alloc", "rust_no_alloc_test"). The output
60     /// will be printed on stdout in both cases.
61     output_format: OutputFormat,
62 
63     #[argh(positional)]
64     /// input file.
65     input_file: String,
66 }
67 
main() -> Result<(), String>68 fn main() -> Result<(), String> {
69     let opt: Opt = argh::from_env();
70 
71     if opt.version {
72         println!("Packet Description Language parser version 1.0");
73         return Ok(());
74     }
75 
76     let mut sources = ast::SourceDatabase::new();
77     match parser::parse_file(&mut sources, opt.input_file) {
78         Ok(file) => {
79             let analyzed_file = match analyzer::analyze(&file) {
80                 Ok(file) => file,
81                 Err(diagnostics) => {
82                     diagnostics
83                         .emit(
84                             &sources,
85                             &mut termcolor::StandardStream::stderr(termcolor::ColorChoice::Always)
86                                 .lock(),
87                         )
88                         .expect("Could not print analyzer diagnostics");
89                     return Err(String::from("Analysis failed"));
90                 }
91             };
92 
93             match opt.output_format {
94                 OutputFormat::JSON => {
95                     println!("{}", backends::json::generate(&file).unwrap())
96                 }
97                 OutputFormat::Rust => {
98                     println!("{}", backends::rust::generate(&sources, &analyzed_file))
99                 }
100                 OutputFormat::RustNoAlloc => {
101                     let schema = backends::intermediate::generate(&file).unwrap();
102                     println!("{}", backends::rust_no_allocation::generate(&file, &schema).unwrap())
103                 }
104                 OutputFormat::RustNoAllocTest => {
105                     println!(
106                         "{}",
107                         backends::rust_no_allocation::test::generate_test_file().unwrap()
108                     )
109                 }
110             }
111             Ok(())
112         }
113 
114         Err(err) => {
115             let writer = termcolor::StandardStream::stderr(termcolor::ColorChoice::Always);
116             let config = term::Config::default();
117             term::emit(&mut writer.lock(), &config, &sources, &err).expect("Could not print error");
118             Err(String::from("Error while parsing input"))
119         }
120     }
121 }
122