use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use crate::DisplaySystem;
use crate::error::Error;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd)]
pub enum TokenKind {
Terminal,
Production
}
impl TokenKind {
pub fn is_terminal(&self) -> bool {
matches!(self, TokenKind::Terminal)
}
pub fn is_production(&self) -> bool {
matches!(self, TokenKind::Production)
}
}
impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TokenKind::Terminal => f.write_str("Terminal"),
TokenKind::Production => f.write_str("Production")
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd)]
pub struct Token {
kind: TokenKind,
code: u32
}
impl Token {
pub fn new(kind: TokenKind, code: u32) -> Self {
Token {
kind, code
}
}
pub fn kind(&self) -> TokenKind {
self.kind
}
pub fn code(&self) -> u32 {
self.code
}
#[inline]
pub fn is_terminal(&self) -> bool {
self.kind.is_terminal()
}
#[inline]
pub fn is_production(&self) -> bool {
self.kind.is_production()
}
}
impl Display for Token {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}[{}]", self.kind, self.code)
}
}
pub trait TokenStore {
fn add_token(&self, name: &str, kind: TokenKind) -> crate::Result<Token>;
fn get_token(&self, name: &str) -> Option<Token>;
}
impl TokenStore for RefCell<HashMap<String, Token>> {
fn add_token(&self, name: &str, kind: TokenKind) -> crate::Result<Token> {
let mut map = self.borrow_mut();
if let Some(value) = map.get(name) {
return Ok(*value);
}
let max = map.values().map(|t| t.code).max().unwrap_or(0);
let token = Token::new(kind, max + 1);
map.insert(name.to_string(), token);
Ok(token)
}
fn get_token(&self, name: &str) -> Option<Token> {
let map = self.borrow();
map.get(name).cloned()
}
}
impl DisplaySystem for Token {
fn format(&self, names: &HashMap<Token, String>) -> crate::Result<String> {
let name = names.get(self)
.ok_or_else(|| Error::general(format!("No name supplied for token {self:?}")))?;
Ok(name.clone())
}
}