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