1use std::io::Write;
4use lexigram_lib::file_utils::{get_tagged_source, replace_tagged_source, SrcTagError};
5use lexigram_lib::lexergen::LexigramCrate;
6use lexigram_lib::parsergen::NTValue;
7#[derive(Clone, Copy, PartialEq, Debug)]
10pub enum Action { Generate, Verify }
11
12#[derive(Clone, PartialEq, Debug)]
14pub enum Specification {
15 None,
17 String(String),
19 File { filename: String },
21 FileTag { filename: String, tag: String },
23}
24
25impl Specification {
26 pub fn is_none(&self) -> bool {
27 self == &Specification::None
28 }
29
30 pub fn get_type(&self) -> String {
31 match self {
32 Specification::None => "no content".to_string(),
33 Specification::String(_) => "String text".to_string(),
34 Specification::File { filename } => format!("file '{filename}'"),
35 Specification::FileTag { filename, tag } => format!("file '{filename}' / tag '{tag}'"),
36 }
37 }
38
39 pub fn get(self) -> Result<Option<String>, SrcTagError> {
40 match self {
41 Specification::None => Ok(None),
42 Specification::String(s) => Ok(Some(s)),
43 Specification::File { filename } => Ok(Some(std::fs::read_to_string(filename)?)),
44 Specification::FileTag { filename, tag } => get_tagged_source(&filename, &tag).map(Some),
45 }
46 }
47}
48
49#[derive(Clone, PartialEq, Debug)]
51pub enum CodeLocation {
52 None,
55 File { filename: String },
58 FileTag { filename: String, tag: String },
61 StdOut,
64}
65
66impl CodeLocation {
67 pub fn is_none(&self) -> bool {
68 self == &CodeLocation::None
69 }
70
71 pub fn get_type(&self) -> String {
72 match self {
73 CodeLocation::None => "no content".to_string(),
74 CodeLocation::File { filename } => format!("file '{filename}'"),
75 CodeLocation::FileTag { filename, tag } => format!("file '{filename}' / tag '{tag}'"),
76 CodeLocation::StdOut => "stdout".to_string(),
77 }
78 }
79
80 pub fn read(&self) -> Result<Option<String>, SrcTagError> {
81 match self {
82 CodeLocation::None => Ok(None),
83 CodeLocation::File { filename } => Ok(Some(std::fs::read_to_string(filename)?)),
84 CodeLocation::FileTag { filename, tag } => get_tagged_source(filename, tag).map(Some),
85 CodeLocation::StdOut => {
86 Err(SrcTagError::Io(std::io::Error::new(std::io::ErrorKind::InvalidInput, "stdout can only be used as output")))
87 }
88 }
89 }
90
91 pub fn write(&self, source: &str) -> Result<(), SrcTagError> {
92 match self {
93 CodeLocation::None => Ok(()),
94 CodeLocation::File { filename } => {
95 Ok(std::fs::write(filename, source)?)
96 }
97 CodeLocation::FileTag { filename, tag } => replace_tagged_source(filename, tag, source),
98 CodeLocation::StdOut => {
99 Ok(std::io::stdout().write_all(source.as_bytes())?)
100 }
101 }
102 }
103}
104
105#[derive(Clone, PartialEq, Debug)]
112pub struct Options {
113 pub lexer_spec: Specification,
115 pub lexer_code: CodeLocation,
117 pub lexer_indent: usize,
119 pub parser_spec: Specification,
121 pub parser_code: CodeLocation,
123 pub parser_indent: usize,
125 pub lexer_headers: Vec<String>,
127 pub parser_headers: Vec<String>,
129 pub libs: Vec<String>,
131 pub start_nt: Option<String>,
135 pub gen_parser_alts: bool,
139 pub gen_wrapper: bool,
143 pub gen_span_params: bool,
147 pub gen_token_enums: bool,
149 pub lib_crate: LexigramCrate,
154 pub nt_value: NTValue,
156 pub types_code: CodeLocation,
158 pub types_indent: usize,
160 pub listener_code: CodeLocation,
162 pub listener_indent: usize,
164}
165
166impl Options {
167 pub fn new() -> Self {
168 Self::default()
169 }
170
171 fn has_lexer_spec(&self) -> bool {
172 self.lexer_spec != Specification::None
173 }
174
175 fn has_lexer_code(&self) -> bool {
176 self.lexer_code != CodeLocation::None
177 }
178
179 fn has_parser_spec(&self) -> bool {
180 self.parser_spec != Specification::None
181 }
182
183 fn has_parser_code(&self) -> bool {
184 self.parser_code != CodeLocation::None
185 }
186
187 fn has_types_code(&self) -> bool {
188 self.types_code != CodeLocation::None
189 }
190
191 fn has_listener_code(&self) -> bool {
192 self.listener_code != CodeLocation::None
193 }
194}
195
196impl Default for Options {
197 fn default() -> Self {
198 Options {
199 lexer_spec: Specification::None,
200 lexer_code: CodeLocation::None,
201 lexer_indent: 0,
202 parser_spec: Specification::None,
203 parser_code: CodeLocation::None,
204 parser_indent: 0,
205 lexer_headers: vec![],
206 parser_headers: vec![],
207 libs: vec![],
208 start_nt: None,
209 gen_parser_alts: false,
210 gen_wrapper: true,
211 gen_span_params: false,
212 gen_token_enums: false,
213 lib_crate: LexigramCrate::Core,
214 nt_value: NTValue::Default,
215 types_code: CodeLocation::None,
216 types_indent: 0,
217 listener_code: CodeLocation::None,
218 listener_indent: 0,
219 }
220 }
221}
222
223#[derive(Clone, Copy, PartialEq, Debug)]
224enum BuilderState { Start, Lexer, Parser, Types, Listener, Error }
225
226#[derive(Clone, Debug)]
252pub struct OptionsBuilder {
253 options: Options,
254 state: BuilderState,
255 message: Option<String>,
256}
257
258pub(crate) static ERR_LEXER_SPEC_ALREADY_SET: &str = "lexer lexicon specification already set";
259pub(crate) static ERR_LEXER_CODE_ALREADY_SET: &str = "lexer code location already set";
260pub(crate) static ERR_COMBINED_SPEC_GIVEN_TOO_LATE: &str = "combined lexicon/grammar specification set after other lexer/parser options";
261pub(crate) static ERR_COMBINED_SPEC_ALREADY_SET: &str = "combined lexicon/grammar specification already set";
262pub(crate) static ERR_LEXER_AFTER_PARSER: &str = "lexer option set after parser options";
263pub(crate) static ERR_LEXER_AFTER_TEMPLATES: &str = "lexer option set after template options";
264pub(crate) static ERR_LEXER_SPEC_OR_CODE_ALREADY_SET: &str = "lexer code location and/or specification already set";
265pub(crate) static ERR_PARSER_SET_BEFORE_LEXER_NOT_SET: &str = "parser option set before any lexer options has been set";
266pub(crate) static ERR_MISSING_LEXER_OPTION: &str = "lexer is missing option(s)";
267pub(crate) static ERR_PARSER_SPEC_ALREADY_SET: &str = "parser grammar specifications already set";
268pub(crate) static ERR_PARSER_AFTER_TEMPLATES: &str = "parser option set after template options";
269pub(crate) static ERR_PARSER_CODE_ALREADY_SET: &str = "parser code location already set";
270pub(crate) static ERR_PARSER_SPEC_OR_CODE_ALREADY_SET: &str = "parser code location and/or specification already set";
271pub(crate) static ERR_MISSING_PARSER_OPTION: &str = "parser is missing option(s)";
272pub(crate) static ERR_MISSING_PARSER_CODE: &str = "parser code isn't set yet";
273pub(crate) static ERR_TYPES_CODE_ALREADY_SET: &str = "location of template code for types already set";
274pub(crate) static ERR_LISTENER_CODE_ALREADY_SET: &str = "location of template code for listener implementation already set";
275
276
277impl OptionsBuilder {
278 pub fn new() -> Self {
280 OptionsBuilder { state: BuilderState::Start, options: Options::new(), message: None }
281 }
282
283 pub fn has_error(&self) -> bool {
285 self.state == BuilderState::Error
286 }
287
288 pub fn get_error_message(&self) -> Option<&str> {
290 self.message.as_deref()
291 }
292
293 pub fn reset(&mut self) {
294 self.state = BuilderState::Start;
295 self.message = None;
296 self.options = Options::new();
297 }
298
299 fn set_error(&mut self, method: &str, message: &str) {
300 if self.state != BuilderState::Error {
301 self.state = BuilderState::Error;
302 self.message = Some(format!("{}{}", method, message));
303 }
304 }
305
306 pub fn combined_spec(&mut self, combined_spec: Specification) -> &mut Self {
308 match self.state {
309 BuilderState::Start => {
310 if !self.options.has_lexer_spec() {
311 self.options.lexer_spec = combined_spec.clone();
312 self.options.parser_spec = combined_spec;
313 } else {
314 self.set_error("combined spec: ", ERR_COMBINED_SPEC_ALREADY_SET);
315 }
316 }
317 BuilderState::Lexer | BuilderState::Parser | BuilderState::Types | BuilderState::Listener => {
318 self.set_error("combined spec: ", ERR_COMBINED_SPEC_GIVEN_TOO_LATE);
319 }
320 BuilderState::Error => {}
321 }
322 self
323 }
324
325 pub fn lexer_spec(&mut self, lexer_spec: Specification) -> &mut Self {
327 match self.state {
328 BuilderState::Start | BuilderState::Lexer => {
329 if !self.options.has_lexer_spec() {
330 self.state = BuilderState::Lexer;
331 self.options.lexer_spec = lexer_spec;
332 } else {
333 self.set_error("lexer spec: ", ERR_LEXER_SPEC_ALREADY_SET);
334 }
335 }
336 BuilderState::Parser => {
337 self.set_error("lexer spec: ", ERR_LEXER_AFTER_PARSER);
338 }
339 BuilderState::Types | BuilderState::Listener => {
340 self.set_error("lexer spec: ", ERR_LEXER_AFTER_TEMPLATES);
341 }
342 BuilderState::Error => {}
343 }
344 self
345 }
346
347 pub fn lexer_code(&mut self, lexer_code: CodeLocation) -> &mut Self {
349 match self.state {
350 BuilderState::Start | BuilderState::Lexer => {
351 if !self.options.has_lexer_code() {
352 self.state = BuilderState::Lexer;
353 self.options.lexer_code = lexer_code;
354 } else {
355 self.set_error("lexer code: ", ERR_LEXER_CODE_ALREADY_SET);
356 }
357 }
358 BuilderState::Parser => {
359 self.set_error("lexer code: ", ERR_LEXER_AFTER_PARSER);
360 }
361 BuilderState::Types | BuilderState::Listener => {
362 self.set_error("lexer code: ", ERR_LEXER_AFTER_TEMPLATES);
363 }
364 BuilderState::Error => {}
365 }
366 self
367 }
368
369 pub fn lexer(&mut self, lexer_spec: Specification, lexer_code: CodeLocation) -> &mut Self {
371 match self.state {
372 BuilderState::Start if !self.options.has_lexer_spec() && !self.options.has_lexer_code() => {
373 self.state = BuilderState::Lexer;
374 self.options.lexer_spec = lexer_spec;
375 self.options.lexer_code = lexer_code;
376 }
377 BuilderState::Start | BuilderState::Lexer => {
378 self.set_error("lexer: ", ERR_LEXER_SPEC_OR_CODE_ALREADY_SET);
379 }
380 BuilderState::Parser => {
381 self.set_error("lexer: ", ERR_LEXER_AFTER_PARSER);
382 }
383 BuilderState::Types | BuilderState::Listener => {
384 self.set_error("lexer: ", ERR_LEXER_AFTER_TEMPLATES);
385 }
386 BuilderState::Error => {}
387 }
388 self
389 }
390
391 pub fn parser_spec(&mut self, parser_spec: Specification) -> &mut Self {
393 match self.state {
394 BuilderState::Start => {
395 self.set_error("parser spec: ", ERR_PARSER_SET_BEFORE_LEXER_NOT_SET);
396 }
397 BuilderState::Lexer | BuilderState::Parser => {
398 if !self.options.has_parser_spec() {
399 self.state = BuilderState::Parser;
400 self.options.parser_spec = parser_spec;
401 } else {
402 self.set_error("parser spec: ", ERR_PARSER_SPEC_ALREADY_SET);
403 }
404 }
405 BuilderState::Types | BuilderState::Listener => {
406 self.set_error("parser specs: ", ERR_PARSER_AFTER_TEMPLATES);
407 }
408 BuilderState::Error => {}
409 }
410 self
411 }
412
413 pub fn parser_code(&mut self, parser_code: CodeLocation) -> &mut Self {
415 match self.state {
416 BuilderState::Start => {
417 self.set_error("parser code: ", ERR_PARSER_SET_BEFORE_LEXER_NOT_SET);
418 }
419 BuilderState::Lexer | BuilderState::Parser => {
420 if !self.options.has_parser_code() {
421 self.state = BuilderState::Parser;
422 self.options.parser_code = parser_code;
423 } else {
424 self.set_error("parser code: ", ERR_PARSER_CODE_ALREADY_SET);
425 }
426 }
427 BuilderState::Types | BuilderState::Listener => {
428 self.set_error("parser code: ", ERR_PARSER_AFTER_TEMPLATES);
429 }
430 BuilderState::Error => {}
431 }
432 self
433 }
434
435 pub fn parser(&mut self, parser_spec: Specification, parser_code: CodeLocation) -> &mut Self {
437 match self.state {
438 BuilderState::Start => {
439 self.set_error("parser: ", ERR_PARSER_SET_BEFORE_LEXER_NOT_SET);
440 }
441 BuilderState::Lexer if !self.options.has_parser_spec() && !self.options.has_parser_code() => {
442 self.state = BuilderState::Parser;
443 self.options.parser_spec = parser_spec;
444 self.options.parser_code = parser_code;
445 }
446 BuilderState::Lexer | BuilderState::Parser => {
447 self.set_error("parser: ", ERR_PARSER_SPEC_OR_CODE_ALREADY_SET);
448 }
449 BuilderState::Types | BuilderState::Listener => {
450 self.set_error("parser: ", ERR_PARSER_AFTER_TEMPLATES);
451 }
452 BuilderState::Error => {}
453 }
454 self
455 }
456
457 pub fn types_code(&mut self, types_code: CodeLocation) -> &mut Self {
459 match self.state {
460 BuilderState::Start | BuilderState::Lexer | BuilderState::Parser
461 | BuilderState::Types | BuilderState::Listener => {
462 if !self.options.has_parser_code() {
463 self.set_error("types code: ", ERR_MISSING_PARSER_CODE);
464 } else if !self.options.has_types_code() {
465 self.state = BuilderState::Types;
466 self.options.types_code = types_code;
467 } else {
468 self.set_error("types code: ", ERR_TYPES_CODE_ALREADY_SET);
469 }
470 }
471 BuilderState::Error => {}
472 }
473 self
474 }
475
476 pub fn listener_code(&mut self, listener_code: CodeLocation) -> &mut Self {
478 match self.state {
479 BuilderState::Start | BuilderState::Lexer | BuilderState::Parser
480 | BuilderState::Types | BuilderState::Listener => {
481 if !self.options.has_parser_code() {
482 self.set_error("listener code: ", ERR_MISSING_PARSER_CODE);
483 } else if !self.options.has_listener_code() {
484 self.state = BuilderState::Listener;
485 self.options.listener_code = listener_code;
486 } else {
487 self.set_error("listener code: ", ERR_LISTENER_CODE_ALREADY_SET);
488 }
489 }
490 BuilderState::Error => {}
491 }
492 self
493 }
494
495 pub fn indent(&mut self, indent: usize) -> &mut Self {
497 match self.state {
498 BuilderState::Start => {
499 self.options.lexer_indent = indent;
500 self.options.parser_indent = indent;
501 self.options.types_indent = indent;
502 self.options.listener_indent = indent;
503 }
504 BuilderState::Lexer => self.options.lexer_indent = indent,
505 BuilderState::Parser => self.options.parser_indent = indent,
506 BuilderState::Types => self.options.types_indent = indent,
507 BuilderState::Listener => self.options.listener_indent = indent,
508 BuilderState::Error => {}
509 }
510 self
511 }
512
513 pub fn headers<I: IntoIterator<Item=T>, T: Into<String>>(&mut self, headers: I) -> &mut Self {
518 let hdr: Vec<String> = headers.into_iter().map(|s| s.into()).collect();
519 match self.state {
520 BuilderState::Start => {
521 self.options.lexer_headers.extend(hdr.clone());
522 self.options.parser_headers.extend(hdr);
523 }
524 BuilderState::Lexer => self.options.lexer_headers.extend(hdr),
525 BuilderState::Parser => self.options.parser_headers.extend(hdr),
526 BuilderState::Types | BuilderState::Listener => {
527 }
529 BuilderState::Error => {}
530 }
531 self
532 }
533
534 pub fn libs<I: IntoIterator<Item=T>, T: Into<String>>(&mut self, libs: I) -> &mut Self {
541 self.options.libs.extend(libs.into_iter().map(|s| s.into()));
542 self
543 }
544
545 pub fn start_nt<T: Into<String>>(&mut self, name_opt: Option<T>) -> &mut Self {
550 self.options.start_nt = name_opt.map(|s| s.into());
551 self
552 }
553
554 pub fn parser_alts(&mut self, parser_alts: bool) -> &mut Self {
559 self.options.gen_parser_alts = parser_alts;
560 self
561 }
562
563 pub fn wrapper(&mut self, wrapper: bool) -> &mut Self {
567 self.options.gen_wrapper = wrapper;
568 self
569 }
570
571 pub fn span_params(&mut self, span_params: bool) -> &mut Self {
578 self.options.gen_span_params = span_params;
579 self
580 }
581
582 pub fn token_enums(&mut self, token_enums: bool) -> &mut Self {
603 self.options.gen_token_enums = token_enums;
604 self
605 }
606
607 pub fn use_full_lib(&mut self, use_full_lib: bool) -> &mut Self {
626 self.options.lib_crate = if use_full_lib { LexigramCrate::Full } else { LexigramCrate::Core };
627 self
628 }
629
630 pub fn set_crate(&mut self, lcrate: LexigramCrate) -> &mut Self {
649 self.options.lib_crate = lcrate;
650 self
651 }
652
653 pub fn set_nt_value(&mut self, nt_value: NTValue) -> &mut Self {
654 self.options.nt_value = nt_value;
655 self
656 }
657
658 fn check_sanity(&mut self) {
659 if !self.has_error() {
660 if !self.options.has_lexer_spec() || !self.options.has_lexer_code() {
661 self.set_error("", ERR_MISSING_LEXER_OPTION)
662 } else if self.options.has_parser_spec() ^ self.options.has_parser_code() {
663 self.set_error("", ERR_MISSING_PARSER_OPTION)
664 }
665 }
666 }
667
668 pub fn build(&mut self) -> Result<Options, String> {
675 self.check_sanity();
676 let error = self.state == BuilderState::Error;
677 let result = if error {
678 Err(self.message.take().expect("error without message"))
679 } else {
680 Ok(std::mem::take(&mut self.options))
681 };
682 self.reset();
683 result
684 }
685
686 pub fn options(self) -> Options {
697 self.options
698 }
699}
700
701impl Default for OptionsBuilder {
702 fn default() -> Self {
703 Self::new()
704 }
705}
706
707pub mod macros {
711 #[macro_export]
720 macro_rules! genspec {
721 (none) => {
722 $crate::options::Specification::None
723 };
724 (string: $string: expr) => {
725 $crate::options::Specification::String($string.to_string())
726 };
727 (filename: $file: expr) => {
728 $crate::options::Specification::File { filename: $file.to_string() }
729 };
730 (filename: $file: expr, tag: $tag: expr) => {
731 $crate::options::Specification::FileTag { filename: $file.to_string(), tag: $tag.to_string() }
732 };
733 }
734
735 #[macro_export]
744 macro_rules! gencode {
745 (none) => {
746 $crate::options::CodeLocation::None
747 };
748 (filename: $file: expr) => {
749 $crate::options::CodeLocation::File { filename: $file.to_string() }
750 };
751 (filename: $file: expr, tag: $tag: expr) => {
752 $crate::options::CodeLocation::FileTag { filename: $file.to_string(), tag: $tag.to_string() }
753 };
754 (stdout) => {
755 $crate::options::CodeLocation::StdOut
756 };
757 }
758}