keyword-parser 0.0.3

Keyword parser for combine.
Documentation
use transition_table:: { Optional, Transition };
use combine::{
    ParseError, ParseResult, Parser, RangeStream,
    stream::uncons,
};
use core::marker::PhantomData;

struct Keyword<K, J, V, T>
where
    T: AsRef<[Transition<K, J, V>]>,
{
    tbl: T,
    _key: PhantomData<K>,
    _idx: PhantomData<J>,
    _value: PhantomData<V>,
}

impl<K, J, V, T> Keyword<K, J, V, T>
where
    T: AsRef<[Transition<K, J, V>]>,
{
    fn new(tbl: T) -> Self {
        Self {
            tbl,
            _key: PhantomData,
            _idx: PhantomData,
            _value: PhantomData,
        }
    }
}

impl<I, J, V, T> Parser<I> for Keyword<I::Token, J, V, T>
where
    I: RangeStream,
    I::Token: Copy + Ord,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    J: Copy + Into<usize>,
    V: Optional,
    T: AsRef<[Transition<I::Token, J, V>]>,
{
    type Output = V::Inner;
    type PartialState = ();

    fn parse_lazy(&mut self, input: &mut I) -> ParseResult<Self::Output, I::Error> {
        let position = input.position();
        let mut checkpoint = input.checkpoint();
        let tbl = self.tbl.as_ref();
        let mut e = match tbl.last() {
            Some(e) => e,
            None => {
                let mut e = I::Error::empty(position);

                e.add_message("table is empty!!");
                return ParseResult::PeekErr(e.into());
            },
        };
        let mut last_match: Option<V::Inner> = None;

        loop {
            match uncons(input) {
                ParseResult::PeekOk(c) | ParseResult::CommitOk(c) => {
                    let r = &tbl[e.1.into()..e.2.into()];

                    match r.binary_search_by_key(&c, |e| e.0) {
                        Ok(j) => {
                            let found = &r[j];

                            if let Some(v) = found.3.inner() {
                                checkpoint = input.checkpoint();
                                last_match = Some(v);
                            }
                            e = found;
                        },
                        Err(_) => break match input.reset(checkpoint) {
                            Ok(_) => match last_match {
                                Some(v) => ParseResult::CommitOk(v),
                                None => ParseResult::PeekErr(I::Error::empty(position).into()),
                            },
                            Err(e) => ParseResult::CommitErr(e),
                        },
                    }
                },
                ParseResult::PeekErr(err) => break match input.reset(checkpoint) {
                    Ok(_) => match last_match {
                        Some(v) => ParseResult::CommitOk(v),
                        None => ParseResult::PeekErr(err),
                    },
                    Err(e) => ParseResult::PeekErr(e.into()),
                },
                ParseResult::CommitErr(err) => break match input.reset(checkpoint) {
                    Ok(_) => match last_match {
                        Some(v) => ParseResult::CommitOk(v),
                        None => ParseResult::CommitErr(err),
                    },
                    Err(e) => ParseResult::CommitErr(e),
                },
            }
        }
    }
}

/// Searches keyword using the trie tree generated [keyword] attribute macro.
/// ```rust
/// # use keyword_parser::transition::*;
/// # use transition_table::*;
/// # use combine::EasyParser;
/// const UNITS_TBL: [(&str, u8); 27] = [
///     // L
///     ("m", 1),
///     ("inch", 2),
///     ("もしもし", 3),
///     // M
///     ("g", 4),
///     ("lb", 5),
///     // T
///     ("s", 6),
///     ("Hz", 7),
///     // θ
///     ("K", 8),
///     ("\u{00B0}R", 9), // °R
///     ("\u{00B0}F", 10), // °F
///     ("\u{2109}", 11), // ℉
///     ("\u{00B0}C", 12), // °C
///     ("\u{2103}", 13), // ℃
///     // N
///     ("mol", 14),
///     // I
///     ("A", 15),
///     // J
///     ("cd", 16),
///     // Force
///     ("N", 17),
///     ("gf", 18),
///     ("lbf", 19),
///     // Pressure
///     ("Pa", 20),
///     ("ata", 21),
///     ("psi", 22),
///     // Energy
///     ("J", 23),
///     ("cal", 24),
///     ("Btu", 25),
///     ("W", 26),
///     ("Wh", 27),
/// ];
///
/// let tree = Entry::<char, _>::new(UNITS_TBL.iter());
/// let tbl: Vec<Transition<_, _, _>> = tree.into();
/// let mut p = keyword(&tbl);
/// let i = p.easy_parse("もしもし").map(|x| x.0).unwrap();
///
/// assert_eq!(UNITS_TBL[i].1, 3);
/// ```
///
/// [keyword](https://docs.rs/const-array-attrs/*/const_array_attrs/attr.keywords.html)
pub fn keyword<I, J, V, T>(tbl: T) -> impl Parser<I, Output = V::Inner>
where
    I: RangeStream,
    I::Token: Copy + Ord,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    J: Copy + Into<usize>,
    V: Optional,
    T: AsRef<[Transition<I::Token, J, V>]>,
{
    Keyword::new(tbl)
}