1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! `lrlex` is a partial replacement for [`lex`](http://dinosaur.compilertools.net/lex/index.html)
//! / [`flex`](https://westes.github.io/flex/manual/). It takes in a `.l` file and statically
//! compiles it to Rust code. The resulting [LRNonStreamingLexerDef] can then be given an input
//! string, from which it instantiates an [LRNonStreamingLexer]. This provides an iterator which
//! can produce the sequence of [lrpar::Lexeme]s for that input, as well as answer basic queries
//! about [cfgrammar::Span]s (e.g. extracting substrings, calculating line and column numbers).

#![allow(clippy::new_without_default)]
#![allow(clippy::type_complexity)]
#![allow(clippy::unnecessary_wraps)]
#![allow(clippy::upper_case_acronyms)]

use std::{error::Error, fmt};

mod ctbuilder;
#[doc(hidden)]
pub mod lexemes;
mod lexer;
mod parser;

#[allow(deprecated)]
pub use crate::ctbuilder::LexerBuilder;
pub use crate::{
    ctbuilder::{ct_token_map, CTLexer, CTLexerBuilder, LexerKind, Visibility},
    lexemes::DefaultLexeme,
    lexer::{LRNonStreamingLexer, LRNonStreamingLexerDef, LexerDef, Rule},
    parser::StartState,
};

use cfgrammar::yacc::parser::SpansKind;
use cfgrammar::Span;

pub type LexBuildResult<T> = Result<T, Vec<LexBuildError>>;

/// Any error from the Lex parser returns an instance of this struct.
#[derive(Debug)]
pub struct LexBuildError {
    pub(crate) kind: LexErrorKind,
    pub(crate) spans: Vec<Span>,
}

impl Error for LexBuildError {}

/// The various different possible Lex parser errors.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LexErrorKind {
    PrematureEnd,
    RoutinesNotSupported,
    UnknownDeclaration,
    MissingSpace,
    InvalidName,
    UnknownStartState,
    DuplicateStartState,
    InvalidStartState,
    InvalidStartStateName,
    DuplicateName,
    RegexError,
}

impl LexBuildError {
    pub fn spans(&self) -> impl Iterator<Item = Span> + '_ {
        self.spans.iter().copied()
    }

    pub fn spanskind(&self) -> SpansKind {
        match self.kind {
            LexErrorKind::PrematureEnd
            | LexErrorKind::RoutinesNotSupported
            | LexErrorKind::UnknownDeclaration
            | LexErrorKind::MissingSpace
            | LexErrorKind::InvalidName
            | LexErrorKind::UnknownStartState
            | LexErrorKind::InvalidStartState
            | LexErrorKind::InvalidStartStateName
            | LexErrorKind::RegexError => SpansKind::Error,
            LexErrorKind::DuplicateName | LexErrorKind::DuplicateStartState => {
                SpansKind::DuplicationError
            }
        }
    }
}

impl fmt::Display for LexBuildError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match self.kind {
            LexErrorKind::PrematureEnd => "File ends prematurely",
            LexErrorKind::RoutinesNotSupported => "Routines not currently supported",
            LexErrorKind::UnknownDeclaration => "Unknown declaration",
            LexErrorKind::MissingSpace => "Rule is missing a space",
            LexErrorKind::InvalidName => "Invalid rule name",
            LexErrorKind::UnknownStartState => "Start state not known",
            LexErrorKind::DuplicateStartState => "Start state already exists",
            LexErrorKind::InvalidStartState => "Invalid start state",
            LexErrorKind::InvalidStartStateName => "Invalid start state name",
            LexErrorKind::DuplicateName => "Rule name already exists",
            LexErrorKind::RegexError => "Invalid regular expression",
        };
        write!(f, "{s}")
    }
}

#[deprecated(
    since = "0.8.0",
    note = "This struct has been renamed to LRNonStreamingLexerDef"
)]
pub type NonStreamingLexerDef<LexemeT, StorageT> = LRNonStreamingLexerDef<LexemeT, StorageT>;

/// A convenience macro for including statically compiled `.l` files. A file `src/a/b/c.l`
/// processed by [CTLexerBuilder::lexer_in_src_dir] can then be used in a crate with
/// `lrlex_mod!("a/b/c.l")`.
///
/// Note that you can use `lrlex_mod` with [CTLexerBuilder::output_path] if, and only if, the
/// output file was placed in [std::env::var]`("OUT_DIR")` or one of its subdirectories.
#[macro_export]
macro_rules! lrlex_mod {
    ($path:expr) => {
        include!(concat!(env!("OUT_DIR"), "/", $path, ".rs"));
    };
}