1
pub trait SyllableChar {
2
    fn char(&self) -> char;
3
}
4

            
5
/// Matches nothing, consumes nothing, always succeeds
6
pub fn match_unit<T: SyllableChar>() -> impl Fn(&[T]) -> Option<usize> {
7
    |_cs: &[T]| Some(0)
8
}
9

            
10
/// Matches against a single character
11
pub fn match_one<T: SyllableChar>(f: impl Fn(char) -> bool) -> impl Fn(&[T]) -> Option<usize> {
12
    move |cs: &[T]| match cs.first() {
13
        Some(c) if f(c.char()) => Some(1),
14
        _ => None,
15
    }
16
}
17

            
18
/// Succeeds if the input is non empty
19
pub fn match_nonempty<T: SyllableChar>(
20
    f: impl Fn(&[T]) -> Option<usize>,
21
) -> impl Fn(&[T]) -> Option<usize> {
22
    move |cs: &[T]| f(cs).filter(|&n| n > 0)
23
}
24

            
25
/// Succeeds if `f` succeeds otherwise consumes nothing
26
pub fn match_optional<T: SyllableChar>(
27
    f: impl Fn(&[T]) -> Option<usize>,
28
) -> impl Fn(&[T]) -> Option<usize> {
29
    move |cs: &[T]| f(cs).or(Some(0))
30
}
31

            
32
/// `f? g`: matches either `g` or `f g`
33
pub fn match_optional_seq<T: SyllableChar>(
34
    f: impl Fn(&[T]) -> Option<usize>,
35
    g: impl Fn(&[T]) -> Option<usize>,
36
) -> impl Fn(&[T]) -> Option<usize> {
37
    move |cs: &[T]| match_either(&g, match_seq(&f, &g))(cs)
38
}
39

            
40
#[allow(dead_code)]
41
pub fn match_repeat_num<T: SyllableChar>(
42
    num: usize,
43
    f: impl Fn(&[T]) -> Option<usize>,
44
) -> impl Fn(&[T]) -> Option<usize> {
45
    move |mut cs: &[T]| {
46
        let mut total = 0;
47
        for _ in 0..num {
48
            let n = f(cs)?;
49
            total += n;
50
            cs = &cs[n..];
51
        }
52
        Some(total)
53
    }
54
}
55

            
56
/// Match up to `max` instances of `f`, followed by `g`
57
pub fn match_repeat_upto<T: SyllableChar>(
58
    max: usize,
59
    f: impl Fn(&[T]) -> Option<usize>,
60
    g: impl Fn(&[T]) -> Option<usize>,
61
) -> impl Fn(&[T]) -> Option<usize> {
62
    move |mut cs: &[T]| {
63
        // Initial case: zero f matches
64
        let mut best = g(cs);
65
        let mut nf = 0;
66
        for _ in 0..max {
67
            // Match up to max instances of f
68
            if let Some(n) = f(cs) {
69
                nf += n;
70
                cs = &cs[n..];
71
                // If f is followed by g update matching range
72
                if let Some(ng) = g(cs) {
73
                    best = Some(nf + ng);
74
                }
75
            } else {
76
                break;
77
            }
78
        }
79
        best
80
    }
81
}
82

            
83
/// Match `f1` followed by `f2`.
84
///
85
/// Fails if `f1` or `f2` fail.
86
pub fn match_seq<T: SyllableChar>(
87
    f1: impl Fn(&[T]) -> Option<usize>,
88
    f2: impl Fn(&[T]) -> Option<usize>,
89
) -> impl Fn(&[T]) -> Option<usize> {
90
    move |cs: &[T]| {
91
        let n1 = f1(cs)?;
92
        let n2 = f2(&cs[n1..])?;
93
        Some(n1 + n2)
94
    }
95
}
96

            
97
/// Matches whichever of `f1` or `f2` match the most input.
98
///
99
/// Uses `f2`'s match if they match the same input
100
pub fn match_either<T: SyllableChar>(
101
    f1: impl Fn(&[T]) -> Option<usize>,
102
    f2: impl Fn(&[T]) -> Option<usize>,
103
) -> impl Fn(&[T]) -> Option<usize> {
104
    move |cs: &[T]| {
105
        let n1 = f1(cs);
106
        let n2 = f2(cs);
107
        std::cmp::max(n1, n2)
108
    }
109
}
110

            
111
pub fn match_either_seq<T: SyllableChar>(
112
    f1: impl Fn(&[T]) -> Option<usize>,
113
    f2: impl Fn(&[T]) -> Option<usize>,
114
    g: impl Fn(&[T]) -> Option<usize>,
115
) -> impl Fn(&[T]) -> Option<usize> {
116
    move |cs: &[T]| {
117
        let n1 = match_seq(&f1, &g)(cs);
118
        let n2 = match_seq(&f2, &g)(cs);
119
        std::cmp::max(n1, n2)
120
    }
121
}