1use std::fs;
2
3use clap::ValueEnum;
4
5use crate::table::TableType;
6use crate::{Error, Result};
7use std::path::{Path, PathBuf};
8
9use crate::generator::generate_parser;
10
11#[derive(Debug, Default, Clone, ValueEnum)]
13pub enum ParserAlgo {
14 #[default]
15 LR,
16 GLR,
17}
18
19#[derive(Debug, Default, Clone, ValueEnum)]
21pub enum LexerType {
22 #[default]
24 Default,
25 Custom,
27}
28
29#[derive(Debug, Default, Clone, ValueEnum)]
31pub enum BuilderType {
32 #[default]
34 Default,
35 Generic,
37 Custom,
39}
40
41#[derive(Debug, Default, Clone, ValueEnum)]
43pub enum GeneratorTableType {
44 Arrays,
48 #[default]
52 Functions,
53}
54
55#[derive(Debug, Clone)]
79pub struct Settings {
80 pub(crate) out_dir_root: Option<PathBuf>,
81 pub(crate) out_dir_actions_root: Option<PathBuf>,
82 pub(crate) root_dir: Option<PathBuf>,
83
84 pub(crate) prefer_shifts: bool,
85 pub(crate) prefer_shifts_over_empty: bool,
86 pub(crate) table_type: TableType,
87 pub(crate) parser_algo: ParserAlgo,
88 pub(crate) print_table: bool,
89 pub(crate) exclude: Vec<String>,
90 pub(crate) actions: bool,
91 pub(crate) notrace: bool,
92
93 pub(crate) lexer_type: LexerType,
94 pub(crate) builder_type: BuilderType,
95 pub(crate) builder_loc_info: bool,
96 pub(crate) generator_table_type: GeneratorTableType,
97 pub(crate) input_type: String,
98
99 pub(crate) lexical_disamb_most_specific: bool,
100 pub(crate) lexical_disamb_longest_match: bool,
101 pub(crate) lexical_disamb_grammar_order: bool,
102
103 pub(crate) partial_parse: bool,
104 pub(crate) skip_ws: bool,
105
106 pub(crate) force: bool,
107 force_explicit: bool,
108
109 pub(crate) dot: bool,
110 pub(crate) fancy_regex: bool,
111}
112
113impl Default for Settings {
114 fn default() -> Self {
115 let out_dir_root = std::env::var("OUT_DIR").map_or(None, |d| Some(PathBuf::from(d)));
118
119 let root_dir =
121 std::env::var("CARGO_MANIFEST_DIR").map_or(None, |d| Some(PathBuf::from(d)));
122
123 Self {
124 root_dir,
125 out_dir_root: out_dir_root.clone(),
126 out_dir_actions_root: out_dir_root,
127 prefer_shifts: false,
128 prefer_shifts_over_empty: true,
129 table_type: Default::default(),
130 parser_algo: Default::default(),
131 print_table: false,
132 actions: true,
133 notrace: false,
134 lexer_type: Default::default(),
135 builder_type: Default::default(),
136 builder_loc_info: false,
137 generator_table_type: Default::default(),
138 input_type: "str".into(),
139 lexical_disamb_most_specific: true,
140 lexical_disamb_longest_match: true,
141 lexical_disamb_grammar_order: true,
142 partial_parse: false,
143 skip_ws: true,
144 force: true, force_explicit: false,
146 exclude: vec![],
147 dot: false,
148 fancy_regex: false,
149 }
150 }
151}
152
153impl Settings {
154 pub fn new() -> Self {
156 Settings::default()
157 }
158
159 pub fn root_dir(mut self, root_dir: PathBuf) -> Self {
165 self.root_dir = Some(root_dir);
166 self
167 }
168
169 pub fn out_dir_root(mut self, out_dir: PathBuf) -> Self {
172 self.out_dir_root = Some(out_dir);
173 self
174 }
175
176 pub fn out_dir_actions_root(mut self, out_dir: PathBuf) -> Self {
179 self.out_dir_actions_root = Some(out_dir);
180 self
181 }
182
183 pub fn in_source_tree(mut self) -> Self {
187 self.out_dir_root = None;
188 if matches!(self.builder_type, BuilderType::Default) {
189 self.actions_in_source_tree()
190 } else {
191 self
192 }
193 }
194
195 pub fn actions_in_source_tree(mut self) -> Self {
198 if !matches!(self.builder_type, BuilderType::Default) {
199 panic!("Settings 'actions_in_source_tree' is only available for the default builder type!");
200 }
201 self.out_dir_actions_root = None;
202 if !self.force_explicit {
203 self.force = false;
204 }
205 self
206 }
207
208 pub fn exclude(mut self, exclude: Vec<String>) -> Self {
211 self.exclude = exclude;
212 self
213 }
214
215 pub fn prefer_shifts(mut self, prefer: bool) -> Self {
218 self.prefer_shifts = prefer;
219 self
220 }
221
222 pub fn prefer_shifts_over_empty(mut self, prefer: bool) -> Self {
225 self.prefer_shifts_over_empty = prefer;
226 self
227 }
228
229 pub fn table_type(mut self, table_type: TableType) -> Self {
231 self.table_type = table_type;
232 self
233 }
234
235 pub fn parser_algo(mut self, parser_algo: ParserAlgo) -> Self {
237 match parser_algo {
238 ParserAlgo::LR => {}
239 ParserAlgo::GLR => {
240 self.table_type = TableType::LALR_RN;
242 self.prefer_shifts = false;
244 self.prefer_shifts_over_empty = false;
245 self.lexical_disamb_grammar_order = false;
247 }
248 }
249 self.parser_algo = parser_algo;
250 self
251 }
252
253 pub fn lexer_type(mut self, lexer_type: LexerType) -> Self {
256 self.lexer_type = lexer_type;
257 self
258 }
259
260 pub fn builder_type(mut self, builder_type: BuilderType) -> Self {
262 self.builder_type = builder_type;
263 self
264 }
265
266 pub fn builder_loc_info(mut self, builder_loc_info: bool) -> Self {
269 self.builder_loc_info = builder_loc_info;
270 self
271 }
272
273 pub fn generator_table_type(mut self, generator_table_type: GeneratorTableType) -> Self {
275 self.generator_table_type = generator_table_type;
276 self
277 }
278
279 pub fn input_type(mut self, input_type: String) -> Self {
281 self.input_type = input_type;
282 self
283 }
284
285 pub fn lexical_disamb_most_specific(mut self, most_specific: bool) -> Self {
287 self.lexical_disamb_most_specific = most_specific;
288 self
289 }
290
291 pub fn lexical_disamb_longest_match(mut self, longest_match: bool) -> Self {
293 self.lexical_disamb_longest_match = longest_match;
294 self
295 }
296
297 pub fn lexical_disamb_grammar_order(mut self, grammar_order: bool) -> Self {
299 if let ParserAlgo::LR = self.parser_algo {
300 if !grammar_order {
301 panic!("Can't disable grammar order strategy for LR.")
302 }
303 }
304 self.lexical_disamb_grammar_order = grammar_order;
305 self
306 }
307
308 pub fn fancy_regex(mut self, fancy_regex: bool) -> Self {
311 self.fancy_regex = fancy_regex;
312 self
313 }
314
315 pub fn print_table(mut self, print_table: bool) -> Self {
316 self.print_table = print_table;
317 self
318 }
319
320 pub fn partial_parse(mut self, partial_parse: bool) -> Self {
324 self.partial_parse = partial_parse;
325 self
326 }
327
328 pub fn skip_ws(mut self, skip_ws: bool) -> Self {
331 self.skip_ws = skip_ws;
332 self
333 }
334
335 pub fn actions(mut self, actions: bool) -> Self {
338 self.actions = actions;
339 self
340 }
341
342 pub fn notrace(mut self, notrace: bool) -> Self {
346 let notrace = if !notrace {
347 std::env::var("RUSTEMO_NOTRACE").is_ok()
348 } else {
349 std::env::set_var("RUSTEMO_NOTRACE", "1");
350 true
351 };
352
353 self.notrace = notrace;
354 self
355 }
356
357 pub fn force(mut self, force: bool) -> Self {
359 self.force = force;
360 self.force_explicit = true;
361 self
362 }
363
364 pub fn dot(mut self, dot: bool) -> Self {
367 self.dot = dot;
368 self
369 }
370
371 pub fn process_dir(&self) -> Result<()> {
374 if let Some(root_dir) = &self.root_dir {
375 if !root_dir.exists() {
376 return Err(Error::Error(format!(
377 "Directory/File {root_dir:?} doesn't exist."
378 )));
379 }
380
381 let visitor = |grammar: &Path| -> Result<()> {
382 self.process_grammar(grammar)?;
383 Ok(())
384 };
385
386 self.visit_dirs(root_dir, &visitor)
387 } else {
388 Err(Error::Error("Root dir must be set!".to_string()))
389 }
390 }
391
392 pub fn process_grammar(&self, grammar: &Path) -> Result<()> {
396 println!("Generating parser for grammar {grammar:?}");
397 let relative_outdir = |p: &Path| -> Result<PathBuf> {
398 Ok(p.join(
399 grammar
400 .parent()
401 .ok_or(Error::Error(format!(
402 "Cannot find parent of '{grammar:?}' file."
403 )))?
404 .strip_prefix(self.root_dir.as_ref().expect("'root_dir' must be set!"))
405 .or(Err(Error::Error(format!(
406 "Cannot remove prefix '{:?}' from '{grammar:?}'.",
407 &self.root_dir
408 ))))?,
409 ))
410 };
411
412 let out_dir = self
413 .out_dir_root
414 .as_ref()
415 .map(|p| relative_outdir(p))
416 .transpose()?;
417
418 let out_dir_actions = self
419 .out_dir_actions_root
420 .as_ref()
421 .map(|p| relative_outdir(p))
422 .transpose()?;
423
424 if let Some(ref dir) = out_dir {
425 println!("Parser out dir: {dir:?}");
426 }
427 if let Some(ref dir) = out_dir_actions {
428 println!("Actions out dir: {dir:?}");
429 }
430
431 generate_parser(
432 grammar,
433 out_dir.as_deref(),
434 out_dir_actions.as_deref(),
435 self,
436 )
437 }
438
439 fn visit_dirs(&self, dir: &Path, visitor: &dyn Fn(&Path) -> Result<()>) -> Result<()> {
442 if dir.is_dir() {
443 for entry in fs::read_dir(dir)? {
444 let entry = entry?;
445 let path = entry.path();
446
447 let path_name = path.to_string_lossy();
449 if self.exclude.iter().any(|e| path_name.contains(e)) {
450 println!("Excluding path: {path_name:?}");
451 continue;
452 }
453
454 if path.is_dir() {
455 self.visit_dirs(&path, visitor)?;
456 } else if matches!(path.extension(), Some(ext) if ext == "rustemo") {
457 visitor(&path)?
458 }
459 }
460 }
461 Ok(())
462 }
463}
464
465pub fn process_dir<P: AsRef<Path>>(dir: P) -> Result<()> {
478 Settings::new()
479 .root_dir(PathBuf::from(dir.as_ref()))
480 .process_dir()?;
481 Ok(())
482}
483
484pub fn process_crate_dir() -> Result<()> {
487 Settings::new().process_dir()?;
488 Ok(())
489}
490
491pub fn process_grammar<P: AsRef<Path>>(grammar: P) -> Result<()> {
497 Settings::new().process_grammar(grammar.as_ref())?;
498 Ok(())
499}