use std::cmp::Ordering;
use std::fmt;
use std::fmt::Debug;
use thiserror::*;
#[derive(Debug, PartialEq, Eq, Clone, Error, Hash)]
pub enum Expected {
EOI,
Char(char),
Nil,
CharIn(&'static str),
ObOn(&'static str, &'static str),
Str(&'static str),
OneOf(Vec<Expected>),
Except(Box<Expected>),
}
impl fmt::Display for Expected {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Expected::*;
match self {
EOI => write!(f, "EOI"),
Char(c) => write!(f, "Char:\"{}\"", c),
Nil => write!(f, "Nil"),
CharIn(s) => write!(f, "Char In {:?}", s),
ObOn(p, o) => write!(f, "{} on parser {}", o, p),
Str(s) => write!(f, "{:?}", s),
OneOf(v) => {
write!(f, "one of:(")?;
for e in v {
write!(f, "{} ", e)?;
}
write!(f, ")")
}
Except(e) => write!(f, " Except : ({})", e),
}
}
}
impl Expected {
pub fn or(self, b: Self) -> Self {
match self {
Expected::OneOf(mut v) => {
v.push(b);
Expected::OneOf(v)
}
v => Expected::OneOf(vec![v, b]),
}
}
pub fn except(a: Self) -> Self {
Expected::Except(Box::new(a))
}
pub fn join(self, b: Self) -> Self {
match (self, b) {
(Expected::OneOf(mut ae), Expected::OneOf(be)) => {
ae.extend(be);
Expected::OneOf(ae)
}
(Expected::OneOf(mut ae), b) | (b, Expected::OneOf(mut ae)) => {
if b != Expected::Nil {
ae.push(b);
}
Expected::OneOf(ae)
}
(Expected::Nil, a) => a,
(a, Expected::Nil) => a,
(a, b) => Expected::OneOf(vec![a, b]),
}
}
pub fn first(a: Self, b: Self) -> Self {
match a == Expected::Nil {
true => b,
false => a,
}
}
}
#[derive(Clone, PartialEq, Eq, Error, Hash)]
pub struct PErr<'a> {
pub exp: Expected,
pub found: &'a str,
pub index: Option<usize>,
pub line: usize,
pub col: usize,
pub is_brk: bool,
pub child: Option<Box<PErr<'a>>>,
}
fn compare_index(a: &Option<usize>, b: &Option<usize>) -> Ordering {
match (a, b) {
(Some(a), Some(b)) => a.cmp(b),
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
_ => Ordering::Equal,
}
}
fn join_children<'a>(a: Option<Box<PErr<'a>>>, b: Option<Box<PErr<'a>>>) -> Option<Box<PErr<'a>>> {
match (a, b) {
(Some(ac), Some(bc)) => Some(Box::new((*ac).join(*bc))),
(None, b) => b,
(a, None) => a,
}
}
pub fn n_chars(s: &str, n: usize) -> String {
s.chars().take(n).collect()
}
impl<'a> PErr<'a> {
pub fn longer(mut self, b: Self) -> Self {
match compare_index(&self.index, &b.index) {
Ordering::Greater => self,
Ordering::Less => b,
_ => {
self.child = join_children(self.child, b.child);
self.exp = self.exp.join(b.exp);
self
}
}
}
pub fn join(mut self, mut b: Self) -> Self {
match compare_index(&self.index, &b.index) {
Ordering::Greater => {
self.child = join_children(self.child, Some(Box::new(b)));
self
}
Ordering::Less => {
b.child = join_children(b.child, Some(Box::new(self)));
b
}
_ => {
self.child = join_children(self.child, b.child);
self.exp = self.exp.join(b.exp);
self
}
}
}
pub fn join_op(self, b: Option<Self>) -> Self {
match b {
Some(v) => self.join(v),
None => self,
}
}
pub fn strung(self) -> StrungError {
StrungError {
exp: self.exp,
found: n_chars(self.found, 10),
line: self.line,
col: self.col,
index: self.index,
is_brk: self.is_brk,
child: self.child.map(|v| Box::new((*v).strung())),
}
}
pub fn brk(mut self) -> Self {
self.is_brk = true;
self
}
pub fn set_brk(mut self, b: bool) -> Self {
self.is_brk = b;
self
}
}
impl<'a> fmt::Debug for PErr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let i_str = match self.index {
Some(n) => n.to_string(),
None => "EOI".to_string(),
};
write!(
f,
"Expected '{}', Found '{}', at (i={},l={},c={})\n",
self.exp,
n_chars(&self.found, 10),
i_str,
self.line,
self.col
)?;
if let Some(ref c) = self.child {
write!(f, "\t{:?}", c)?
}
Ok(())
}
}
impl<'a> fmt::Display for PErr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let i_str = match self.index {
Some(n) => n.to_string(),
None => "EOI".to_string(),
};
write!(
f,
"Expected '{}', Found '{}', at (i={},l={},c={})\n",
self.exp,
n_chars(&self.found, 10),
i_str,
self.line,
self.col
)?;
if let Some(ref c) = self.child {
write!(f, "\t{}", c)?
}
Ok(())
}
}
#[derive(Clone, Error, PartialEq, Eq, Hash)]
pub struct StrungError {
pub exp: Expected,
pub found: String,
pub index: Option<usize>,
pub line: usize,
pub col: usize,
pub is_brk: bool,
pub child: Option<Box<StrungError>>,
}
impl fmt::Debug for StrungError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let i_str = match self.index {
Some(n) => n.to_string(),
None => "EOI".to_string(),
};
write!(
f,
"Expected '{}', Found '{}', at (i={},l={},c={})\n",
self.exp, self.found, i_str, self.line, self.col
)
}
}
impl fmt::Display for StrungError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let i_str = match self.index {
Some(n) => n.to_string(),
None => "EOI".to_string(),
};
write!(
f,
"Expected '{}', Found '{}', at (i={},l={},c={})\n",
self.exp, self.found, i_str, self.line, self.col
)?;
if let Some(ref c) = self.child {
write!(f, "\t{}", c)?
}
Ok(())
}
}