1 use crate::grammar::parse_tree::{ActionKind, Alternative, ExprSymbol, Name, Symbol, SymbolKind};
2 
3 #[derive(Debug)]
4 pub enum AlternativeAction<'a> {
5     User(&'a ActionKind),
6     Default(Symbols<'a>),
7 }
8 
9 #[derive(Debug)]
10 pub enum Symbols<'a> {
11     Named(Vec<(usize, Name, &'a Symbol)>),
12     Anon(Vec<(usize, &'a Symbol)>),
13 }
14 
analyze_action(alt: &Alternative) -> AlternativeAction<'_>15 pub fn analyze_action(alt: &Alternative) -> AlternativeAction<'_> {
16     // We can't infer types for alternatives with actions
17     if let Some(ref code) = alt.action {
18         return AlternativeAction::User(code);
19     }
20 
21     AlternativeAction::Default(analyze_expr(&alt.expr))
22 }
23 
analyze_expr(expr: &ExprSymbol) -> Symbols<'_>24 pub fn analyze_expr(expr: &ExprSymbol) -> Symbols<'_> {
25     // First look for named symbols.
26     let named_symbols: Vec<_> = expr
27         .symbols
28         .iter()
29         .enumerate()
30         .filter_map(|(idx, sym)| match sym.kind {
31             SymbolKind::Name(ref id, ref sub) => Some((idx, id.clone(), &**sub)),
32             _ => None,
33         })
34         .collect();
35     if !named_symbols.is_empty() {
36         return Symbols::Named(named_symbols);
37     }
38 
39     // Otherwise, make a tuple of the items they chose with `<>`.
40     let chosen_symbol_types: Vec<_> = expr
41         .symbols
42         .iter()
43         .enumerate()
44         .filter_map(|(idx, sym)| match sym.kind {
45             SymbolKind::Choose(ref sub) => Some((idx, &**sub)),
46             _ => None,
47         })
48         .collect();
49     if !chosen_symbol_types.is_empty() {
50         return Symbols::Anon(chosen_symbol_types);
51     }
52 
53     // If they didn't choose anything with `<>`, make a tuple of everything.
54     Symbols::Anon(expr.symbols.iter().enumerate().collect())
55 }
56 
57 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
58 pub enum Presence {
59     None,
60     InCurlyBrackets,
61     Normal,
62 }
63 
64 impl Presence {
is_in_curly_brackets(self) -> bool65     pub fn is_in_curly_brackets(self) -> bool {
66         self == Presence::InCurlyBrackets
67     }
68 }
69 
check_between_braces(action: &str) -> Presence70 pub fn check_between_braces(action: &str) -> Presence {
71     if let Some(funky_index) = action.find("<>") {
72         let (before, after) = {
73             let (before, after) = action.split_at(funky_index);
74             (before.trim(), after[2..].trim())
75         };
76 
77         let last_before = before.chars().last();
78         let next_after = after.chars().next();
79         if let (Some('{'), Some('}')) = (last_before, next_after) {
80             Presence::InCurlyBrackets
81         } else {
82             Presence::Normal
83         }
84     } else {
85         Presence::None
86     }
87 }
88 
89 #[cfg(test)]
90 mod test {
91     use super::*;
92 
93     #[test]
detecting_normal_funky_expression()94     fn detecting_normal_funky_expression() {
95         assert_eq!(Presence::Normal, check_between_braces("<>"));
96         assert_eq!(Presence::Normal, check_between_braces("ble <> blaa"));
97         assert_eq!(Presence::Normal, check_between_braces("ble <> } b"));
98         assert_eq!(Presence::Normal, check_between_braces("bl{ e <> } b"));
99         assert_eq!(Presence::Normal, check_between_braces("bl{ e <>} b"));
100         assert_eq!(Presence::Normal, check_between_braces("bl{ e <> e } b"));
101         assert_eq!(Presence::Normal, check_between_braces("bl{ <> e } b"));
102         assert_eq!(Presence::Normal, check_between_braces("bl{<> e } b"));
103         assert_eq!(Presence::Normal, check_between_braces("bl{<>"));
104         assert_eq!(Presence::Normal, check_between_braces("<>}"));
105     }
106 
107     #[test]
detecting_nopresence_of_funky_expression()108     fn detecting_nopresence_of_funky_expression() {
109         assert_eq!(Presence::None, check_between_braces("< >"));
110         assert_eq!(Presence::None, check_between_braces("ble <b> blaa"));
111     }
112 
113     #[test]
detecting_incurlybrackets_funky_expression()114     fn detecting_incurlybrackets_funky_expression() {
115         assert_eq!(Presence::InCurlyBrackets, check_between_braces("{<>}"));
116         assert_eq!(
117             Presence::InCurlyBrackets,
118             check_between_braces("ble{<> }blaa")
119         );
120         assert_eq!(
121             Presence::InCurlyBrackets,
122             check_between_braces("ble{ <> } b")
123         );
124         assert_eq!(
125             Presence::InCurlyBrackets,
126             check_between_braces("bl{         <>} b")
127         );
128         assert_eq!(Presence::InCurlyBrackets, check_between_braces("bl{<>} b"));
129         assert_eq!(
130             Presence::InCurlyBrackets,
131             check_between_braces("bl{<>         } b")
132         );
133     }
134 }
135