ielr 0.1.1

Table generation backend for LR parser generators
Documentation
/*
 * Copyright 2022 Arnaud Golfouse
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

use crate::input::Token;

/// Creates a new token, panics if passed `0`.
const fn new_token(n: crate::indices::PlainToken) -> Token {
    match Token::new(n) {
        Some(token) => token,
        None => panic!("`Token` cannot be 0"),
    }
}

pub(crate) fn collect<T: Copy, C>(slice: &[T]) -> C
where
    C: FromIterator<T>,
{
    slice.iter().copied().collect()
}

pub(crate) mod tokens {
    use crate::{input::Token, test_common::new_token};
    pub(crate) const T1: Token = new_token(1);
    pub(crate) const T2: Token = new_token(2);
    pub(crate) const T3: Token = new_token(3);
    pub(crate) const T4: Token = new_token(4);
    pub(crate) const T5: Token = new_token(5);
    pub(crate) const INT: Token = new_token(6);
    pub(crate) const PLUS: Token = new_token(7);
    pub(crate) const MINUS: Token = new_token(8);
    pub(crate) const TIMES: Token = new_token(9);
    pub(crate) const DIV: Token = new_token(10);
    pub(crate) const EQUAL: Token = new_token(11);
    pub(crate) const NEQUAL: Token = new_token(12);
    pub(crate) const MORE: Token = new_token(13);
    pub(crate) const LESS: Token = new_token(14);
    pub(crate) const MOREEQ: Token = new_token(15);
    pub(crate) const LESSEQ: Token = new_token(16);
    pub(crate) const AND: Token = new_token(17);
    pub(crate) const OR: Token = new_token(18);
    pub(crate) const XOR: Token = new_token(19);
    pub(crate) const RANGE: Token = new_token(20);
    pub(crate) const OPEN_RANGE: Token = new_token(21);
    pub(crate) const BITAND: Token = new_token(22);
    pub(crate) const BITOR: Token = new_token(23);
    pub(crate) const BITXOR: Token = new_token(24);
    pub(crate) const LSHIFT: Token = new_token(25);
    pub(crate) const RSHIFT: Token = new_token(26);
    pub(crate) const ASSIGN: Token = new_token(27);
    pub(crate) const ASSIGN_ADD: Token = new_token(28);
    pub(crate) const ASSIGN_SUB: Token = new_token(29);
    pub(crate) const ASSIGN_MUL: Token = new_token(30);
    pub(crate) const ASSIGN_DIV: Token = new_token(31);
    pub(crate) const ASSIGN_MOD: Token = new_token(32);
    pub(crate) const ASSIGN_BITAND: Token = new_token(33);
    pub(crate) const ASSIGN_BITOR: Token = new_token(34);
    pub(crate) const ASSIGN_BITXOR: Token = new_token(35);
    pub(crate) const QUESTION: Token = new_token(36);
    pub(crate) const CALL: Token = new_token(37);
    pub(crate) const INDEX: Token = new_token(38);
    pub(crate) const IF: Token = new_token(39);
    pub(crate) const PUB: Token = new_token(40);
    pub(crate) const FN: Token = new_token(41);
}

pub(crate) mod nodes {
    use crate::input::Node;
    pub(crate) const START: Node = Node(0);
    pub(crate) const N1: Node = Node(1);
    pub(crate) const N2: Node = Node(2);
    pub(crate) const N3: Node = Node(3);
    pub(crate) const N4: Node = Node(4);
    pub(crate) const N5: Node = Node(5);
    pub(crate) const N6: Node = Node(6);
    pub(crate) const N7: Node = Node(7);
    pub(crate) const N8: Node = Node(8);
    pub(crate) const E: Node = Node(9);
    pub(crate) const FUNCTION: Node = Node(10);
    pub(crate) const ITEM: Node = Node(11);
    pub(crate) const ITEMS: Node = Node(12);
    pub(crate) const VISIBILITY: Node = Node(13);
}

pub(crate) mod expressions {
    use crate::{
        input::{
            Grammar, Node, ProdIdx,
            Symbol::{self, Node as N, Token as T},
            Token,
        },
        lr::LRTable,
    };

    pub(crate) mod symbols {
        pub(crate) use crate::test_common::{
            nodes::{E, START},
            tokens::*,
        };
    }

    /// Get the grammar:
    /// ```text
    /// E → E + E   [2, 3]
    /// E → E - E   [2, 3]
    /// E → E × E   [4, 5]
    /// E → - E     [_, 6]
    /// E → if E    [_, 1]
    /// ```
    pub(crate) fn get_grammar_with_precedence() -> (Grammar, [ProdIdx; 5]) {
        use symbols::*;

        let mut grammar = Grammar::new();
        let add_prod = grammar
            .add_production(E, vec![N(E), T(PLUS), N(E)])
            .unwrap();
        let sub_prod = grammar
            .add_production(E, vec![N(E), T(MINUS), N(E)])
            .unwrap();
        let mul_prod = grammar
            .add_production(E, vec![N(E), T(TIMES), N(E)])
            .unwrap();
        let neg_prod = grammar.add_production(E, vec![T(MINUS), N(E)]).unwrap();
        let if_prod = grammar.add_production(E, vec![T(IF), N(E)]).unwrap();

        let family = grammar.add_precedence_family();

        // Precedence is:
        // - `- E`
        // - `E × E` (left-associative)
        // - `E + E` and `E - E` (left-associative)
        // - `if E`
        grammar
            .get_production_mut(add_prod)
            .unwrap()
            .set_left_precedence(family, 2)
            .set_right_precedence(family, 3);
        grammar
            .get_production_mut(sub_prod)
            .unwrap()
            .set_left_precedence(family, 2)
            .set_right_precedence(family, 3);
        grammar
            .get_production_mut(mul_prod)
            .unwrap()
            .set_left_precedence(family, 4)
            .set_right_precedence(family, 5);
        grammar
            .get_production_mut(neg_prod)
            .unwrap()
            .set_right_precedence(family, 6);
        grammar
            .get_production_mut(if_prod)
            .unwrap()
            .set_right_precedence(family, 1);

        (grammar, [add_prod, sub_prod, mul_prod, neg_prod, if_prod])
    }

    #[derive(Clone, Copy)]
    pub(crate) struct DisplayTable<'a, Kind>(&'a LRTable<Kind>);

    impl<Kind: 'static> DisplayTable<'_, Kind> {
        fn token_str(token: Token) -> &'static str {
            match token {
                symbols::PLUS => "+",
                symbols::MINUS => "-",
                symbols::TIMES => "×",
                symbols::IF => "if",
                _ => unreachable!(),
            }
        }

        fn node_str(node: Node) -> &'static str {
            match node {
                symbols::START => "START",
                symbols::E => "E",
                _ => unreachable!(),
            }
        }

        fn symbol_str(symbol: Symbol) -> &'static str {
            match symbol {
                T(t) => Self::token_str(t),
                N(n) => Self::node_str(n),
            }
        }
    }

    impl<Kind: 'static> std::fmt::Display for DisplayTable<'_, Kind> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let (grammar, _) = get_grammar_with_precedence();

            f.write_str("Table {\n")?;
            for (state_idx, state) in self.0.states.iter().enumerate() {
                writeln!(f, "    State({state_idx}) {{")?;
                f.write_str("        items: [\n")?;
                for (item_idx, item) in state.items.iter().enumerate() {
                    writeln!(f, "            LRItem({item_idx}): {{")?;
                    writeln!(f, "                {}", Self::node_str(item.prod_idx.lhs))?;
                    let rhs = grammar.get_rhs(item.prod_idx).unwrap_or_default();
                    for (idx, s) in rhs.iter().enumerate() {
                        f.write_str(if item.index as usize == idx {
                            ""
                        } else {
                            " "
                        })?;
                        f.write_str(Self::symbol_str(*s))?;
                    }
                    if rhs.len() == item.index as usize {
                        f.write_str("")?;
                    }
                    f.write_str(" [")?;
                    if let Some(&Some(left)) = item.annotations.forbidden_left.get(0) {
                        write!(f, "{left}")?;
                    } else {
                        f.write_str("_")?;
                    }
                    f.write_str(", ")?;
                    if let Some(&Some(right)) = item.annotations.forbidden_right.get(0) {
                        write!(f, "{right}")?;
                    } else {
                        f.write_str("_")?;
                    }
                    f.write_str("]\n")?;
                    writeln!(
                        f,
                        "                directly_derives: {:?},",
                        item.directly_derives
                    )?;
                    writeln!(f, "                derives: {:?},", item.derives)?;
                    f.write_str("            },\n")?;
                }
                f.write_str("        ],\n")?;
                f.write_str("    },\n")?;
            }
            f.write_str("}")
        }
    }
}