use crate::parser::Symbol;
use crate::{TokenId, VarId};
#[derive(Clone, Debug)]
pub struct FixedSymTable {
t: Vec<(String, Option<String>)>, nt: Vec<String>, }
impl FixedSymTable {
pub fn new(t: Vec<(String, Option<String>)>, nt: Vec<String>) -> Self {
FixedSymTable { t, nt }
}
pub fn get_terminals(&self) -> impl Iterator<Item = &(String, Option<String>)> {
self.t.iter()
}
pub fn get_num_t(&self) -> usize {
self.t.len()
}
pub fn get_nonterminals(&self) -> impl Iterator<Item = &String> {
self.nt.iter()
}
pub fn get_num_nt(&self) -> usize {
self.nt.len()
}
#[cfg(test)]
pub fn dump(&self, title: &str) {
if !title.is_empty() {
println!("{title}");
}
println!(
"- nonterminals:\n{}",
self.get_nonterminals().enumerate().map(|(v, s)| format!(" - NT[{v}]: {s}")).collect::<Vec<_>>().join("\n"));
println!(
"- terminals:\n{}",
self.get_terminals().enumerate()
.map(|(t, (n, v_maybe))| format!(" - T[{t}]: {n}{}", if let Some(v) = v_maybe { format!(" = {v:?}") } else { String::new() }))
.collect::<Vec<_>>().join("\n"));
}
}
pub trait SymInfoTable {
fn is_token_data(&self, token: TokenId) -> bool;
fn is_symbol_t_data(&self, symbol: &Symbol) -> bool;
fn is_symbol_t_fixed(&self, symbol: &Symbol) -> bool;
fn get_t_str(&self, token: TokenId) -> String;
fn get_t_name(&self, token: TokenId) -> String;
fn get_nt_name(&self, var: VarId) -> String;
fn get_name(&self, symbol: &Symbol) -> String;
fn get_str(&self, symbol: &Symbol) -> String;
fn get_name_quote(&self, symbol: &Symbol) -> String;
}
impl SymInfoTable for FixedSymTable {
fn is_token_data(&self, token: TokenId) -> bool {
self.t[token as usize].1.is_none()
}
fn is_symbol_t_data(&self, symbol: &Symbol) -> bool {
if let Symbol::T(token) = symbol {
self.t.get(*token as usize).map(|t| t.1.is_none()).unwrap_or(false)
} else {
false
}
}
fn is_symbol_t_fixed(&self, symbol: &Symbol) -> bool {
if let Symbol::T(token) = symbol {
self.t.get(*token as usize).map(|t| t.1.is_some()).unwrap_or(false)
} else {
false
}
}
fn get_t_str(&self, token: TokenId) -> String {
match token {
_ if (token as usize) < self.t.len() => {
let (name, literal) = &self.t[token as usize];
literal.as_ref().unwrap_or(name).clone()
}
TokenId::MAX => "<bad character>".to_string(),
_ => format!("T({token}?)")
}
}
fn get_t_name(&self, token: TokenId) -> String {
if token as usize >= self.t.len() {
format!("T({token}?)")
} else {
self.t[token as usize].0.clone()
}
}
fn get_nt_name(&self, var: VarId) -> String {
if var as usize >= self.nt.len() { return format!("NT({var}?)") }
self.nt[var as usize].clone()
}
fn get_name(&self, symbol: &Symbol) -> String {
match symbol {
Symbol::Empty | Symbol::End => symbol.to_string(),
Symbol::T(token) => self.get_t_name(*token),
Symbol::NT(var) => self.get_nt_name(*var),
}
}
fn get_str(&self, symbol: &Symbol) -> String {
match symbol {
Symbol::Empty | Symbol::End => symbol.to_string(),
Symbol::T(token) => self.get_t_str(*token),
Symbol::NT(var) => self.get_nt_name(*var),
}
}
fn get_name_quote(&self, symbol: &Symbol) -> String {
match symbol {
Symbol::Empty | Symbol::End => symbol.to_string(),
Symbol::T(token) => if self.is_symbol_t_fixed(symbol) { format!("{:?}", self.get_t_str(*token)) } else { self.get_t_str(*token) },
Symbol::NT(var) => self.get_nt_name(*var),
}
}
}