use regex_automata::dfa::Automaton;
use regex_automata::dfa::dense::DFA;
use crate::Hash;
use crate::Hasher;
use crate::GLLImplementationError;
use crate::ImplementationResult;
use crate::GLLError;
use crate::from_utf8;
use crate::GLLBlockLabel;
use crate::Terminal;
use crate::GLLResult;
use crate::HashSet;
use crate::Rc;
use crate::Value;
use std::fmt::Debug;
use crate::GLLState;
pub trait Label<'a>: Debug {
fn is_eps(&self) -> bool {
false
}
fn first_set(&self, state: &GLLState<'a>) -> ImplementationResult<'a, Vec<(Vec<GLLBlockLabel<'a>>, Option<Terminal<'a>>)>>;
fn code(&self, state: &mut GLLState<'a>) -> GLLResult<'a, ()>;
fn first(&self, state: &mut GLLState<'a>) -> GLLResult<'a, bool> {
self._first(state, &mut HashSet::default())
}
fn _first(&self, state: &mut GLLState<'a>, seen: &mut HashSet<Rc<str>>) -> GLLResult<'a, bool> {
let fst = self.first_set(state)?;
for (alt, fin) in fst {
let mut check_fin = true;
for sub in alt {
let uuid = sub.uuid();
if seen.contains(uuid) {
continue
}
seen.insert(uuid.into());
if sub._first(state, seen)? {
return Ok(true);
} else if !sub.is_nullable(state)? {
check_fin = false;
break;
}
}
if check_fin {
if let Some(last) = fin {
return Ok(last.is_empty() || state.has_next(last))
}
}
}
Ok(false)
}
fn is_terminal(&self) -> bool {
false
}
fn is_nullable(&self, state: &GLLState<'a>) -> ImplementationResult<'a, bool> {
self._is_nullable(state, &mut HashSet::default())
}
fn _is_nullable(&self, state: &GLLState<'a>, seen: &mut HashSet<Rc<str>>) -> ImplementationResult<'a, bool> {
if self.is_eps() {
Ok(true)
} else {
let str_repr = self.uuid();
if !seen.contains(str_repr) {
seen.insert(str_repr.into());
let fst = self.first_set(state)?;
for (alt, _) in fst {
if let Some(sub) = alt.into_iter().next() {
if sub._is_nullable(state, seen)? {
return Ok(true)
}
}
}
}
Ok(false)
}
}
fn _weight(&self, state: &GLLState<'a>) -> Option<ImplementationResult<'a, Value<'a>>>;
fn weight(&self, state: &GLLState<'a>) -> ImplementationResult<'a, Value<'a>> {
self._weight(state).map_or_else(|| Ok(1.into()), |weight| weight)
}
fn to_string(&self) -> &str;
fn str_parts(&self) -> Vec<&str>;
fn uuid(&self) -> &str;
fn attr_rep_map(&self) -> (Vec<&str>, Vec<&str>);
}
impl<'a> Label<'a> for Terminal<'a> {
fn is_eps(&self) -> bool {
self.is_empty()
}
fn first_set(&self, _: &GLLState<'a>) -> ImplementationResult<'a, Vec<(Vec<GLLBlockLabel<'a>>, Option<Terminal<'a>>)>> {
Ok(vec![(Vec::new(), Some(*self))])
}
fn _first(&self, state: &mut GLLState<'a>, _: &mut HashSet<Rc<str>>) -> GLLResult<'a, bool> {
Ok(self.is_eps() || state.has_next(self))
}
fn code(&self, _: &mut GLLState<'a>) -> GLLResult<'a, ()> {
Err(GLLError::ImplementationError(GLLImplementationError::Fatal("Attempted running the `code` method on a terminal.")))
}
fn is_terminal(&self) -> bool {
true
}
fn to_string(&self) -> &str {
#[allow(clippy::expect_used)]
from_utf8(self).expect("Terminal was non-utf8")
}
fn _is_nullable(&self, _: &GLLState<'a>, _: &mut HashSet<Rc<str>>) -> ImplementationResult<'a, bool> {
Ok(self.is_eps())
}
fn uuid(&self) -> &str {
self.to_string()
}
fn str_parts(&self) -> Vec<&str> {
vec![self.to_string()]
}
fn attr_rep_map(&self) -> (Vec<&str>, Vec<&str>) {
(Vec::new(), Vec::new())
}
fn _weight(&self, _state: &GLLState<'a>) -> Option<ImplementationResult<'a, Value<'a>>> {
Some(Err(GLLImplementationError::Fatal("Attempted running the `_weight` method on a terminal.")))
}
}
#[derive(Debug)]
pub struct RegexTerminal<'a> {
pattern: &'a str,
pub automaton: DFA<&'a [u32]>
}
impl<'a> RegexTerminal<'a> {
#[must_use]
pub const fn new(pattern: &'a str, automaton: DFA<&'a [u32]>) -> Self {
Self { pattern, automaton }
}
}
impl<'a> Label<'a> for RegexTerminal<'a> {
fn first_set(&self, _: &GLLState<'a>) -> ImplementationResult<'a, Vec<(Vec<GLLBlockLabel<'a>>, Option<Terminal<'a>>)>> {
Err(GLLImplementationError::Fatal("Attempted running the `first_set` method on a regex."))
}
fn code(&self, _: &mut GLLState<'a>) -> GLLResult<'a, ()> {
Err(GLLError::ImplementationError(GLLImplementationError::Fatal("Attempted running the `code` method on a regex.")))
}
fn _weight(&self, _: &GLLState<'a>) -> Option<ImplementationResult<'a, Value<'a>>> {
Some(Err(GLLImplementationError::Fatal("Attempted running the `weight` method on a regex.")))
}
fn to_string(&self) -> &str {
self.pattern
}
fn str_parts(&self) -> Vec<&str> {
vec![self.to_string()]
}
fn uuid(&self) -> &str {
self.to_string()
}
fn attr_rep_map(&self) -> (Vec<&str>, Vec<&str>) {
(Vec::new(), Vec::new())
}
fn is_eps(&self) -> bool {
self.automaton.pattern_len() == 0
}
fn _first(&self, state: &mut GLLState<'a>, _: &mut HashSet<Rc<str>>) -> GLLResult<'a, bool> {
state.has_regex(self.pattern)
}
fn is_terminal(&self) -> bool {
true
}
fn _is_nullable(&self, _: &GLLState<'a>, _: &mut HashSet<Rc<str>>) -> ImplementationResult<'a, bool> {
Ok(self.automaton.has_empty())
}
}
impl<'a> Hash for dyn Label<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.uuid().hash(state);
}
}
impl<'a> PartialEq for dyn Label<'a> {
fn eq(&self, other: &Self) -> bool {
self.uuid() == other.uuid()
}
}
impl<'a> Eq for dyn Label<'a>{}