use nom::IResult;
use nom_locate::LocatedSpan;
use super::*;
type Span<'a> = LocatedSpan<&'a str>;
pub fn parse_grammar(input: Span) -> IResult<Span, Grammar> {
let (input, rules) = nom::combinator::all_consuming(nom::sequence::terminated(
parse_rules,
nom::combinator::eof,
))(input)?;
Ok((input, Grammar { rules }))
}
fn parse_rules(input: Span) -> IResult<Span, Vec<Rule>> {
let (input, rules) =
nom::multi::separated_list1(nom::character::complete::multispace1, parse_rule)(input)?;
Ok((input, rules))
}
fn parse_rule(input: Span) -> IResult<Span, Rule> {
let (input, rule) = nom::branch::alt((
nom::sequence::preceded(
nom::combinator::peek(nom::character::complete::satisfy(|c| c.is_uppercase())),
nom::combinator::map(parse_lexer_rule, Rule::LexerRule),
),
nom::sequence::preceded(
nom::combinator::peek(nom::character::complete::satisfy(|c| c.is_lowercase())),
nom::combinator::map(parse_parser_rule, Rule::ParserRule),
),
))(input)?;
Ok((input, rule))
}
fn parse_lexer_rule(input: Span) -> IResult<Span, LexerRule> {
let (input, (token_ref, _, lexer_alt_list, _)) = nom::sequence::tuple((
parse_token_ref,
nom::character::complete::char(':'),
parse_lexer_alt_list,
nom::character::complete::char(';'),
))(input)?;
Ok((
input,
LexerRule {
token_ref,
lexer_alt_list,
},
))
}
fn parse_token_ref(input: Span) -> IResult<Span, String> {
let (input, token_ref) = nom::sequence::delimited(
nom::combinator::peek(nom::character::complete::satisfy(|c| c.is_uppercase())),
nom::character::complete::alpha1,
nom::character::complete::multispace0,
)(input)?;
Ok((input, token_ref.to_string()))
}
fn parse_lexer_alt_list(input: Span) -> IResult<Span, Vec<LexerAlt>> {
let (input, lexer_alt_list) = nom::multi::separated_list1(
nom::character::complete::char('|'),
nom::sequence::preceded(nom::character::complete::multispace0, parse_lexer_alt),
)(input)?;
Ok((input, lexer_alt_list))
}
fn parse_lexer_alt(input: Span) -> IResult<Span, LexerAlt> {
let (input, lexer_elements) = nom::multi::separated_list1(
nom::character::complete::multispace1,
parse_lexer_element,
)(input)?;
Ok((input, LexerAlt { lexer_elements }))
}
fn parse_lexer_element(input: Span) -> IResult<Span, LexerElement> {
let (input, (lexer_atom, ebnf_suffix)) =
nom::sequence::pair(parse_lexer_atom, parse_ebnf_suffix)(input)?;
Ok((
input,
LexerElement {
lexer_atom,
ebnf_suffix,
},
))
}
fn parse_lexer_atom(input: Span) -> IResult<Span, LexerAtom> {
let (input, lexer_atom) = nom::branch::alt((
nom::combinator::map(parse_lexer_char_set, LexerAtom::LexerCharSet),
nom::combinator::map(parse_character_range, LexerAtom::CharacterRange),
nom::combinator::map(parse_terminal, LexerAtom::Terminal),
))(input)?;
Ok((input, lexer_atom))
}
fn parse_terminal(input: Span) -> IResult<Span, Terminal> {
let (input, terminal) = nom::branch::alt((
nom::combinator::map(parse_literal, Terminal::Literal),
nom::combinator::map(parse_token_ref, Terminal::TokenRef),
))(input)?;
Ok((input, terminal))
}
fn parse_character_range(input: Span) -> IResult<Span, CharacterRange> {
let (input, (start, end)) = nom::combinator::map(
nom::sequence::separated_pair(
parse_literal,
nom::sequence::pair(
nom::bytes::complete::tag(".."),
nom::character::complete::multispace0,
),
parse_literal,
),
|(start, end)| {
if start.len() != 1 || end.len() != 1 {
nom::error::Error::new(input, nom::error::ErrorKind::Tag);
}
let start_char = start.chars().next().unwrap();
let end_char = end.chars().next().unwrap();
if !start_char.is_ascii() || !end_char.is_ascii() {
nom::error::Error::new(input, nom::error::ErrorKind::Tag);
}
(start_char, end_char)
},
)(input)?;
Ok((input, CharacterRange { start, end }))
}
fn parse_lexer_char_set(input: Span) -> IResult<Span, LexerCharSet> {
let (input, chars) = nom::sequence::delimited(
nom::character::complete::char('['),
nom::multi::many1(parse_lexer_char),
nom::character::complete::char(']'),
)(input)?;
Ok((input, LexerCharSet { chars }))
}
fn parse_lexer_char(input: Span) -> IResult<Span, LexerChar> {
let (input, lexer_char) = nom::branch::alt((
nom::combinator::map(parse_lexer_char_range, LexerChar::CharacterRange),
nom::combinator::map(parse_single_character, LexerChar::SingleCharacter),
))(input)?;
Ok((input, lexer_char))
}
fn parse_lexer_char_range(input: Span) -> IResult<Span, CharacterRange> {
let (input, (start, end)) = nom::sequence::separated_pair(
nom::character::complete::satisfy(|c| c != ']'),
nom::character::complete::char('-'),
nom::character::complete::satisfy(|c| c != ']'),
)(input)?;
Ok((input, CharacterRange { start, end }))
}
fn parse_single_character(input: Span) -> IResult<Span, char> {
let (input, char) = nom::character::complete::satisfy(|c| c != ']')(input)?;
Ok((input, char))
}
fn parse_literal(input: Span) -> IResult<Span, String> {
let (input, (_, literal, _, _)) = nom::sequence::tuple((
nom::character::complete::char('\''),
nom::bytes::complete::take_until("'"),
nom::character::complete::char('\''),
nom::character::streaming::multispace0,
))(input)?;
Ok((input, literal.to_string()))
}
fn parse_parser_rule(input: Span) -> IResult<Span, ParserRule> {
let (input, (rule_ref, _, alt_list, _)) = nom::sequence::tuple((
parse_rule_ref,
nom::character::complete::char(':'),
parse_rule_alt_list,
nom::character::complete::char(';'),
))(input)?;
Ok((input, ParserRule { rule_ref, alt_list }))
}
fn parse_rule_alt_list(input: Span) -> IResult<Span, Vec<Alternative>> {
let (input, rule_alt_list) = nom::multi::separated_list1(
nom::character::complete::char('|'),
nom::sequence::preceded(nom::character::complete::multispace0, parse_alternative),
)(input)?;
Ok((input, rule_alt_list))
}
fn parse_alternative(input: Span) -> IResult<Span, Alternative> {
let (input, elements) =
nom::multi::separated_list1(nom::character::complete::multispace1, parse_element)(input)?;
Ok((input, Alternative { elements }))
}
fn parse_element(input: Span) -> IResult<Span, Element> {
let (input, element) = nom::branch::alt((
nom::combinator::map(parse_atom_element, Element::AtomElement),
nom::combinator::map(parse_ebnf_element, Element::EbnfElement),
))(input)?;
Ok((input, element))
}
fn parse_atom_element(input: Span) -> IResult<Span, AtomElement> {
let (input, (atom, ebnf_suffix)) = nom::sequence::pair(parse_atom, parse_ebnf_suffix)(input)?;
Ok((input, AtomElement { atom, ebnf_suffix }))
}
fn parse_atom(input: Span) -> IResult<Span, Atom> {
let (input, atom) = nom::branch::alt((
nom::combinator::map(parse_terminal, Atom::Terminal),
nom::combinator::map(parse_rule_ref, Atom::RuleRef),
))(input)?;
Ok((input, atom))
}
fn parse_ebnf_suffix(input: Span) -> IResult<Span, Option<EbnfSuffix>> {
let (input, ebnf_suffix) = nom::combinator::map(
nom::combinator::opt(nom::character::complete::one_of("?*+")),
|c| match c {
Some('?') => Some(EbnfSuffix::Question),
Some('*') => Some(EbnfSuffix::Star),
Some('+') => Some(EbnfSuffix::Plus),
_ => None,
},
)(input)?;
Ok((input, ebnf_suffix))
}
fn parse_rule_ref(input: Span) -> IResult<Span, String> {
let (input, token_ref) = nom::sequence::delimited(
nom::combinator::peek(nom::character::complete::satisfy(|c| c.is_lowercase())),
nom::character::complete::alpha1,
nom::character::complete::multispace0,
)(input)?;
Ok((input, token_ref.to_string()))
}
fn parse_ebnf_element(input: Span) -> IResult<Span, EbnfElement> {
let (input, (block, ebnf_suffix)) = nom::sequence::pair(parse_block, parse_ebnf_suffix)(input)?;
Ok((input, EbnfElement { block, ebnf_suffix }))
}
fn parse_block(input: Span) -> IResult<Span, Vec<Alternative>> {
let (input, block) = nom::sequence::delimited(
nom::character::complete::char('('),
parse_rule_alt_list,
nom::character::complete::char(')'),
)(input)?;
Ok((input, block))
}