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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Inlining of nonterminals

use crate::grammar::repr::*;
use crate::normalize::NormResult;

mod graph;

#[cfg(test)]
mod test;

pub fn inline(mut grammar: Grammar) -> NormResult<Grammar> {
    let order = graph::inline_order(&grammar)?;
    for nt in order {
        inline_nt(&mut grammar, &nt);
    }
    Ok(grammar)
}

fn inline_nt(grammar: &mut Grammar, inline_nt: &NonterminalString) {
    let inline_productions = grammar.productions_for(inline_nt).to_vec();
    for data in grammar.nonterminals.values_mut() {
        let mut new_productions = vec![];
        let mut new_action_fn_defns = vec![];

        for into_production in &data.productions {
            if !into_production
                .symbols
                .contains(&Symbol::Nonterminal(inline_nt.clone()))
            {
                new_productions.push(into_production.clone());
                continue;
            }

            let mut inliner = Inliner {
                action_fn_defns: &grammar.action_fn_defns,
                inline_nonterminal: inline_nt.clone(),
                into_production,
                inline_fallible: 0,
                inline_productions: &inline_productions,
                new_symbols: vec![],
                new_productions: &mut new_productions,
                new_action_fn_defns: &mut new_action_fn_defns,
            };

            inliner.inline(&into_production.symbols);
        }

        data.productions = new_productions;
        grammar.action_fn_defns.extend(new_action_fn_defns);
    }
}

struct Inliner<'a> {
    /// Action fn defns
    action_fn_defns: &'a [ActionFnDefn],

    /// The nonterminal `A` being inlined
    inline_nonterminal: NonterminalString,

    /// The full set of productions `A = B C D | E F G` for the
    /// nonterminal `A` being inlined
    inline_productions: &'a [Production],

    /// Number of actions that we have inlined for `A` so far which
    /// have been fallible. IOW, if we are inlining `A` into `X = Y A
    /// A Z`, and in the first instance of `A` we used a fallible
    /// action, but the second we used an infallible one, count would
    /// be 1.
    inline_fallible: u32,

    /// The `X = Y A Z` being inlined into
    into_production: &'a Production,

    /// The list of symbols we building up for the new production.
    /// For example, this would (eventually) contain `Y B C D Z`,
    /// given our running example.
    new_symbols: Vec<InlinedSymbol>,

    /// The output vector of all productions for `X` that we have created
    new_productions: &'a mut Vec<Production>,

    /// Vector of all action fn defns from the grammar.
    new_action_fn_defns: &'a mut Vec<ActionFnDefn>,
}

impl<'a> Inliner<'a> {
    fn inline(&mut self, into_symbols: &[Symbol]) {
        if into_symbols.is_empty() {
            // create an action fn for the result of inlining
            let into_action = self.into_production.action;
            let into_fallible = self.action_fn_defns[into_action.index()].fallible;
            let into_ret_type = self.action_fn_defns[into_action.index()].ret_type.clone();
            let inline_fallible = self.inline_fallible != 0;
            let index = self.action_fn_defns.len() + self.new_action_fn_defns.len();
            let action_fn = ActionFn::new(index);
            let inline_defn = InlineActionFnDefn {
                action: into_action,
                symbols: self.new_symbols.clone(),
            };
            self.new_action_fn_defns.push(ActionFnDefn {
                fallible: into_fallible || inline_fallible,
                ret_type: into_ret_type,
                kind: ActionFnDefnKind::Inline(inline_defn),
            });
            let prod_symbols: Vec<Symbol> = self
                .new_symbols
                .iter()
                .flat_map(|sym| match *sym {
                    InlinedSymbol::Original(ref s) => vec![s.clone()],
                    InlinedSymbol::Inlined(_, ref s) => s.clone(),
                })
                .collect();
            self.new_productions.push(Production {
                nonterminal: self.into_production.nonterminal.clone(),
                span: self.into_production.span,
                symbols: prod_symbols,
                action: action_fn,
            });
        } else {
            let next_symbol = &into_symbols[0];
            match *next_symbol {
                Symbol::Nonterminal(ref n) if *n == self.inline_nonterminal => {
                    // Replace the current symbol with each of the
                    // `inline_productions` in turn.
                    for inline_production in self.inline_productions {
                        // If this production is fallible, increment
                        // count of fallible actions.
                        let inline_action = inline_production.action;
                        let fallible = self.action_fn_defns[inline_action.index()].fallible;
                        self.inline_fallible += fallible as u32;

                        // Push the symbols of the production inline.
                        self.new_symbols.push(InlinedSymbol::Inlined(
                            inline_production.action,
                            inline_production.symbols.clone(),
                        ));

                        // Inline remaining symbols:
                        self.inline(&into_symbols[1..]);

                        // Reset state after we have inlined remaining symbols:
                        self.new_symbols.pop();
                        self.inline_fallible -= fallible as u32;
                    }
                }
                _ => {
                    self.new_symbols
                        .push(InlinedSymbol::Original(next_symbol.clone()));
                    self.inline(&into_symbols[1..]);
                    self.new_symbols.pop();
                }
            }
        }
    }
}