use crate::debug_trace;
use crate::regex::Regex;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone)]
pub enum Symbol {
Nonterminal {
name: String,
binding: Option<String>,
},
Terminal {
regex: Regex,
binding: Option<String>,
},
}
impl Eq for Symbol {}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Symbol::Nonterminal {
name: a,
binding: ba,
},
Symbol::Nonterminal {
name: b,
binding: bb,
},
) => a == b && ba == bb,
(
Symbol::Terminal {
regex: a,
binding: ba,
..
},
Symbol::Terminal {
regex: b,
binding: bb,
..
},
) => a.equiv(b) && ba == bb,
_ => false,
}
}
}
impl Hash for Symbol {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Symbol::Nonterminal { name, binding } => {
0u8.hash(state);
name.hash(state);
binding.hash(state);
}
Symbol::Terminal { regex, binding } => {
1u8.hash(state);
regex.to_pattern().hash(state);
binding.hash(state);
}
}
}
}
impl Symbol {
pub fn new(value: String) -> Self {
debug_trace!("grammar", "Creating symbol from value: {}", value);
if value.len() >= 2
&& ((value.starts_with('\'') && value.ends_with('\''))
|| (value.starts_with('"') && value.ends_with('"')))
{
let literal = value[1..value.len() - 1].to_string();
Symbol::Terminal {
regex: Regex::literal(&literal),
binding: None,
}
} else if value.starts_with('/') && value.ends_with('/') && value.len() > 2 {
let pattern = value[1..value.len() - 1].to_string();
Symbol::Terminal {
regex: Regex::new(&pattern).expect("invalid regex literal"),
binding: None,
}
} else {
Symbol::Nonterminal {
name: value,
binding: None,
}
}
}
pub fn attach_binding(mut self, binding: String) -> Self {
match &mut self {
Symbol::Nonterminal { binding: slot, .. } | Symbol::Terminal { binding: slot, .. } => {
*slot = Some(binding);
}
}
self
}
pub fn binding(&self) -> Option<&String> {
match self {
Symbol::Nonterminal { binding, .. } | Symbol::Terminal { binding, .. } => {
binding.as_ref()
}
}
}
pub fn has_binding(&self) -> bool {
self.binding().is_some()
}
}