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
120
121
122
123
124
125
126
127
use crate::lexer::TerminalIndex;
use crate::parser::{NonTerminalIndex, ProductionIndex, ScannerIndex};
use std::fmt::{Display, Error, Formatter};

///
/// The type of the elements in the parser stack.
///
#[derive(Debug, Clone, Copy)]
pub enum ParseType {
    ///
    /// The index of a non-terminal in the generated NON_TERMINALS array
    ///
    N(NonTerminalIndex),

    ///
    /// The index of a terminal in the generated TERMINALS array
    ///
    T(TerminalIndex),

    ///
    /// Instruction to switch to a scanner configuration with the given index
    ///
    S(ScannerIndex),

    ///
    /// Instruction to push the index of the current scanner and switch to a scanner configuration
    /// with the given index
    ///
    Push(ScannerIndex),

    ///
    /// Instruction to pop the index of the scanner pushed before and switch to the scanner
    /// configuration with that index
    ///
    Pop,

    ///
    /// End of production marker
    ///
    E(ProductionIndex),
}

impl ParseType {
    pub(crate) fn is_switch(&self) -> bool {
        matches!(self, Self::S(_)) || matches!(self, Self::Push(_)) || matches!(self, Self::Pop)
    }
}

impl Display for ParseType {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        match self {
            Self::N(n) => write!(f, "N({})", n),
            Self::T(t) => write!(f, "T({})", t),
            Self::S(s) => write!(f, "S({})", s),
            Self::Push(s) => write!(f, "Push({})", s),
            Self::Pop => write!(f, "Pop"),
            Self::E(e) => write!(f, "E({})", e),
        }
    }
}

///
/// The generated parsers are push down automata (PDA) which utilize a stack
/// during parsing. It helps to process the grammar's productions.
///
#[derive(Debug, Default)]
pub struct ParseStack {
    ///
    /// The actual stack.
    ///
    pub stack: Vec<ParseType>,
    terminal_names: &'static [&'static str],
    non_terminal_names: &'static [&'static str],
}

impl ParseStack {
    ///
    /// Creates a new instance with the given parameters.
    ///
    pub fn new(
        terminal_names: &'static [&'static str],
        non_terminal_names: &'static [&'static str],
    ) -> Self {
        Self {
            stack: Vec::new(),
            terminal_names,
            non_terminal_names,
        }
    }

    fn decode_terminal(&self, terminal_index: TerminalIndex) -> &'static str {
        self.terminal_names[terminal_index as usize]
    }

    fn decode_non_terminal(&self, non_terminal_index: usize) -> &'static str {
        self.non_terminal_names[non_terminal_index]
    }

    /// Returns all relevant tokens that are already expected (i.e. resolved) from the parse stack
    /// The strategy is to only take terminal indices as long no non-terminal (with unresolved
    /// content) or any scanner switch instructions is found. End of production markers are ignored.
    pub(crate) fn expected_token_types(&self) -> Vec<TerminalIndex> {
        self.stack
            .iter()
            .rev()
            .take_while(|e| matches!(e, ParseType::E(_)) || matches!(e, ParseType::T(_)))
            .filter_map(|e| match e {
                ParseType::T(t) => Some(*t),
                _ => None,
            })
            .collect::<Vec<_>>()
    }
}

impl Display for ParseStack {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        self.stack
            .iter()
            .rev()
            .enumerate()
            .try_for_each(|(i, e)| match e {
                ParseType::T(t) => writeln!(f, "{} - T({})", i, self.decode_terminal(*t)),
                ParseType::N(n) => writeln!(f, "{} - N({})", i, self.decode_non_terminal(*n)),
                _ => writeln!(f, "{} - {}", i, e),
            })
    }
}