Skip to main content

lexi_gram/
options.rs

1// Copyright (c) 2025 Redglyph (@gmail.com). All Rights Reserved.
2
3use 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/// Action performed by the source generator: generates the source or verifies it
8/// (verification is only possible with some [CodeLocation] options).
9#[derive(Clone, Copy, PartialEq, Debug)]
10pub enum Action { Generate, Verify }
11
12/// Specification of the lexer or parser (lexicon or grammar content or location)
13#[derive(Clone, PartialEq, Debug)]
14pub enum Specification {
15    /// No source
16    None,
17    /// Source is in a string
18    String(String),
19    /// Source is an existing file
20    File { filename: String },
21    /// Source is between tags in an existing file
22    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/// Location of the code to be generated or verified
50#[derive(Clone, PartialEq, Debug)]
51pub enum CodeLocation {
52    /// * Generate mode: doesn't write anything.
53    /// * Verify mode: returns `None`.
54    None,
55    /// * Generate mode: creates a new file or overwrites an existing one.
56    /// * Verify mode: reads the expected code from an existing file.
57    File { filename: String },
58    /// * Generate mode: inserts the code between tags into an existing file.
59    /// * Verify mode: reads the expected code between tags of an existing file.
60    FileTag { filename: String, tag: String },
61    /// * Generate mode: writes the code to stdout.
62    /// * not a valid option in verify mode
63    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// ---------------------------------------------------------------------------------------------
106
107/// Options used to generate the source code of the lexer, parser, wrapper, and listener from a lexicon and a grammar
108/// (the grammar is optional if only the lexer must be generated).
109///
110/// See [OptionsBuilder] for the accompanying builder.
111#[derive(Clone, PartialEq, Debug)]
112pub struct Options {
113    /// Specification of the lexer (lexicon)
114    pub lexer_spec: Specification,
115    /// Location of the generated/verified lexer code
116    pub lexer_code: CodeLocation,
117    /// Indentation of lexer source code
118    pub lexer_indent: usize,
119    /// Specification of the parser (grammar)
120    pub parser_spec: Specification,
121    /// Location of the generated/verified parser code
122    pub parser_code: CodeLocation,
123    /// Indentation of parser source code
124    pub parser_indent: usize,
125    /// Extra headers before the lexer code
126    pub lexer_headers: Vec<String>,
127    /// Custom headers to insert before the parser code
128    pub parser_headers: Vec<String>,
129    /// Custom `use` libraries to include in the parser code (only if `parser_code` isn't `None`)
130    pub libs: Vec<String>,
131    /// Name of the start nonterminal, which defaults to the first one defined in the grammar.
132    ///
133    /// Default: `None`.
134    pub start_nt: Option<String>,
135    /// Includes the definitions of the alternatives in the parser, for debugging purposes
136    ///
137    /// Default: `false`
138    pub gen_parser_alts: bool,
139    /// Generates the wrapper, which is necessary to interface a listener (only if `parser_code` isn't `None`)
140    ///
141    /// Default: `true`
142    pub gen_wrapper: bool,
143    /// Generates the span parameters in the listener methods, to get the position of the terminals/nonterminals (only if `gen_wrapper` is `true`)
144    ///
145    /// Default: `false`
146    pub gen_span_params: bool,
147    /// Generates enums for the terminal and nonterminal values.
148    pub gen_token_enums: bool,
149    /// Uses the full library instead of the core library in the generated code. Use this option for a lexer / parser that
150    /// needs `lexigram_lib` instead of the smaller `lexigram_core` crate.
151    ///
152    /// Default: `false`
153    pub lib_crate: LexigramCrate,
154    /// Sets the nonterminals that have a value.
155    pub nt_value: NTValue,
156    /// Location of the generated template for the user types
157    pub types_code: CodeLocation,
158    /// Indentation of the generated template for the user types
159    pub types_indent: usize,
160    /// Location of the template for the listener implementation
161    pub listener_code: CodeLocation,
162    /// Indentation of the template for the listener implementation
163    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/// Builder of the [Options] object.
227///
228/// There are 3 types of options:
229/// * options related to the lexer: [lexer](OptionsBuilder::lexer), [indent](OptionsBuilder::indent), [headers](OptionsBuilder::headers)
230/// * options related to the parser: [parser](OptionsBuilder::parser), [indent](OptionsBuilder::indent), [headers](OptionsBuilder::headers)
231/// * general options: [libs](OptionsBuilder::libs), [parser_alts](OptionsBuilder::parser_alts),
232///   [wrapper](OptionsBuilder::wrapper), [span_params](OptionsBuilder::span_params)
233///
234/// Initially, the default option settings corresponds to [Options]'s defaults. The builder offers a convenient way to chain method
235/// in order to set custom options.
236///
237/// The builder offers a way to use some of those options differently, depending on their position in the method chain.
238/// The options [indent](OptionsBuilder::indent) and [headers](OptionsBuilder::headers) are:
239/// * applied to both the lexer and parser if placed before [lexer](OptionsBuilder::lexer) and [parser](OptionsBuilder::parser)
240/// * applied to the lexer if placed after [lexer](OptionsBuilder::lexer) and before [parser](OptionsBuilder::parser)
241/// * applied to the parser if placed after [parser](OptionsBuilder::parser).
242///
243/// The option [parser](OptionsBuilder::parser) mustn't be used before [lexer](OptionsBuilder::lexer).
244///
245/// The [build](OptionsBuilder::build) method creates the resulting [Options] object. It doesn't check the object is properly set, however,
246/// since the user may want to modify it later. For instance, it doesn't verify that the lexer is defined as something else than 'none'.
247/// The current configuration is cleared after that point, and a new object can be created again with the same builder.
248///
249/// The [options](OptionsBuilder::options) method moves the builder to create the resulting [Options] object, so it can't be reused
250/// (unless cloned before callind the method).
251#[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    /// Creates a builder for [Options]
279    pub fn new() -> Self {
280        OptionsBuilder { state: BuilderState::Start, options: Options::new(), message: None }
281    }
282
283    /// Checks if the builder has encountered an error
284    pub fn has_error(&self) -> bool {
285        self.state == BuilderState::Error
286    }
287
288    /// Gets the current error message, if any
289    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    /// Sets the location of the combined lexicon and grammar specification (default is none)
307    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    /// Sets the location of the lexer's lexicon specification (default is none)
326    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    /// Sets the location of the lexer's generated code (default is none)
348    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    /// Sets the location of the lexer's lexicon specification and generated code (default is none for both)
370    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    /// Sets the location the parser's grammar specification (default is none)
392    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    /// Sets the location the parser's generated code (default is none)
414    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    /// Sets the location the parser's grammar specification and generated code (default is none for both)
436    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    /// Sets the location the generated template for the user types (default is none)
458    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    /// Sets the location the generated template for the listener implementation (default is none)
477    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    /// Sets the indentation of the generated code, in number of space characters (default is 0)
496    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    /// **Adds** optional headers, which will be placed in front of the code (even before the `use`
514    /// clauses). This can be used to place inner attributes like `#![allow(unused)]` or `#![cfg(...)]`.
515    ///
516    /// This method can be called several times to add more headers.
517    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                // ignored; no headers in templates
528            }
529            BuilderState::Error => {}
530        }
531        self
532    }
533
534    /// **Adds** user crates and modules to the list of `use` dependencies for the parser / wrapper.
535    /// This can be used to define the user types needed in the wrapper / listener
536    /// (those types can be initially copied from the generated code; they're commented out near the
537    /// beginning, after the context type definitions).
538    ///
539    /// This method can be called several times to add more dependencies.
540    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    /// Defines the start nonterminal. The default, if `name_opt` is `None` or if this method isn't called,
546    /// is the first one defined in the grammar.
547    ///
548    /// Default: `None`
549    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    /// Sets the boolean option that generates more explicit debug messages in the parser when a parsing error
555    /// is encountered. It requires to generate additional information.
556    ///
557    /// Default: `false`
558    pub fn parser_alts(&mut self, parser_alts: bool) -> &mut Self {
559        self.options.gen_parser_alts = parser_alts;
560        self
561    }
562
563    /// Sets the boolean option that generates the wrapper.
564    ///
565    /// Default: `true`
566    pub fn wrapper(&mut self, wrapper: bool) -> &mut Self {
567        self.options.gen_wrapper = wrapper;
568        self
569    }
570
571    /// Sets the boolean option that generates the extra `span` parameters in the listener callback methods.
572    /// These parameters locate the terminals and nonterminals in the source text, so they can be used
573    /// for instance to generate report messages with the precise location of symbols that caused an
574    /// error.
575    ///
576    /// Default: `false`
577    pub fn span_params(&mut self, span_params: bool) -> &mut Self {
578        self.options.gen_span_params = span_params;
579        self
580    }
581
582    /// Generates enums for the terminal and nonterminal values. They may be helpful in the optional
583    /// listener trait methods like `hook()` and `intercept_token()` when they are used.
584    ///
585    /// # Example
586    ///
587    /// ```ignore
588    /// #[derive(Clone, Copy, PartialEq, Debug)]
589    /// #[repr(u16)]
590    /// pub enum Term {
591    ///     #[doc = "','"]        Comma = 0,
592    ///     #[doc = "';'"]        SemiColon = 1,
593    /// }
594    ///
595    /// #[derive(Clone, Copy, PartialEq, Debug)]
596    /// #[repr(u16)]
597    /// pub enum NTerm {
598    ///     #[doc = "`program`"]                   Program = 0,
599    ///     #[doc = "`stmt_i`, parent: `program`"] StmtI = 1,
600    /// }
601    /// ```
602    pub fn token_enums(&mut self, token_enums: bool) -> &mut Self {
603        self.options.gen_token_enums = token_enums;
604        self
605    }
606
607    /// Uses the full [lexigram_lib] crate in the generated code if `use_full_lib` is true, or the
608    /// smaller [lexigram_core](lexigram_lib::lexigram_core) if false.
609    /// Use this option for a lexer / parser that needs to access the code generation features in [lexigram_lib].
610    ///
611    /// See also [set_crate](OptionsBuilder::set_crate) for a custom name or path.
612    ///
613    /// ##Example
614    ///
615    /// ```ignore
616    /// let options = OptionsBuilder::new()
617    ///     .lexer(genspec!(filename: LEXICON), gencode!(filename: LEXER))
618    ///     .parser(genspec!(filename: GRAMMAR), gencode!(filename: PARSER))
619    ///     .use_full_lib(false)
620    ///     .build()
621    ///     .expect("should have no error");
622    /// try_gen_parser(action, options)?;
623    /// ```
624    /// -> `use lexigram_core::parser::Parser;` and so on
625    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    /// Sets the `use` path to the lexigram core library.
631    ///
632    /// See also [use_full_lib](OptionsBuilder::set_crate) for the most common situations.
633    ///
634    /// ##Example
635    ///
636    /// ```ignore
637    /// use lexigram_core as core;
638    ///
639    /// let options = OptionsBuilder::new()
640    ///     .lexer(genspec!(filename: LEXICON), gencode!(filename: LEXER))
641    ///     .parser(genspec!(filename: GRAMMAR), gencode!(filename: PARSER))
642    ///     .set_crate(LexigramCrate::Custom("core".to_string()))
643    ///     .build()
644    ///     .expect("should have no error");
645    /// try_gen_parser(action, options)?;
646    /// ```
647    /// -> `use core::parser::Parser;` and so on
648    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    /// Creates an [Options] object with the current options defined earlier by [OptionsBuilder]'s methods.
669    ///
670    /// **The builder resets the options to their default values** after creating and returning that object,
671    /// so it can be reused to generate other [Options] objects, but the options must be set again.
672    /// If you want the builder to keep the options, consider cloning it and using [options()](OptionsBuilder::options)
673    /// instead.
674    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    /// Creates an [Options] object with the current options defined earlier by [OptionsBuilder]'s methods.
687    ///
688    /// This method moves the builder. If you want to reuse the builder, consider cloning it (if you want
689    /// to keep the same options) or using [build()](OptionsBuilder::build) instead.
690    ///
691    /// **Important note**: once [lexer](OptionsBuilder::lexer) has been called, it's not possible to call
692    /// it again. Similarly, once [parser](OptionsBuilder::parser) has been called, it's not possible to
693    /// call it or [lexer](OptionsBuilder::lexer) again (see the [Options] doc for further details).
694    /// It means that **if you clone the builder and generate an option object with this method, the
695    /// remaining builder will only be partially reconfigurable**.
696    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
707// ---------------------------------------------------------------------------------------------
708// Macros
709
710pub mod macros {
711    /// Generates a [Specification](crate::options::Specification) object:
712    /// ```ignore
713    /// genspec!(none)
714    /// genspec!(string: expr)
715    /// genspec!(filename: expr)
716    /// genspec!(filename: expr, tag: expr)
717    /// ```
718    /// where `expr.to_string()` are valid strings
719    #[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    /// Generates a [CodeLocation](crate::options::CodeLocation) object:
736    /// ```ignore
737    /// gencode!(none)
738    /// gencode!(string: expr)
739    /// gencode!(filename: expr)
740    /// gencode!(filename: expr, tag: expr)
741    /// ```
742    /// where `expr.to_string()` are valid strings
743    #[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}