match_string/
exts.rs

1use crate::base::{Destination, Pattern, PeekableExt, Satisfies};
2use std::cell::RefCell;
3
4pub struct Token<Ref, Dest> {
5    pub predicate: fn(&Ref) -> bool,
6    pub parser: fn(Vec<Ref>) -> Dest,
7    pub at_least: usize,
8    pub skip_leading: Option<fn(&Ref) -> bool>,
9}
10
11impl<'a, Reference, RefT, D> Pattern<'a, Reference> for Token<RefT, D>
12where
13    Reference: Iterator<Item = RefT> + Clone + PeekableExt,
14{
15    type Iter = core::iter::Empty<Reference::Item>;
16    type Dest = D;
17
18    fn get_iter(&'a self) -> Self::Iter {
19        core::iter::empty()
20    }
21
22    fn consume_with_dest(
23        &'a self,
24        reference: &mut Reference,
25        dest: Option<&RefCell<Self::Dest>>,
26    ) -> bool
27    where
28        Reference::Item: Satisfies<<Self::Iter as Iterator>::Item>,
29    {
30        let mut trial = reference.clone();
31        if let Some(skip) = self.skip_leading {
32            while let Some(p) = trial.peek() {
33                if skip(p) {
34                    let _ = trial.next();
35                    continue;
36                }
37                break;
38            }
39        }
40        let mut collected: Vec<RefT> = Vec::new();
41
42        while let Some(peeked) = trial.peek() {
43            if (self.predicate)(peeked) {
44                if let Some(next_item) = trial.next() {
45                    collected.push(next_item);
46                } else {
47                    break;
48                }
49            } else {
50                break;
51            }
52        }
53
54        if collected.len() < self.at_least {
55            return false;
56        }
57        let mut advance = collected.len();
58        if let Some(skip) = self.skip_leading {
59            let mut temp = reference.clone();
60            let mut skipped = 0usize;
61            while let Some(p) = temp.peek() {
62                if skip(p) {
63                    let _ = temp.next();
64                    skipped += 1;
65                    continue;
66                }
67                break;
68            }
69            advance += skipped;
70        }
71        for _ in 0..advance {
72            reference.next();
73        }
74
75        // Parse the collected slice into the destination value
76        let parsed = (self.parser)(collected);
77
78        if let Some(dref) = dest {
79            *dref.borrow_mut() = parsed;
80        }
81
82        true
83    }
84}
85
86impl Destination<char> for usize {}
87
88// Numeric token helpers (parametric by base `N`).
89fn pred_num<const N: u32>(ch: &char) -> bool {
90    ch.to_digit(N).is_some()
91}
92
93fn parse_num<const N: u32>(v: Vec<char>) -> usize {
94    v.into_iter().fold(0usize, |acc, c| {
95        acc.saturating_mul(N as usize)
96            .saturating_add(c.to_digit(N).unwrap() as usize)
97    })
98}
99
100const fn make_num<const N: u32>() -> Token<char, usize> {
101    Token {
102        predicate: pred_num::<N>,
103        parser: parse_num::<N>,
104        at_least: 1,
105        skip_leading: None,
106    }
107}
108
109pub const NUM: Token<char, usize> = make_num::<10>();
110pub const HEX: Token<char, usize> = make_num::<16>();
111pub const OCT: Token<char, usize> = make_num::<8>();
112pub const BIN: Token<char, usize> = make_num::<2>();
113
114pub const WS: Token<char, ()> = Token {
115    predicate: |ch| ch.is_whitespace(),
116    parser: |_| (),
117    at_least: 1,
118    skip_leading: None,
119};
120
121pub const ALPHABETIC: Token<char, String> = Token {
122    predicate: |ch| ch.is_alphabetic(),
123    parser: |v| v.into_iter().collect(),
124    at_least: 1,
125    skip_leading: None,
126};
127
128pub const ALPHANUMERIC: Token<char, String> = Token {
129    predicate: |ch| ch.is_alphanumeric(),
130    parser: |v| v.into_iter().collect(),
131    at_least: 1,
132    skip_leading: None,
133};