use crate::value::Value;
use crate::ImplementationResult;
use crate::GLLImplementationError;
use crate::ValueError;
use crate::InnerValueError;
use crate::GLLState;
use crate::GLLBlockLabel;
use crate::Rc;
use crate::Ident;
use crate::Hash;
use crate::Hasher;
#[derive(Debug)]
pub struct GrammarSlot<'a> {
pub label: GLLBlockLabel<'a>,
pub rule: Rc<Vec<Ident>>,
pub dot: usize,
pub pos: usize,
pub uuid: &'a str
}
impl<'a> Eq for GrammarSlot<'a> {}
impl<'a> PartialEq for GrammarSlot<'a> {
fn eq(&self, other: &Self) -> bool {
if self.is_complete() && other.is_complete() { self.label.to_string() == other.label.to_string()
} else {
Self::strict_comp(self, other)
}
}
}
impl<'a> Hash for GrammarSlot<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
if self.is_complete() {
self.label.to_string().hash(state);
} else {
Self::strict_hash(self, state);
}
}
}
impl<'a> GrammarSlot<'a> {
pub fn new(label: GLLBlockLabel<'a>, rule: Rc<Vec<Ident>>, dot: usize, pos: usize, uuid: &'a str) -> Self {
Self {label, rule, dot, pos, uuid}
}
#[must_use]
pub fn is_eps(&self) -> bool {
self.is_empty()
}
#[must_use]
pub fn is_last(&self, state: &GLLState<'a>) -> bool {
self.dot == self.len() || (self.dot == self.len()-1 && self.curr_block(state).is_eps())
}
#[must_use]
pub fn len(&self) -> usize {
self.rule.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn to_string(&self, state: &GLLState<'a>, math_mode: bool) -> String {
let mut res = String::new();
if math_mode {
if self.label.is_eps() {
res.push_str("$\\epsilon$ ");
} else {
res.push_str(&v_latexescape::escape(self.label.to_string()).to_string().replace('·', "$\\cdot$"));
}
} else {
res.push_str(self.label.to_string());
}
if self.dot == self.len() + 1 {
return res
}
if math_mode {
res.push_str(" $\\rightarrow$ ");
} else {
res.push_str(" -> ");
}
for (i, r) in self.rule.iter().enumerate() {
let label = state.get_label(r);
if i == self.dot {
let parts = label.str_parts();
for (j, s) in parts.iter().enumerate() {
if j == self.pos {
if math_mode {
res.push_str("$\\bigcdot$");
} else {
res.push('•');
}
res.push(' ');
}
if math_mode {
res.push_str(&v_latexescape::escape(s).to_string().replace('·', "$\\cdot$"));
} else {
res.push_str(s);
}
res.push(' ');
}
} else {
if math_mode {
if label.is_eps() {
res.push_str("$\\epsilon$ ");
} else {
res.push_str(&v_latexescape::escape(label.to_string()).to_string().replace('·', "$\\cdot$"));
}
} else {
res.push_str(label.to_string());
}
res.push(' ');
}
}
if self.is_last(state) {
if math_mode {
res.push_str("$\\bigcdot$");
} else {
res.push('•');
}
};
res.trim_end().to_owned()
}
fn curr_block(&self, state: &GLLState<'a>) -> GLLBlockLabel<'a> {
state.get_label(&self.rule[self.dot])
}
pub fn weight(&self, state: &GLLState<'a>) -> ImplementationResult<'a, Value<'a>> {
self.curr_block(state).weight(state)
}
pub(crate) fn is_complete(&self) -> bool {
self.dot == self.len()+1
}
pub fn partial_cmp(&self, other: &Self, state: &GLLState<'a>) -> ImplementationResult<'a, std::cmp::Ordering> {
let left_weight = self.weight(state)?;
let right_weight = other.weight(state)?;
left_weight.partial_cmp(&right_weight).map_or_else(|| Err(GLLImplementationError::ValueError(ValueError::ValueError(InnerValueError::ComparisonError(left_weight, right_weight)))), Ok)
}
pub(crate) fn strict_comp(orig: &Self, other: &Self) -> bool {
orig.uuid == other.uuid && orig.dot == other.dot && orig.pos == other.pos
}
pub(crate) fn strict_hash<H: Hasher>(cand: &Self, state: &mut H) {
cand.uuid.hash(state);
cand.dot.hash(state);
cand.pos.hash(state);
}
}