more detailed parse errors when decoding with flat

This commit is contained in:
Kasey White 2022-10-22 18:35:16 -04:00 committed by Lucas
parent 4ef654b660
commit 15cfb22c8f
3 changed files with 207 additions and 4 deletions

View File

@ -18,6 +18,8 @@ pub enum Error {
DecodeChar(u32), DecodeChar(u32),
#[error("{0}")] #[error("{0}")]
Message(String), Message(String),
#[error("Parse error: So far we parsed {0} and we ran into error: {1}")]
ParseError(String, anyhow::Error),
#[error(transparent)] #[error(transparent)]
Custom(#[from] anyhow::Error), Custom(#[from] anyhow::Error),
} }

View File

@ -1,5 +1,6 @@
use std::{collections::VecDeque, fmt::Debug, rc::Rc}; use std::{collections::VecDeque, fmt::Debug, rc::Rc};
use anyhow::anyhow;
use flat_rs::{ use flat_rs::{
de::{self, Decode, Decoder}, de::{self, Decode, Decoder},
en::{self, Encode, Encoder}, en::{self, Encode, Encoder},
@ -109,10 +110,14 @@ where
T: Binder<'b>, T: Binder<'b>,
{ {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> { fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
let mut state_log: Vec<String> = vec![];
let version = (usize::decode(d)?, usize::decode(d)?, usize::decode(d)?); let version = (usize::decode(d)?, usize::decode(d)?, usize::decode(d)?);
let term = Term::decode(d)?; let term_option = Term::decode_debug(d, &mut state_log);
Ok(Program { version, term }) match term_option {
Ok(term) => Ok(Program { version, term }),
Err(error) => Err(de::Error::ParseError(state_log.join(""), anyhow!(error))),
}
} }
} }
@ -190,15 +195,189 @@ where
6 => Ok(Term::Error), 6 => Ok(Term::Error),
7 => Ok(Term::Builtin(DefaultFunction::decode(d)?)), 7 => Ok(Term::Builtin(DefaultFunction::decode(d)?)),
x => Err(de::Error::Message(format!( x => Err(de::Error::Message(format!(
"Unknown term constructor tag: {} and buffer position is {} and buffer length is {}", "Unknown term constructor tag: {}{} {:02X?} {} {} {} {}",
x, x,
d.buffer.len() - d.pos, ".\n\nHere are the buffer bytes (5 preceding) ",
d.buffer
.iter()
.skip(if d.pos - 5 > 0 { d.pos - 5 } else { 0 })
.take(10),
"\n\nBuffer position is",
d.pos,
"and buffer length is",
d.buffer.len() d.buffer.len()
))), ))),
} }
} }
} }
impl<'b, T> Term<T>
where
T: Binder<'b>,
{
fn decode_debug(d: &mut Decoder, state_log: &mut Vec<String>) -> Result<Term<T>, de::Error> {
match decode_term_tag(d)? {
0 => {
state_log.push("(var ".to_string());
let var_option = T::decode(d);
match var_option {
Ok(var) => {
state_log.push(format!("{})", var.text()));
Ok(Term::Var(var))
}
Err(error) => {
state_log.push("parse error)".to_string());
Err(error)
}
}
}
1 => {
state_log.push("(delay ".to_string());
let term_option = Term::decode_debug(d, state_log);
match term_option {
Ok(term) => {
state_log.push(")".to_string());
Ok(Term::Delay(Rc::new(term)))
}
Err(error) => {
state_log.push(")".to_string());
Err(error)
}
}
}
2 => {
state_log.push("(lam ".to_string());
let var_option = T::binder_decode(d);
match var_option {
Ok(var) => {
state_log.push(var.text().to_string());
let term_option = Term::decode_debug(d, state_log);
match term_option {
Ok(term) => {
state_log.push(")".to_string());
Ok(Term::Lambda {
parameter_name: var,
body: Rc::new(term),
})
}
Err(error) => {
state_log.push(")".to_string());
Err(error)
}
}
}
Err(error) => {
state_log.push(")".to_string());
Err(error)
}
}
}
3 => {
state_log.push("[ ".to_string());
let function_term_option = Term::decode_debug(d, state_log);
match function_term_option {
Ok(function) => {
state_log.push(" ".to_string());
let arg_term_option = Term::decode_debug(d, state_log);
match arg_term_option {
Ok(argument) => {
state_log.push("]".to_string());
Ok(Term::Apply {
function: Rc::new(function),
argument: Rc::new(argument),
})
}
Err(error) => {
state_log.push("]".to_string());
Err(error)
}
}
}
Err(error) => {
state_log.push(" not parsed]".to_string());
Err(error)
}
}
}
// Need size limit for Constant
4 => {
state_log.push("(con ".to_string());
let con_option = Constant::decode(d);
match con_option {
Ok(constant) => {
state_log.push(format!("{})", constant.to_pretty()));
Ok(Term::Constant(constant))
}
Err(error) => {
state_log.push("parse error)".to_string());
Err(error)
}
}
}
5 => {
state_log.push("(force ".to_string());
let term_option = Term::decode_debug(d, state_log);
match term_option {
Ok(term) => {
state_log.push(")".to_string());
Ok(Term::Force(Rc::new(term)))
}
Err(error) => {
state_log.push(")".to_string());
Err(error)
}
}
}
6 => {
state_log.push("(error)".to_string());
Ok(Term::Error)
}
7 => {
state_log.push("(builtin ".to_string());
let builtin_option = DefaultFunction::decode(d);
match builtin_option {
Ok(builtin) => {
state_log.push(format!("{})", builtin));
Ok(Term::Builtin(builtin))
}
Err(error) => {
state_log.push("parse error)".to_string());
Err(error)
}
}
}
x => {
state_log.push("parse error".to_string());
let buffer_slice: Vec<u8> = d
.buffer
.to_vec()
.iter()
.skip(if d.pos - 5 > 0 { d.pos - 5 } else { 0 })
.take(10)
.cloned()
.collect();
Err(de::Error::Message(format!(
"Unknown term constructor tag: {}{} {:02X?} {} {} {} {}",
x,
".\n\nHere are the buffer bytes (5 preceding) ",
buffer_slice,
"\n\nBuffer position is",
d.pos,
"and buffer length is",
d.buffer.len()
)))
}
}
}
}
/// Integers are typically smaller so we save space /// Integers are typically smaller so we save space
/// by encoding them in 7 bits and this allows it to be byte alignment agnostic. /// by encoding them in 7 bits and this allows it to be byte alignment agnostic.
/// Strings and bytestrings span multiple bytes so using bytestring is /// Strings and bytestrings span multiple bytes so using bytestring is

View File

@ -151,6 +151,28 @@ where
} }
impl Constant { impl Constant {
pub fn to_pretty(&self) -> String {
let mut w = Vec::new();
self.to_doc().render(80, &mut w).unwrap();
String::from_utf8(w)
.unwrap()
.lines()
// This is a hack to deal with blank newlines
// that end up with a bunch of useless whitespace
// because of the nesting
.map(|l| {
if l.chars().all(|c| c.is_whitespace()) {
"".to_string()
} else {
l.to_string()
}
})
.collect::<Vec<_>>()
.join("\n")
}
fn to_doc(&self) -> RcDoc<()> { fn to_doc(&self) -> RcDoc<()> {
match self { match self {
Constant::Integer(i) => RcDoc::text("integer") Constant::Integer(i) => RcDoc::text("integer")