use super::*; use std::iter::FromIterator; use nom::{Err, error::{ErrorKind, ParseError}}; use nom::{FindSubstring, InputLength, InputTake, IResult}; use nom::branch::*; use nom::bytes::complete::*; use nom::combinator::*; use nom::multi::*; use nom::number::complete::*; use nom::sequence::*; fn take_until_any>(tags: Vec) -> impl Fn(Input) -> IResult where Input: InputTake + FindSubstring, T: InputLength + Clone, { move |i: Input| { let min_index = tags.iter().filter_map(|tag| i.find_substring(tag.clone())).min(); let res: IResult<_, _, Error> = match min_index { None => Err(Err::Error(Error::from_error_kind(i, ErrorKind::TakeUntil))), Some(index) => Ok(i.take_split(index)), }; res } } fn take_all(input: &str) -> IResult<&str, &str> { take_while(|_| true)(input) } fn terminal(input: &str) -> IResult<&str, Symbol> { let (input, mut terminal_val) = verify(alt((take_until_any(vec!["[", "//", "^", "\n"]), take_all)), |s: &str| s.len() > 0)(input)?; if !input.starts_with("[") { terminal_val = terminal_val.trim_end(); } Ok((input, Symbol::Terminal(String::from(terminal_val)))) } fn nonterminal_name(input: &str) -> IResult<&str, String> { let (input, head) = take_while_m_n(1, 1, |c: char| (c.is_alphabetic() || c == '_'))(input)?; let (input, tail) = take_while(|c: char| (c.is_alphanumeric() || c == '_'))(input)?; Ok((input, String::from(head.to_owned() + tail))) } fn nonterminal(input: &str) -> IResult<&str, Symbol> { map(delimited(tag("["), nonterminal_name, tag("]")), Symbol::NonTerminal)(input) } fn rat(input: &str) -> IResult<&str, f64> { let (input, num) = double(input)?; let (input, _) = tag("/")(input)?; let (input, denom) = verify(double, |&f| f != 0.0)(input)?; Ok((input, num / denom)) } fn weight(input: &str) -> IResult<&str, f64> { preceded(tag("^"), alt((rat, double)))(input) } fn comment(input: &str) -> IResult<&str, ()> { map(tuple((tag("//"), is_not("\n"))), |_| ())(input) } fn whitespace(input: &str) -> IResult<&str, ()> { map(take_while(|c| (c == ' ' || c == '\t')), |_| ())(input) } fn eol(input: &str) -> IResult<&str, ()> { map(alt((tag("\n"), all_consuming(take(0usize)))), |_| ())(input) } fn empty_lines(input: &str) -> IResult<&str, ()> { map( tuple(( many0(tuple((whitespace, opt(comment), tag("\n")))), opt(tuple((whitespace, opt(comment), eol))), )), |_| () )(input) } fn expr(input: &str) -> IResult<&str, (Expr, f64)> { let (input, (_, _, syms, _, weight, _, _, _, _)) = tuple(( empty_lines, alt((tag(" "), tag("\t"))), many1(alt((terminal, nonterminal))), whitespace, opt(weight), whitespace, opt(comment), eol, empty_lines, ))(input)?; Ok((input, (Expr(syms), weight.unwrap_or(1.0)))) } fn list(input: &str) -> IResult<&str, (String, List)> { let (input, (_, name, _, _, _, exprs)) = tuple(( empty_lines, nonterminal_name, whitespace, opt(comment), tag("\n"), many1(expr), ))(input)?; Ok((input, (name, List(exprs)))) } fn grammar(input: &str) -> IResult<&str, Grammar> { let (input, lists) = many1(list)(input)?; Ok((input, Grammar(HashMap::from_iter(lists.into_iter())))) } pub fn load_grammar(input: &str) -> Result> { all_consuming(grammar)(input).map(|(_, g)| g) }