#![doc(hidden)]
use diags::{Diags, SourceSpan};
use crate::ParameterValue;
use std::collections::HashMap;
pub struct ConstEntry {
pub value: Option<ParameterValue>,
pub used: bool,
pub decl_loc: Option<SourceSpan>,
}
pub struct SymbolTable {
entries: HashMap<String, ConstEntry>,
}
impl Default for SymbolTable {
fn default() -> Self {
Self::new()
}
}
impl SymbolTable {
pub fn new() -> Self {
SymbolTable {
entries: HashMap::new(),
}
}
pub fn define(&mut self, name: String, value: ParameterValue, loc: Option<SourceSpan>) {
self.entries.insert(
name,
ConstEntry {
value: Some(value),
used: false,
decl_loc: loc,
},
);
}
pub fn declare(&mut self, name: String, loc: SourceSpan) {
self.entries.entry(name).or_insert(ConstEntry {
value: None,
used: false,
decl_loc: Some(loc),
});
}
pub fn assign(
&mut self,
name: &str,
value: ParameterValue,
loc: &SourceSpan,
diags: &Diags,
) -> bool {
if let Some(entry) = self.entries.get_mut(name) {
if entry.value.is_some() {
let Some(decl_loc) = entry.decl_loc.as_ref() else {
return true;
};
let m = format!("Const '{}' cannot be assigned more than once.", name);
diags.err2(
"ERR_231",
&m,
loc.clone(),
decl_loc.clone(),
);
return false;
}
entry.value = Some(value);
true
} else {
let m = format!(
"Assignment to '{}' which was not pre-declared with 'const {};'.",
name, name
);
diags.err1("ERR_230", &m, loc.clone());
false
}
}
pub fn get(&self, name: &str) -> Option<&ParameterValue> {
self.entries.get(name)?.value.as_ref()
}
pub fn mark_used(&mut self, name: &str) {
self.entries
.get_mut(name)
.unwrap_or_else(|| panic!("mark_used: const '{}' is not in the symbol table", name))
.used = true;
}
pub fn contains_key(&self, name: &str) -> bool {
self.entries.contains_key(name)
}
pub fn get_value(&self, name: &str) -> Option<ParameterValue> {
self.entries.get(name)?.value.clone()
}
pub fn iter_defined_with_used(&self) -> impl Iterator<Item = (&str, &ParameterValue, bool)> {
self.entries
.iter()
.filter_map(|(k, e)| e.value.as_ref().map(|v| (k.as_str(), v, e.used)))
}
pub fn warn_unused(&self, diags: &Diags) {
for (name, entry) in &self.entries {
if entry.value.is_some() && !entry.used {
let m = format!("Const '{}' is defined but never used.", name);
diags.warn_opt("ERR_229", &m, entry.decl_loc.clone());
}
}
}
}