lr_parser 0.1.2

macros for ruly2
Documentation
use std::collections::{HashMap, HashSet};

use crate::item::Item;

mod first_sets;
mod follow_sets;
mod lr0;
mod lr1;
mod slr;

#[derive(Debug)]
pub enum Action {
    Shift(usize),
    Reduce(Item),
    Accept,
}

pub fn create_parser(
    algorithm: &str,
    start_symbol: &str,
    terminal_symbols: &HashSet<String>,
    nonterminal_symbols: &HashSet<String>,
    map_lhs2items: &HashMap<String, HashSet<Item>>,
) -> String {
    let result = match &*algorithm {
        "LR0" => lr0::parsing_table::compute_lr0_parsing_table(
            start_symbol,
            terminal_symbols,
            map_lhs2items,
        ),

        "SLR" => slr::parsing_table::compute_slr_parsing_table(
            start_symbol,
            nonterminal_symbols,
            map_lhs2items,
        ),

        "LR1" => lr1::parsing_table::compute_lr1_parsing_table(
            start_symbol,
            nonterminal_symbols,
            map_lhs2items,
        ),

        _ => panic!(),
    };

    match result {
        Ok(parsing_table) => {
            let mut ret = String::new();

            ret.push_str(&enum_status(start_symbol));
            ret.push_str(&struct_automaton());
            ret.push_str(&impl_automaton(start_symbol, &parsing_table));
            ret.push_str(&impl_yacc(start_symbol));
            ret.push_str(&enum_tree(terminal_symbols, nonterminal_symbols));
            ret.push_str(&impl_tree(terminal_symbols));

            ret
        }

        Err(error_message) => {
            format!(
                "
impl Yacc {{
    pub fn parse(v: &Vec<Token>) -> Result<{}, String> {{
        Err(\"{}\".to_string())
    }}
}}",
                start_symbol, error_message
            )
        }
    }
}

fn enum_status(start_symbol: &str) -> String {
    format!(
        "
enum Status {{
    Finished({}),
    Running,
}}",
        start_symbol
    )
}

fn struct_automaton() -> String {
    "
struct Automaton {
    input: std::collections::VecDeque<Tree>,
    state_stack: Vec<usize>,
    symbol_stack: Vec<Tree>,
}"
    .to_string()
}

fn impl_automaton(
    start_symbol: &str,
    parsing_table: &HashMap<usize, HashMap<Option<String>, Action>>,
) -> String {
    let mut ret = String::new();

    ret.push_str(
        "
impl Automaton {",
    );

    ret.push_str(&fn_new());

    ret.push_str(&fn_step(parsing_table, start_symbol));

    ret.push_str(&fn_run(start_symbol));

    ret.push_str(
        "
}",
    );

    ret
}

fn fn_new() -> String {
    "
    fn new(input: Vec<Tree>) -> Self {
        let mut input: std::collections::VecDeque<_> = input.into_iter().collect();
        input.push_back(Tree::F_(()));
        Automaton {
            input,
            state_stack: vec![0],
            symbol_stack: vec![],
        }
    }"
    .to_string()
}

fn shift(from: usize, x: &str, to: usize) -> String {
    format!(
        "
            (Some({}), Some(Tree::{}(_))) => {{
                self.state_stack.push({});
                self.symbol_stack.push(self.input.pop_front().unwrap());
                return Ok(Status::Running);
            }}",
        from, x, to
    )
}

fn reduce(from: usize, x: &str, item: &Item) -> String {
    let field_num = item.get_rhs().len();

    let s1 = item
        .get_rhs()
        .iter()
        .enumerate()
        .rev()
        .fold("".to_string(), |mut s, (i, symbol)| {
            s.push_str(&format!("Some(Tree::{}(t{})), ", symbol, i));
            s
        });
    let s2 = (0..field_num).fold("".to_string(), |mut s, i| {
        s.push_str(&format!("Box::new(t{}), ", i));
        s
    });

    format!(
        "
            (Some({}), Some(Tree::{}(_))) => {{
                if let ({}) = ({}) {{
                    if let ({}) = ({}) {{
                        self.input.push_front(Tree::{}({}::{}({})));
                        return Ok(Status::Running);
                    }}
                }}
            }}",
        from,
        x,
        "Some(_), ".repeat(item.get_rhs().len()),
        "self.state_stack.pop(), ".repeat(item.get_rhs().len()),
        s1,
        "self.symbol_stack.pop(), ".repeat(item.get_rhs().len()),
        item.get_lhs(),
        item.get_lhs(),
        item.get_rule_name(),
        s2
    )
}

fn accept(from: usize, start_symbol: &str) -> String {
    format!(
        "
            (Some({}), None) => {{
                if let Some(Tree::F_(_)) = self.symbol_stack.pop() {{
                    if let Some(Tree::{}(x)) = self.symbol_stack.pop() {{
                        return Ok(Status::Finished(x));
                    }}
                }}
            }}",
        from, start_symbol
    )
}

fn fn_step(
    parsing_table: &HashMap<usize, HashMap<Option<String>, Action>>,
    start_symbol: &str,
) -> String {
    let mut ret = String::new();

    ret.push_str(
        "
    fn step(&mut self) -> Result<Status, String> {
        match (self.state_stack.last(), self.input.front()) {",
    );

    for (from, map) in parsing_table {
        for pair in map {
            let s = match pair {
                (Some(x), Action::Shift(to)) => shift(*from, x, *to),
                (Some(x), Action::Reduce(item)) => reduce(*from, x, item),
                (None, Action::Accept) => accept(*from, start_symbol),
                _ => panic!(),
            };

            ret.push_str(&s);
        }
    }

    ret.push_str(
        "

            _ => {}
        }

        Err(\"ParseError!\".to_string())
    }",
    );

    ret
}

fn fn_run(start_symbol: &str) -> String {
    format!(
        "
    fn run(&mut self) -> Result<{}, String> {{
        loop {{
            if let Status::Finished(t) = self.step()? {{
                return Ok(t);
            }}
        }}
    }}",
        start_symbol
    )
}

fn impl_yacc(start_symbol: &str) -> String {
    format!(
        "
impl Yacc {{
    pub fn parse(v: &Vec<Token>) -> Result<{}, String> {{
        let v: Vec<_> = v.iter().map(|x| Tree::from(x)).collect();
        let mut automaton = Automaton::new(v);

        automaton.run()
    }}
}}",
        start_symbol
    )
}

fn enum_tree(terminal_symbols: &HashSet<String>, nonterminal_symbols: &HashSet<String>) -> String {
    let mut ret = String::new();

    ret.push_str(
        "
#[derive(Debug)]
enum Tree {",
    );

    for symbol in terminal_symbols {
        if symbol == "F_" {
            continue;
        }

        ret.push_str(&format!(
            "
    {}({}),",
            symbol, symbol
        ));
    }

    for symbol in nonterminal_symbols {
        if symbol == "S_" {
            continue;
        }

        ret.push_str(&format!(
            "
    {}({}),",
            symbol, symbol
        ));
    }

    ret.push_str(
        "
    F_(()),
}",
    );

    ret
}

fn impl_tree(terminal_symbols: &HashSet<String>) -> String {
    let mut ret = String::new();

    ret.push_str(
        "
impl Tree {
    fn from(t: &Token) -> Self {
        match t {",
    );

    for symbol in terminal_symbols {
        if symbol == "F_" {
            continue;
        }

        ret.push_str(&format!(
            "
            Token::{}(x) => Tree::{}(x.clone()),",
            symbol, symbol
        ));
    }

    ret.push_str(
        "
        }
    }
}",
    );

    ret
}