use super::{Decorate, SymbolAttribute};
use crate::analysis::k_tuple::TerminalMappings;
use crate::parser::parol_grammar::{Factor, LookaheadExpression, UserDefinedTypeName};
use crate::parser::to_grammar_config::try_from_factor;
use anyhow::{Result, anyhow};
use parol_runtime::parser::ScannerIndex;
use std::convert::TryFrom;
use std::fmt::{Debug, Display, Error, Formatter, Write};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum TerminalKind {
Legacy,
Regex,
Raw,
}
impl TerminalKind {
fn escape_raw_terminal(term: &str) -> String {
let mut escaped = String::with_capacity(term.len());
let mut chars = term.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\\' {
let mut lookahead = chars.clone();
if matches!(lookahead.next(), Some('u')) && matches!(lookahead.next(), Some('{')) {
let mut hex_digits = String::new();
let mut is_valid_escape = false;
for c in lookahead.by_ref() {
if c == '}' {
is_valid_escape = !hex_digits.is_empty();
break;
}
if c.is_ascii_hexdigit() {
hex_digits.push(c);
} else {
break;
}
}
if is_valid_escape {
escaped.push_str("\\u{");
escaped.push_str(&hex_digits);
escaped.push('}');
chars = lookahead;
continue;
}
}
}
escaped.push_str(regex::escape(&ch.to_string()).as_str());
}
escaped
}
pub fn delimiter(&self) -> char {
match self {
TerminalKind::Legacy => '"',
TerminalKind::Regex => '/',
TerminalKind::Raw => '\'',
}
}
pub fn behaves_like(&self, other: TerminalKind) -> bool {
match self {
TerminalKind::Legacy | TerminalKind::Regex => match other {
TerminalKind::Legacy | TerminalKind::Regex => true,
TerminalKind::Raw => false,
},
TerminalKind::Raw => match other {
TerminalKind::Legacy | TerminalKind::Regex => false,
TerminalKind::Raw => true,
},
}
}
pub fn expands_like(
this_term: &str,
this_kind: TerminalKind,
other_term: &str,
other_kind: TerminalKind,
) -> bool {
this_kind.expand(this_term) == other_kind.expand(other_term)
}
pub fn expand(&self, term: &str) -> String {
match self {
crate::TerminalKind::Legacy | crate::TerminalKind::Regex => term.to_string(),
crate::TerminalKind::Raw => Self::escape_raw_terminal(term),
}
}
}
#[cfg(test)]
mod tests {
use super::TerminalKind;
#[test]
fn raw_term_expansion_preserves_unicode_escape_sequence() {
assert_eq!(TerminalKind::Raw.expand(r"\u{0027}"), r"\u{0027}");
}
#[test]
fn raw_term_expansion_keeps_escaping_meta_characters() {
assert_eq!(TerminalKind::Raw.expand("{"), r"\{");
assert_eq!(TerminalKind::Raw.expand(r"a+b"), r"a\+b");
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Terminal {
Trm(
String,
TerminalKind,
Vec<usize>,
SymbolAttribute,
Option<UserDefinedTypeName>,
Option<String>,
Option<LookaheadExpression>,
),
Eps,
End,
}
impl Terminal {
pub fn t(t: &str, s: Vec<usize>, a: SymbolAttribute) -> Self {
Self::Trm(t.to_owned(), TerminalKind::Legacy, s, a, None, None, None)
}
pub fn is_trm(&self) -> bool {
matches!(self, Self::Trm(..))
}
pub fn is_eps(&self) -> bool {
matches!(self, Self::Eps)
}
pub fn is_end(&self) -> bool {
matches!(self, Self::End)
}
pub fn create(s: &Symbol) -> Self {
match s {
Symbol::T(Terminal::Trm(t, k, s, a, u, m, l)) => Terminal::Trm(
t.to_string(),
*k,
s.to_vec(),
*a,
u.clone(),
m.clone(),
l.clone(),
),
Symbol::T(Terminal::End) => Terminal::End,
_ => panic!("Unexpected symbol type: {s:?}"),
}
}
pub fn add_scanner(&mut self, sc: usize) {
match self {
Terminal::Trm(_, _, s, _, _, _, _) => {
if !s.contains(&sc) {
s.push(sc);
s.sort_unstable();
}
}
_ => panic!("Unexpected symbol type: {self:?}"),
}
}
pub fn format<R, S>(&self, scanner_state_resolver: &R, user_type_resolver: &S) -> Result<String>
where
R: Fn(&[usize]) -> String,
S: Fn(&str) -> Option<String>,
{
match self {
Self::Trm(t, k, s, a, u, m, l) => {
let mut d = String::new();
let delimiter = k.delimiter();
a.decorate(&mut d, &format!("{delimiter}{t}{delimiter}"))
.map_err(|e| anyhow!("Decorate error!: {}", e))?;
if let Some(la) = l {
write!(d, " {}", la.to_par()).map_err(|e| anyhow!(e))?;
}
if let Some(member) = m {
if l.is_some() {
write!(d, " ").map_err(|e| anyhow!(e))?;
}
write!(d, "@{member}").map_err(|e| anyhow!(e))?;
}
if let Some(user_type) = u {
let user_type =
if let Some(alias) = user_type_resolver(user_type.to_string().as_str()) {
alias
} else {
user_type.to_string()
};
if user_type != "%t_type" {
write!(d, " : {user_type}").map_err(|e| anyhow!(e))?;
}
}
if *s == vec![0] {
Ok(d)
} else {
Ok(format!("<{}>{}", scanner_state_resolver(s), d))
}
}
Self::Eps => Ok("\u{03B5}".to_string()), Self::End => Ok("$".to_string()),
}
}
}
impl Display for Terminal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> {
match self {
Self::Trm(t, k, ..) => {
let delimiter = k.delimiter();
write!(f, "{delimiter}{t}{delimiter}")
}
Self::Eps => write!(f, "\u{03B5}"), Self::End => write!(f, "$"),
}
}
}
impl TerminalMappings<Terminal> for Terminal {
#[inline]
fn eps() -> Terminal {
Self::Eps
}
#[inline]
fn end() -> Terminal {
Self::End
}
#[inline]
fn is_eps(&self) -> bool {
self.is_eps()
}
#[inline]
fn is_end(&self) -> bool {
self.is_end()
}
#[inline]
fn is_inv(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Symbol {
N(
String,
SymbolAttribute,
Option<UserDefinedTypeName>,
Option<String>,
),
T(Terminal),
#[deprecated(
since = "4.0.0",
note = "Scanner switching directives have been removed from the grammar syntax."
)]
S(ScannerIndex),
#[deprecated(
since = "4.0.0",
note = "Scanner switching directives have been removed from the grammar syntax."
)]
Push(ScannerIndex),
#[deprecated(
since = "4.0.0",
note = "Scanner switching directives have been removed from the grammar syntax."
)]
Pop,
}
impl Symbol {
pub fn n(n: &str) -> Self {
Self::N(n.to_owned(), SymbolAttribute::default(), None, None)
}
pub fn e() -> Self {
Self::T(Terminal::End)
}
#[deprecated(
since = "4.0.0",
note = "Scanner switching directives have been removed from the grammar syntax."
)]
pub fn s(_: usize) -> Self {
unimplemented!("Scanner switching directives have been removed from the grammar syntax.")
}
pub fn is_t(&self) -> bool {
matches!(self, Self::T(_))
}
pub fn is_n(&self) -> bool {
matches!(self, Self::N(..))
}
pub fn is_end(&self) -> bool {
matches!(self, Self::T(Terminal::End))
}
pub fn is_switch(&self) -> bool {
false
}
pub fn get_t(&self) -> Option<Terminal> {
if let Self::T(t) = &self {
Some(t.clone())
} else {
None
}
}
pub fn get_t_ref(&self) -> Option<&Terminal> {
if let Self::T(t) = &self {
Some(t)
} else {
None
}
}
pub fn get_n(&self) -> Option<String> {
if let Self::N(n, ..) = &self {
Some(n.clone())
} else {
None
}
}
pub fn get_n_ref(&self) -> Option<&str> {
if let Self::N(n, ..) = &self {
Some(n)
} else {
None
}
}
pub fn attribute(&self) -> SymbolAttribute {
match self {
Symbol::N(_, a, _, _) | Symbol::T(Terminal::Trm(_, _, _, a, _, _, _)) => *a,
_ => SymbolAttribute::None,
}
}
pub fn format<R, S>(&self, scanner_state_resolver: &R, user_type_resolver: &S) -> Result<String>
where
R: Fn(&[usize]) -> String,
S: Fn(&str) -> Option<String>,
{
match self {
Self::N(n, a, u, m) => {
let mut s = String::new();
a.decorate(&mut s, n)
.map_err(|e| anyhow!("Decorate error!: {}", e))?;
if let Some(member) = m {
write!(s, "@{member}").map_err(|e| anyhow!("IO error!: {}", e))?;
}
if let Some(user_type) = u {
let alias =
if let Some(alias) = user_type_resolver(user_type.to_string().as_str()) {
alias
} else if let Some(nt_type) = user_type_resolver(n) {
nt_type
} else {
"%nt_type".to_string()
};
if alias != "%nt_type" && alias != "%t_type" {
write!(s, " : {alias}").map_err(|e| anyhow!("IO error!: {}", e))?;
}
}
Ok(s)
}
Self::T(t) => t.format(scanner_state_resolver, user_type_resolver),
_ => unreachable!(
"Scanner switching directives have been removed from the grammar syntax."
),
}
}
}
impl Display for Symbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> {
match self {
Self::N(n, a, u, m) => {
let mut s = String::new();
a.decorate(&mut s, n)?;
if let Some(member) = m {
write!(s, "@{member}")?;
}
if let Some(user_type) = u {
write!(s, " : {user_type} ")?;
}
write!(f, "{s}")
}
Self::T(t) => {
let mut d = String::new();
self.attribute().decorate(&mut d, &format!("{t}"))?;
write!(f, "{d}")
}
_ => unreachable!(
"Scanner switching directives have been removed from the grammar syntax."
),
}
}
}
impl TryFrom<Factor> for Symbol {
type Error = anyhow::Error;
fn try_from(factor: Factor) -> Result<Self> {
try_from_factor(factor)
}
}