use std::collections::hash_map::{Iter, Keys};
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{LineWriter, Write};
use std::path::Path;
use itertools::Itertools;
use failure::Fallible;
use crate::parsers::text_symt::parsed_text_symt::ParsedTextSymt;
use crate::{Label, Symbol, EPS_SYMBOL};
#[derive(PartialEq, Debug, Clone, Default)]
pub struct SymbolTable {
label_to_symbol: HashMap<Label, Symbol>,
symbol_to_label: HashMap<Symbol, Label>,
num_symbols: usize,
}
macro_rules! write_symt_text {
($symt:expr, $f:expr) => {
for (label, symbol) in $symt.iter().sorted_by_key(|k| k.0) {
writeln!($f, "{}\t{}", symbol, label)?;
}
};
}
impl SymbolTable {
pub fn new() -> Self {
let mut symt = SymbolTable {
label_to_symbol: HashMap::new(),
symbol_to_label: HashMap::new(),
num_symbols: 0,
};
symt.add_symbol(EPS_SYMBOL.to_string());
symt
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn add_symbol<S: Into<String>>(&mut self, sym: S) -> Label {
let label = self.num_symbols;
let sym = sym.into();
self.symbol_to_label.entry(sym.clone()).or_insert(label);
self.label_to_symbol.entry(label).or_insert(sym);
self.num_symbols += 1;
label
}
pub fn len(&self) -> usize {
self.num_symbols
}
pub fn get_label<S: Into<String>>(&self, sym: S) -> Option<Label> {
self.symbol_to_label.get(&sym.into()).cloned()
}
pub fn get_symbol(&self, label: Label) -> Option<&str> {
self.label_to_symbol.get(&label).map(|v| v.as_str())
}
pub fn contains_symbol<S: Into<String>>(&self, sym: S) -> bool {
self.get_label(sym.into()).is_some()
}
pub fn contains_label(&self, label: Label) -> bool {
self.get_symbol(label).is_some()
}
pub fn reserve(&mut self, additional: usize) {
self.label_to_symbol.reserve(additional);
self.symbol_to_label.reserve(additional);
}
pub fn labels(&self) -> Keys<Label, Symbol> {
self.label_to_symbol.keys()
}
pub fn symbols(&self) -> Keys<Symbol, Label> {
self.symbol_to_label.keys()
}
pub fn iter(&self) -> Iter<Label, Symbol> {
self.label_to_symbol.iter()
}
pub fn add_table(&mut self, other: &SymbolTable) {
for symbol in other.symbols() {
self.add_symbol(symbol.as_str());
}
}
fn from_parsed_symt_text(parsed_symt_text: ParsedTextSymt) -> Fallible<Self> {
let num_symbols = parsed_symt_text.pairs.len();
let mut label_to_symbol: HashMap<Label, Symbol> = HashMap::new();
let mut symbol_to_label: HashMap<Symbol, Label> = HashMap::new();
for (symbol, label) in parsed_symt_text.pairs.into_iter() {
label_to_symbol.insert(label, symbol.clone());
symbol_to_label.insert(symbol, label);
}
Ok(SymbolTable {
num_symbols,
symbol_to_label,
label_to_symbol,
})
}
pub fn from_text_string(symt_string: &str) -> Fallible<Self> {
let parsed_symt = ParsedTextSymt::from_string(symt_string)?;
Self::from_parsed_symt_text(parsed_symt)
}
pub fn read_text<P: AsRef<Path>>(&self, path_text_symt: P) -> Fallible<Self> {
let parsed_symt = ParsedTextSymt::from_path(path_text_symt)?;
Self::from_parsed_symt_text(parsed_symt)
}
pub fn write_text<P: AsRef<Path>>(&self, path_output: P) -> Fallible<()> {
let buffer = File::create(path_output.as_ref())?;
let mut line_writer = LineWriter::new(buffer);
write_symt_text!(self, line_writer);
Ok(())
}
pub fn text(&self) -> Fallible<String> {
let buffer = Vec::<u8>::new();
let mut line_writer = LineWriter::new(buffer);
write_symt_text!(self, line_writer);
Ok(String::from_utf8(line_writer.into_inner()?)?)
}
}
impl fmt::Display for SymbolTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write_symt_text!(self, f);
Ok(())
}
}
#[macro_export]
macro_rules! symt {
( $( $x:expr ),* ) => {
{
let mut temp_vec = SymbolTable::new();
$(
temp_vec.add_symbol($x.to_string());
)*
temp_vec
}
};
}