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