1use crate::codegen::generate_file_headers::write_headers;
2use 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)?; 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}