lwb_parser/codegen/
manager.rs

1use crate::codegen::generate_file_headers::write_headers;
2// use crate::codegen::generate_language;
3use crate::codegen::error::CodegenError;
4use crate::codegen::error::CodegenError::NoExtension;
5use crate::codegen::generate_from_pairs::generate_from_pairs;
6use crate::codegen::generate_misc::{generate_parser, generate_root};
7use crate::codegen::generate_structs::generate_structs;
8use crate::codegen::generate_trait_impls::generate_trait_impls;
9use crate::codegen::FormattingFile;
10use crate::config::toml::{find_config_path, read_config, ReadConfigError};
11use crate::config::Config;
12use crate::error::display_miette_error;
13use crate::language::Language;
14use crate::parser::syntax_file::{convert_syntax_file_ast, ParseError, SyntaxFile};
15use crate::sources::source_file::SourceFile;
16use proc_macro2::TokenStream;
17use quote::quote;
18use std::io::Write;
19use std::path::{Path, PathBuf};
20
21pub fn create_module_files<const N: usize>(
22    location: impl AsRef<Path>,
23    files: [&str; N],
24) -> Result<[(FormattingFile, String); N], CodegenError> {
25    let mut location = location.as_ref().to_path_buf();
26    location.set_extension("");
27
28    std::fs::create_dir_all(&location)?;
29    println!("cargo:rerun-if-changed={:?}", location);
30
31    let mut res = files.map(|_| None);
32    for (index, &i) in files.iter().enumerate() {
33        let mut filename = location.clone();
34        filename.push(i);
35        filename.set_extension("rs");
36
37        println!("cargo:rerun-if-changed={:?}", filename);
38        res[index] = Some((
39            FormattingFile::create(&filename)?,
40            filename
41                .file_stem()
42                .ok_or(NoExtension)?
43                .to_string_lossy()
44                .into_owned(),
45        ));
46    }
47
48    Ok(res.map(|i| i.unwrap()))
49}
50
51fn refs(inp: &mut (FormattingFile, String)) -> (&mut FormattingFile, &str) {
52    (&mut inp.0, &inp.1)
53}
54
55pub(crate) struct Generated {
56    impls: TokenStream,
57    structs: TokenStream,
58    from_pairs: TokenStream,
59    root: TokenStream,
60    parser: TokenStream,
61}
62
63fn codegen_internal(
64    source: SourceFile,
65    config: Config,
66    imports: &[&str],
67) -> Result<Generated, CodegenError> {
68    let ast = SyntaxFile::try_parse(&source)?;
69
70    let serialized_parser = bincode::serialize(&ast)?;
71
72    let legacy_ast = convert_syntax_file_ast::convert(ast)?; // TODO make peg parser use new ast
73
74    let mut derives = vec!["Debug", "PartialEq"];
75
76    if config.syntax.serde {
77        derives.extend(["Serialize", "Deserialize"]);
78    }
79
80    let legacy_ast = legacy_ast.simplify()?;
81
82    let structs = generate_structs(&legacy_ast, &derives, config.syntax.non_exhaustive)?;
83    let from_pairs = generate_from_pairs(&legacy_ast, config.syntax.non_exhaustive)?;
84    let impls = generate_trait_impls(&legacy_ast)?;
85    let root = generate_root(
86        imports,
87        &derives,
88        config.syntax.mode.import_location(),
89        config.syntax.non_exhaustive,
90    )?;
91    let parser = generate_parser(&serialized_parser)?;
92
93    Ok(Generated {
94        impls,
95        structs,
96        from_pairs,
97        root,
98        parser,
99    })
100}
101
102#[doc(hidden)]
103pub fn __codegen_tokenstream(
104    source: SourceFile,
105    config: Config,
106    debug: bool,
107) -> Result<TokenStream, CodegenError> {
108    let Generated {
109        impls,
110        structs,
111        from_pairs,
112        root,
113        parser,
114    } = codegen_internal(source, config, &[])?;
115
116    if debug {
117        println!("{}", structs);
118    }
119
120    Ok(quote!(
121        mod ast {
122            #structs
123        }
124        mod from_pairs {
125            #from_pairs
126        }
127        mod ast_impls {
128            #impls
129        }
130        mod parser {
131            #parser
132        }
133
134        pub use ast::*;
135        pub use from_pairs::*;
136        pub use ast_impls::*;
137        pub use parser::*;
138
139        #root
140    ))
141}
142
143fn unwrap<T>(r: Result<T, ReadConfigError>) -> T {
144    match r {
145        Ok(i) => i,
146        Err(e) => {
147            panic!("failed to read config: {e}")
148        }
149    }
150}
151
152pub struct Codegen {
153    config: Config,
154}
155
156impl Default for Codegen {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162impl Codegen {
163    pub fn try_new() -> Result<Self, ReadConfigError> {
164        Self::try_with_config(find_config_path())
165    }
166
167    pub fn try_with_config(path: PathBuf) -> Result<Self, ReadConfigError> {
168        println!("cargo:rerun-if-changed={:?}", path);
169        Ok(Self::with_config_struct(read_config(path)?))
170    }
171
172    pub fn with_config_struct(config: Config) -> Self {
173        Self { config }
174    }
175
176    pub fn new() -> Self {
177        unwrap(Self::try_new())
178    }
179
180    pub fn with_config(path: PathBuf) -> Self {
181        unwrap(Self::try_with_config(path))
182    }
183
184    pub fn codegen(self) {
185        if let Err(e) = self.try_codegen() {
186            match e {
187                CodegenError::ParseError(ParseError::PEG(errs)) => {
188                    for e in errs.iter().rev() {
189                        eprintln!("{}", display_miette_error(e));
190                    }
191                    panic!("failed to generate ast")
192                }
193                e => panic!("failed to generate ast: {e}"),
194            }
195        }
196    }
197
198    pub fn try_codegen(self) -> Result<(), CodegenError> {
199        let mut files = create_module_files(
200            &self.config.syntax.destination,
201            [
202                "mod.rs",
203                "ast.rs",
204                "from_pairs.rs",
205                "ast_impls.rs",
206                "parser.rs",
207            ],
208        )?;
209
210        write_headers(&mut files.iter_mut().map(refs).collect::<Vec<_>>())?;
211
212        let [ref mut f_modrs, ref mut f_ast, ref mut f_from_pairs, ref mut f_ast_trait_impls, ref mut f_serialized_parser] =
213            files.map(|i| i.0);
214
215        let write_serialized_ast = self.config.syntax.write_serialized_ast;
216
217        let Generated {
218            impls,
219            structs,
220            from_pairs,
221            root,
222            parser,
223        } = codegen_internal(
224            SourceFile::open(&self.config.syntax.definition)?,
225            self.config,
226            &["ast", "from_pairs", "ast_impls", "parser"],
227        )?;
228
229        write!(f_modrs, "{}", root)?;
230        write!(f_ast, "{}", structs)?;
231        write!(f_ast_trait_impls, "{}", impls)?;
232        write!(f_from_pairs, "{}", from_pairs)?;
233
234        if write_serialized_ast {
235            write!(f_serialized_parser, "{}", parser)?;
236        }
237
238        Ok(())
239    }
240}