use santiago::lexer::Lexeme;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::{error, fmt, iter};
use crate::types;
use super::Position;
#[derive(Clone, Debug, Default)]
pub struct ErrorSet {
context: Option<Arc<str>>,
line_map: Arc<Mutex<Vec<usize>>>,
errors: BTreeMap<Option<Position>, Vec<Error>>,
}
impl<T> From<santiago::parser::ParseError<T>> for ErrorSet {
fn from(e: santiago::parser::ParseError<T>) -> Self {
let lex = e.at.map(|rc| (*rc).clone());
match lex.as_ref().map(|lex| &lex.position).map(Position::from) {
Some(pos) => ErrorSet::single(pos, Error::ParseFailed(lex)),
None => ErrorSet::single_no_position(Error::ParseFailed(lex)),
}
}
}
impl From<santiago::lexer::LexerError> for ErrorSet {
fn from(e: santiago::lexer::LexerError) -> Self {
ErrorSet::single(e.position, Error::LexFailed(e.message))
}
}
impl ErrorSet {
pub fn new() -> Self {
ErrorSet::default()
}
pub fn first_error(&self) -> Option<(Option<Position>, &Error)> {
self.errors.iter().next().map(|(a, b)| (*a, &b[0]))
}
pub fn iter(&self) -> impl Iterator<Item = &Error> {
self.errors.values().flatten()
}
pub fn single<P: Into<Position>, E: Into<Error>>(position: P, err: E) -> Self {
let mut errors = BTreeMap::default();
errors.insert(Some(position.into()), vec![err.into()]);
ErrorSet {
context: None,
line_map: Arc::new(Mutex::new(vec![])),
errors,
}
}
pub fn single_no_position<E: Into<Error>>(err: E) -> Self {
let mut errors = BTreeMap::default();
errors.insert(None, vec![err.into()]);
ErrorSet {
context: None,
line_map: Arc::new(Mutex::new(vec![])),
errors,
}
}
pub fn add<P: Into<Position>, E: Into<Error>>(&mut self, position: P, err: E) {
self.errors
.entry(Some(position.into()))
.or_default()
.push(err.into());
}
pub fn add_no_position<E: Into<Error>>(&mut self, err: E) {
self.errors.entry(None).or_default().push(err.into());
}
pub fn merge(&mut self, other: &Self) {
match (self.context.as_ref(), other.context.as_ref()) {
(None, None) => {}
(Some(_), None) => {}
(None, Some(b)) => self.context = Some(Arc::clone(b)),
(Some(a), Some(b)) => {
assert_eq!(a, b, "cannot merge error sets for different source input");
}
};
for (pos, errs) in &other.errors {
self.errors
.entry(*pos)
.or_default()
.extend(errs.iter().cloned());
}
}
pub fn add_context(&mut self, s: Arc<str>) {
if self.context.is_some() {
panic!("tried to add context to the same error context twice");
}
self.context = Some(s);
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn len(&self) -> usize {
self.errors.len()
}
pub fn into_result<T>(self, ok: T) -> Result<T, Self> {
if self.is_empty() {
Ok(ok)
} else {
Err(self)
}
}
pub fn into_result_with<T, F: FnOnce() -> T>(self, okfn: F) -> Result<T, Self> {
if self.is_empty() {
Ok(okfn())
} else {
Err(self)
}
}
}
impl error::Error for ErrorSet {
fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
match self.first_error()?.1 {
Error::Bad2ExpNumber(..) => None,
Error::BadWordLength { .. } => None,
Error::EntropyInsufficient { .. } => None,
Error::EntropyTooMuch { .. } => None,
Error::HoleAtCommitTime { .. } => None,
Error::HoleFilledAtCommitTime => None,
Error::NameIllegal(_) => None,
Error::NameIncomplete(_) => None,
Error::NameMissing(_) => None,
Error::NameRepeated(_) => None,
Error::NoMain => None,
Error::ParseFailed(_) => None,
Error::LexFailed(_) => None,
Error::NumberOutOfRange(_) => None,
Error::TypeCheck(ref e) => Some(e),
Error::Undefined(_) => None,
Error::UnknownJet(_) => None,
Error::WitnessDisconnectRepeated { .. } => None,
}
}
}
impl fmt::Display for ErrorSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut line_map = self.line_map.lock().unwrap();
if line_map.is_empty() {
if let Some(ref s) = self.context {
*line_map = iter::repeat(0)
.take(2)
.chain(
s.char_indices()
.filter_map(|(n, ch)| if ch == '\n' { Some(n) } else { None }),
)
.collect();
}
}
for (pos, errs) in &self.errors {
if let Some(pos) = pos {
for err in errs {
if let Some(ref s) = self.context {
let end = line_map.get(pos.line + 1).copied().unwrap_or(s.len());
let line = &s[line_map[pos.line] + 1..end];
writeln!(f, "{:5} | {}", pos.line, line)?;
writeln!(f, " | {:>width$}", "^", width = pos.column)?;
writeln!(f, " \\-- {}", err)?;
writeln!(f)?;
} else {
writeln!(f, "{:4}:{:2}: {}", pos.line, pos.column, err,)?;
writeln!(f)?;
}
}
} else {
for err in errs {
writeln!(f, "Error: {}", err)?;
}
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum Error {
Bad2ExpNumber(u32),
BadWordLength { bit_length: usize },
EntropyInsufficient { bit_length: usize },
EntropyTooMuch { bit_length: usize },
HoleAtCommitTime {
name: Arc<str>,
arrow: types::arrow::Arrow,
},
HoleFilledAtCommitTime,
NameIllegal(Arc<str>),
NameIncomplete(Arc<str>),
NameMissing(Arc<str>),
NameRepeated(Arc<str>),
NoMain,
ParseFailed(Option<Lexeme>),
LexFailed(String),
NumberOutOfRange(String),
TypeCheck(types::Error),
Undefined(String),
UnknownJet(String),
WitnessDisconnectRepeated { name: Arc<str>, count: usize },
}
impl From<types::Error> for Error {
fn from(e: types::Error) -> Self {
Error::TypeCheck(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::BadWordLength { bit_length } => {
write!(f, "word length {} is not a valid power of 2", bit_length)
}
Error::Bad2ExpNumber(exp) => {
write!(f, "types may be 2^n for n a power of 2, but not 2^{}", exp)
}
Error::EntropyInsufficient { bit_length } => write!(
f,
"fail node has insufficient entropy ({} bits, need 128)",
bit_length
),
Error::EntropyTooMuch { bit_length } => write!(
f,
"fail node has too much entropy ({} bits, max 512)",
bit_length
),
Error::HoleAtCommitTime {
ref name,
ref arrow,
} => write!(
f,
"unfilled hole ?{} at commitment time; type arrow {}",
name, arrow
),
Error::HoleFilledAtCommitTime => {
f.write_str("disconnect node has a non-hole child at commit time")
}
Error::NameIllegal(ref s) => {
write!(f, "name `{}` is not allowed in this context", s)
}
Error::NameIncomplete(ref s) => write!(f, "name `{}` has no expression", s),
Error::NameMissing(ref s) => {
write!(f, "name `{}` is referred to but does not exist", s)
}
Error::NameRepeated(ref s) => write!(f, "name `{}` occured mulitple times", s),
Error::NoMain => f.write_str("program does not define `main`"),
Error::NumberOutOfRange(ref n) => {
write!(f, "number {} was out of allowable range", n)
}
Error::ParseFailed(None) => f.write_str("could not parse"),
Error::ParseFailed(Some(ref lex)) => write!(f, "could not parse `{}`", lex.raw),
Error::LexFailed(ref msg) => write!(f, "could not parse: {}", msg),
Error::TypeCheck(ref e) => fmt::Display::fmt(e, f),
Error::Undefined(ref s) => write!(f, "reference to undefined symbol `{}`", s),
Error::UnknownJet(ref s) => write!(f, "unknown jet `{}`", s),
Error::WitnessDisconnectRepeated { ref name, count } => write!(
f,
"witness/disconnect node {} was accessible by {} distinct paths from the same root",
name, count,
),
}
}
}