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),
},
}
}
}
}
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)
}