kiki 7.1.0

A minimalist parser generator for Rust.
Documentation
---
source: kiki/src/tests/e2e_dry.rs
expression: rust_src
---
// This code was generated by Kiki.
// Kiki is an open-source minimalist parser generator for Rust.
// You can read more at https://crates.io/crates/kiki
//
// This code was generated from a grammar with the following hash:
// @sha256 544841a802e160291407ce92621aabf4940055abd8d03e7964dde46f873aff2b

// Since this code is automatically generated,
// some parts may be unidiomatic.
// The linter often complains about these parts.
// However, these warnings are not useful.
// Therefore, we disable certain lints for this file.
#![allow(non_snake_case)]
#![allow(dead_code)]

pub enum Token {
    LParen(()),
    RParen(()),
}

pub enum Expr {
    Empty,
    Wrap(
        (),
        Box<Expr>,
        (),
    ),
}

/// If the parser encounters an unexpected token `t`, it will return `Err(Some(t))`.
/// If the parser encounters an unexpected end of input, it will return `Err(None)`.
pub fn parse<S>(src: S) -> Result<Expr, Option<Token>>
where S: IntoIterator<Item = Token> {
    let mut quasiterminals = src.into_iter()
        .map(Quasiterminal::Terminal)
        .chain(std::iter::once(Quasiterminal::Eof))
        .peekable();
    let mut states = vec![State::S1];
    let mut nodes: Vec<Node> = vec![];
    loop {
        let top_state = *states.last().unwrap();
        let next_quasiterminal_kind = QuasiterminalKind::from_quasiterminal(quasiterminals.peek().unwrap());
        match get_action(top_state, next_quasiterminal_kind) {
            Action::Shift(new_state) => {
                states.push(new_state);
                nodes.push(Node::from_terminal(quasiterminals.next().unwrap().try_into_terminal().unwrap()));
            }

            Action::Reduce(rule_kind) => {
                let (new_node, new_node_kind) = pop_and_reduce(&mut states, &mut nodes, rule_kind);
                nodes.push(new_node);
                let temp_top_state = *states.last().unwrap();
                let Some(new_state) = get_goto(temp_top_state, new_node_kind) else {
                    return Err(quasiterminals.next().unwrap().try_into_terminal().ok());
                };
                states.push(new_state);
            }

            Action::Accept => {
                return Ok(Expr::try_from(nodes.pop().unwrap()).ok().unwrap());
            }

            Action::Err => {
                return Err(quasiterminals.next().unwrap().try_into_terminal().ok());
            }
        }
    }
}

enum Quasiterminal {
    Terminal(Token),
    Eof,
}

#[derive(Clone, Copy, Debug)]
enum QuasiterminalKind {
    LParen = 0,
    RParen = 1,
    Eof = 2,
}

#[derive(Clone, Copy, Debug)]
enum NonterminalKind {
    Expr = 0,
}

#[derive(Clone, Copy, Debug)]
enum State {
    S0 = 0,
    S1 = 1,
    S2 = 2,
    S3 = 3,
    S4 = 4,
}

enum Node {
    Expr(Expr),
    LParen(()),
    RParen(()),
}

#[derive(Clone, Copy, Debug)]
enum Action {
    Shift(State),
    Reduce(RuleKind),
    Accept,
    Err,
}

#[derive(Clone, Copy, Debug)]
enum RuleKind {
    R0 = 0,
    R1 = 1,
}

fn pop_and_reduce(states: &mut Vec<State>, nodes: &mut Vec<Node>, rule_kind: RuleKind) -> (Node, NonterminalKind) {
    match rule_kind {
        RuleKind::R0 => reduce_r0(states, nodes),
        RuleKind::R1 => reduce_r1(states, nodes),
    }
}

fn reduce_r0(_states: &mut Vec<State>, _nodes: &mut Vec<Node>) -> (Node, NonterminalKind) {
    (
        Node::Expr(Expr::Empty),
        NonterminalKind::Expr,
    )
}

fn reduce_r1(states: &mut Vec<State>, nodes: &mut Vec<Node>) -> (Node, NonterminalKind) {
    let t2 = nodes.pop().unwrap().try_into_r_paren_1().ok().unwrap();
    let t1 = Box::new(Expr::try_from(nodes.pop().unwrap()).ok().unwrap());
    let t0 = nodes.pop().unwrap().try_into_l_paren_0().ok().unwrap();

    states.truncate(states.len() - 3);

    (
        Node::Expr(Expr::Wrap(
            t0,
            t1,
            t2,
        )),
        NonterminalKind::Expr,
    )
}

impl QuasiterminalKind {
    fn from_quasiterminal(quasiterminal: &Quasiterminal) -> Self {
        match quasiterminal {
            Quasiterminal::Terminal(terminal) => Self::from_terminal(terminal),
            Quasiterminal::Eof => Self::Eof,
        }
    }

    fn from_terminal(terminal: &Token) -> Self {
        match terminal {
            Token::LParen(_) => Self::LParen,
            Token::RParen(_) => Self::RParen,
        }
    }
}

impl Node {
    fn from_terminal(terminal: Token) -> Self {
        match terminal {
            Token::LParen(t) => Self::LParen(t),
            Token::RParen(t) => Self::RParen(t),
        }
    }
}

impl Quasiterminal {
    fn try_into_terminal(self) -> Result<Token, ()> {
        match self {
            Self::Terminal(terminal) => Ok(terminal),
            Self::Eof => Err(()),
        }
    }
}

static ACTION_TABLE: [[Action; 3]; 5] = [
    [
        Action::Shift(State::S0),
        Action::Reduce(RuleKind::R0),
        Action::Err,
    ],
    [
        Action::Shift(State::S0),
        Action::Err,
        Action::Reduce(RuleKind::R0),
    ],
    [
        Action::Err,
        Action::Shift(State::S3),
        Action::Err,
    ],
    [
        Action::Err,
        Action::Reduce(RuleKind::R1),
        Action::Reduce(RuleKind::R1),
    ],
    [
        Action::Err,
        Action::Err,
        Action::Accept,
    ],
];

fn get_action(top_state: State, next_quasiterminal_kind: QuasiterminalKind) -> Action {
    ACTION_TABLE[top_state as usize][next_quasiterminal_kind as usize]
}

static GOTO_TABLE: [[Option<State>; 1]; 5] = [
    [
        Some(State::S2),
    ],
    [
        Some(State::S4),
    ],
    [
        None,
    ],
    [
        None,
    ],
    [
        None,
    ],
];

fn get_goto(top_state: State, new_node_kind: NonterminalKind) -> Option<State> {
    GOTO_TABLE[top_state as usize][new_node_kind as usize]
}

impl TryFrom<Node> for Expr {
    type Error = Node;

    fn try_from(node: Node) -> Result<Self, Self::Error> {
        match node {
            Node::Expr(n) => Ok(n),
            _ => Err(node),
        }
    }
}

impl Node {
    fn try_into_l_paren_0(self) -> Result<(), Self> {
        match self {
            Self::LParen(t) => Ok(t),
            _ => Err(self),
        }
    }

    fn try_into_r_paren_1(self) -> Result<(), Self> {
        match self {
            Self::RParen(t) => Ok(t),
            _ => Err(self),
        }
    }
}