use std::collections::HashMap;
use crate::grammar::{
AstNodeId, AstNodeType, Grammar, GrammarRuleId, HasTokenType, Index, ReductionResult, TokenType,
};
use crate::lr::InternalToken;
pub mod generator;
pub use generator::{Ambiguity, AmbiguityKind};
pub type StateId = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Action {
Shift(StateId),
Reduce(GrammarRuleId),
Accept,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseError {
UnexpectedToken {
state: StateId,
token_type: TokenType,
position: usize,
},
UnexpectedEof {
state: StateId,
},
}
#[derive(Clone)]
pub struct Parser<T: HasTokenType, A> {
pub grammar: Grammar<T, A>,
pub goto_table: Vec<HashMap<AstNodeType, usize>>,
pub action_table: Vec<HashMap<InternalToken, Action>>,
}
impl<T: HasTokenType, A> Parser<T, A> {
pub fn parse(&self, tokens: &[T]) -> Result<(AstNodeId, Vec<A>), ParseError> {
let mut state_stack: Vec<StateId> = vec![0];
let mut value_stack: Vec<Index> = Vec::new();
let mut ast_nodes: Vec<A> = Vec::new();
let mut position = 0;
loop {
let current_state = *state_stack.last().expect("State stack should not be empty");
let lookahead = if position < tokens.len() {
InternalToken::User(tokens[position].token_type())
} else {
InternalToken::Eof
};
let action = self.action_table[current_state]
.get(&lookahead)
.ok_or_else(|| {
if lookahead == InternalToken::Eof {
ParseError::UnexpectedEof {
state: current_state,
}
} else if let InternalToken::User(token_type) = lookahead {
ParseError::UnexpectedToken {
state: current_state,
token_type,
position,
}
} else {
unreachable!("Special token should never appear during parsing")
}
})?;
match action {
Action::Shift(next_state) => {
value_stack.push(Index::Token(crate::grammar::TokenId(position)));
state_stack.push(*next_state);
position += 1;
}
Action::Reduce(rule_id) => {
let rule = self.grammar.get_rule(*rule_id);
let component_count = rule.components.len();
state_stack.truncate(state_stack.len() - component_count);
let popped_indices: Vec<Index> = value_stack
.drain(value_stack.len() - component_count..)
.collect();
let result_node_id = match (rule.reduction)(&popped_indices, tokens, &ast_nodes)
{
ReductionResult::NewNode(node) => {
let node_id = AstNodeId(ast_nodes.len());
ast_nodes.push(node);
node_id
}
ReductionResult::Forward(node_id) => node_id,
};
value_stack.push(Index::AstNode(result_node_id));
let current_state =
*state_stack.last().expect("State stack should not be empty");
let next_state = self.goto_table[current_state]
.get(&rule.result)
.expect("Goto table should have entry for reduction result");
state_stack.push(*next_state);
}
Action::Accept => {
let root_id = value_stack
.last()
.expect("Value stack should contain result")
.as_ast_node_id();
return Ok((root_id, ast_nodes));
}
}
}
}
}