keyword_parser/
transition.rs

1use transition_table:: { Optional, Transition };
2use combine::{
3    ParseError, ParseResult, Parser, RangeStream,
4    stream::uncons,
5};
6use core::marker::PhantomData;
7
8struct Keyword<K, J, V, T>
9where
10    T: AsRef<[Transition<K, J, V>]>,
11{
12    tbl: T,
13    _key: PhantomData<K>,
14    _idx: PhantomData<J>,
15    _value: PhantomData<V>,
16}
17
18impl<K, J, V, T> Keyword<K, J, V, T>
19where
20    T: AsRef<[Transition<K, J, V>]>,
21{
22    fn new(tbl: T) -> Self {
23        Self {
24            tbl,
25            _key: PhantomData,
26            _idx: PhantomData,
27            _value: PhantomData,
28        }
29    }
30}
31
32impl<I, J, V, T> Parser<I> for Keyword<I::Token, J, V, T>
33where
34    I: RangeStream,
35    I::Token: Copy + Ord,
36    I::Error: ParseError<I::Token, I::Range, I::Position>,
37    J: Copy + Into<usize>,
38    V: Optional,
39    T: AsRef<[Transition<I::Token, J, V>]>,
40{
41    type Output = V::Inner;
42    type PartialState = ();
43
44    fn parse_lazy(&mut self, input: &mut I) -> ParseResult<Self::Output, I::Error> {
45        let position = input.position();
46        let mut checkpoint = input.checkpoint();
47        let tbl = self.tbl.as_ref();
48        let mut e = match tbl.last() {
49            Some(e) => e,
50            None => {
51                let mut e = I::Error::empty(position);
52
53                e.add_message("table is empty!!");
54                return ParseResult::PeekErr(e.into());
55            },
56        };
57        let mut last_match: Option<V::Inner> = None;
58
59        loop {
60            match uncons(input) {
61                ParseResult::PeekOk(c) | ParseResult::CommitOk(c) => {
62                    let r = &tbl[e.1.into()..e.2.into()];
63
64                    match r.binary_search_by_key(&c, |e| e.0) {
65                        Ok(j) => {
66                            let found = &r[j];
67
68                            if let Some(v) = found.3.inner() {
69                                checkpoint = input.checkpoint();
70                                last_match = Some(v);
71                            }
72                            e = found;
73                        },
74                        Err(_) => break match input.reset(checkpoint) {
75                            Ok(_) => match last_match {
76                                Some(v) => ParseResult::CommitOk(v),
77                                None => ParseResult::PeekErr(I::Error::empty(position).into()),
78                            },
79                            Err(e) => ParseResult::CommitErr(e),
80                        },
81                    }
82                },
83                ParseResult::PeekErr(err) => break match input.reset(checkpoint) {
84                    Ok(_) => match last_match {
85                        Some(v) => ParseResult::CommitOk(v),
86                        None => ParseResult::PeekErr(err),
87                    },
88                    Err(e) => ParseResult::PeekErr(e.into()),
89                },
90                ParseResult::CommitErr(err) => break match input.reset(checkpoint) {
91                    Ok(_) => match last_match {
92                        Some(v) => ParseResult::CommitOk(v),
93                        None => ParseResult::CommitErr(err),
94                    },
95                    Err(e) => ParseResult::CommitErr(e),
96                },
97            }
98        }
99    }
100}
101
102/// Searches keyword using the trie tree generated [keyword] attribute macro.
103/// ```rust
104/// # use keyword_parser::transition::*;
105/// # use transition_table::*;
106/// # use combine::EasyParser;
107/// const UNITS_TBL: [(&str, u8); 27] = [
108///     // L
109///     ("m", 1),
110///     ("inch", 2),
111///     ("もしもし", 3),
112///     // M
113///     ("g", 4),
114///     ("lb", 5),
115///     // T
116///     ("s", 6),
117///     ("Hz", 7),
118///     // θ
119///     ("K", 8),
120///     ("\u{00B0}R", 9), // °R
121///     ("\u{00B0}F", 10), // °F
122///     ("\u{2109}", 11), // ℉
123///     ("\u{00B0}C", 12), // °C
124///     ("\u{2103}", 13), // ℃
125///     // N
126///     ("mol", 14),
127///     // I
128///     ("A", 15),
129///     // J
130///     ("cd", 16),
131///     // Force
132///     ("N", 17),
133///     ("gf", 18),
134///     ("lbf", 19),
135///     // Pressure
136///     ("Pa", 20),
137///     ("ata", 21),
138///     ("psi", 22),
139///     // Energy
140///     ("J", 23),
141///     ("cal", 24),
142///     ("Btu", 25),
143///     ("W", 26),
144///     ("Wh", 27),
145/// ];
146///
147/// let tree = Entry::<char, _>::new(UNITS_TBL.iter());
148/// let tbl: Vec<Transition<_, _, _>> = tree.into();
149/// let mut p = keyword(&tbl);
150/// let i = p.easy_parse("もしもし").map(|x| x.0).unwrap();
151///
152/// assert_eq!(UNITS_TBL[i].1, 3);
153/// ```
154///
155/// [keyword](https://docs.rs/const-array-attrs/*/const_array_attrs/attr.keywords.html)
156pub fn keyword<I, J, V, T>(tbl: T) -> impl Parser<I, Output = V::Inner>
157where
158    I: RangeStream,
159    I::Token: Copy + Ord,
160    I::Error: ParseError<I::Token, I::Range, I::Position>,
161    J: Copy + Into<usize>,
162    V: Optional,
163    T: AsRef<[Transition<I::Token, J, V>]>,
164{
165    Keyword::new(tbl)
166}