slipknot 0.1.0

Parsing ANTLR v4 grammar using Rust's nom library.
Documentation
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)| {
            // TODO support unicode
            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))
}