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
use crate::grammar::parse_tree::{ActionKind, Alternative, ExprSymbol, Name, Symbol, SymbolKind};

#[derive(Debug)]
pub enum AlternativeAction<'a> {
    User(&'a ActionKind),
    Default(Symbols<'a>),
}

#[derive(Debug)]
pub enum Symbols<'a> {
    Named(Vec<(usize, Name, &'a Symbol)>),
    Anon(Vec<(usize, &'a Symbol)>),
}

pub fn analyze_action(alt: &Alternative) -> AlternativeAction<'_> {
    // We can't infer types for alternatives with actions
    if let Some(ref code) = alt.action {
        return AlternativeAction::User(code);
    }

    AlternativeAction::Default(analyze_expr(&alt.expr))
}

pub fn analyze_expr(expr: &ExprSymbol) -> Symbols<'_> {
    // First look for named symbols.
    let named_symbols: Vec<_> = expr
        .symbols
        .iter()
        .enumerate()
        .filter_map(|(idx, sym)| match sym.kind {
            SymbolKind::Name(ref id, ref sub) => Some((idx, id.clone(), &**sub)),
            _ => None,
        })
        .collect();
    if !named_symbols.is_empty() {
        return Symbols::Named(named_symbols);
    }

    // Otherwise, make a tuple of the items they chose with `<>`.
    let chosen_symbol_types: Vec<_> = expr
        .symbols
        .iter()
        .enumerate()
        .filter_map(|(idx, sym)| match sym.kind {
            SymbolKind::Choose(ref sub) => Some((idx, &**sub)),
            _ => None,
        })
        .collect();
    if !chosen_symbol_types.is_empty() {
        return Symbols::Anon(chosen_symbol_types);
    }

    // If they didn't choose anything with `<>`, make a tuple of everything.
    Symbols::Anon(expr.symbols.iter().enumerate().collect())
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Presence {
    None,
    InCurlyBrackets,
    Normal,
}

impl Presence {
    pub fn is_in_curly_brackets(self) -> bool {
        self == Presence::InCurlyBrackets
    }
}

pub fn check_between_braces(action: &str) -> Presence {
    if let Some(funky_index) = action.find("<>") {
        let (before, after) = {
            let (before, after) = action.split_at(funky_index);
            (before.trim(), after[2..].trim())
        };

        let last_before = before.chars().last();
        let next_after = after.chars().next();
        if let (Some('{'), Some('}')) = (last_before, next_after) {
            Presence::InCurlyBrackets
        } else {
            Presence::Normal
        }
    } else {
        Presence::None
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn detecting_normal_funky_expression() {
        assert_eq!(Presence::Normal, check_between_braces("<>"));
        assert_eq!(Presence::Normal, check_between_braces("ble <> blaa"));
        assert_eq!(Presence::Normal, check_between_braces("ble <> } b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{ e <> } b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{ e <>} b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{ e <> e } b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{ <> e } b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{<> e } b"));
        assert_eq!(Presence::Normal, check_between_braces("bl{<>"));
        assert_eq!(Presence::Normal, check_between_braces("<>}"));
    }

    #[test]
    fn detecting_nopresence_of_funky_expression() {
        assert_eq!(Presence::None, check_between_braces("< >"));
        assert_eq!(Presence::None, check_between_braces("ble <b> blaa"));
    }

    #[test]
    fn detecting_incurlybrackets_funky_expression() {
        assert_eq!(Presence::InCurlyBrackets, check_between_braces("{<>}"));
        assert_eq!(
            Presence::InCurlyBrackets,
            check_between_braces("ble{<> }blaa")
        );
        assert_eq!(
            Presence::InCurlyBrackets,
            check_between_braces("ble{ <> } b")
        );
        assert_eq!(
            Presence::InCurlyBrackets,
            check_between_braces("bl{         <>} b")
        );
        assert_eq!(Presence::InCurlyBrackets, check_between_braces("bl{<>} b"));
        assert_eq!(
            Presence::InCurlyBrackets,
            check_between_braces("bl{<>         } b")
        );
    }
}