Prettier error handling

This commit is contained in:
Alex Bethel 2021-07-13 13:54:27 -05:00
parent 8818536717
commit a225105f09
4 changed files with 75 additions and 12 deletions

View file

@ -20,6 +20,8 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
error::Error,
fmt::Display,
io::{Read, Write}, io::{Read, Write},
}; };
@ -288,6 +290,25 @@ pub enum ProgramError {
TapeSizeExceededLimit, TapeSizeExceededLimit,
} }
impl Display for ProgramError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ProgramError::DataPointerUnderflow => "data pointer underflow",
ProgramError::IntegerOverflow => "integer overflow",
ProgramError::IntegerUnderflow => "integer underflow",
ProgramError::InputReadError => "input read error",
ProgramError::UnmatchedOpeningBracket => "unmatched `[`",
ProgramError::UnmatchedClosingBracket => "unmatched `]`",
ProgramError::TapeSizeExceededLimit => "tape size exceeded",
}
)
}
}
impl Error for ProgramError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// An error that occurred while the interpreter was being run start-to-end all in one go /// An error that occurred while the interpreter was being run start-to-end all in one go
pub enum InterpretError { pub enum InterpretError {
@ -297,6 +318,18 @@ pub enum InterpretError {
OutputWriteError, OutputWriteError,
} }
impl Display for InterpretError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InterpretError::ProgramError(e) => write!(f, "program error: {}", e),
InterpretError::EndOfInput => write!(f, "unexpected end of input"),
InterpretError::OutputBufferFull => write!(f, "output buffer full"),
InterpretError::OutputWriteError => write!(f, "output write error"),
}
}
}
impl Error for InterpretError {}
impl From<ProgramError> for InterpretError { impl From<ProgramError> for InterpretError {
fn from(e: ProgramError) -> Self { fn from(e: ProgramError) -> Self {
InterpretError::ProgramError(e) InterpretError::ProgramError(e)

View file

@ -1,4 +1,4 @@
use std::{io, ops::Range}; use std::{fmt::Display, io, ops::Range};
use crate::{brian::InterpretError, lexer::Token}; use crate::{brian::InterpretError, lexer::Token};
@ -36,6 +36,37 @@ impl Error {
} }
} }
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Error at range {}-{}: {}",
self.span.start, self.span.end, self.kind
)
}
}
impl std::error::Error for Error {}
impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::SyntaxError(desc) => write!(f, "syntax error: {}", desc),
ErrorKind::UnexpectedEof => write!(f, "unexpected end of file"),
ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token),
ErrorKind::InvalidIdentifier => write!(f, "invalid identifier"),
ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name),
ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name),
ErrorKind::TypeError(desc) => write!(f, "type error: {}", desc),
ErrorKind::TopLevelBreak => write!(f, "can only `break` out of a loop"),
ErrorKind::BfInterpretError(err) => write!(f, "brainfuck error: {}", err),
// TODO: give concrete numbers here.
ErrorKind::MismatchedArgumentError => write!(f, "wrong number of function arguments"),
ErrorKind::MissingLhs => write!(f, "missing expression before binary operation"),
ErrorKind::IOError(err) => write!(f, "I/O error: {}", err),
}
}
}
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(e: io::Error) -> Self { fn from(e: io::Error) -> Self {
Self { Self {

View file

@ -1,4 +1,3 @@
use logos::Source;
use rustyline::Editor; use rustyline::Editor;
use crate::{interpret::ExecEnv, parser::Parser}; use crate::{interpret::ExecEnv, parser::Parser};
@ -10,7 +9,13 @@ pub fn repl(ast_print: bool) {
let readline = rl.readline(":: "); let readline = rl.readline(":: ");
match readline { match readline {
Ok(line) => { Ok(line) => {
if &line == "exit" { // NOTE(Alex): `readline()` leaves a newline at the
// end of the string if stdin is connected to a file
// or unsupported terminal; this can interfere with
// error printing.
let line = line.trim_end();
if line == "exit" {
println!("bye"); println!("bye");
break; break;
} }
@ -23,16 +28,10 @@ pub fn repl(ast_print: bool) {
}); });
if let Err(e) = value { if let Err(e) = value {
println!( println!("{}", e);
"Error `{:?}` occurred at span: {:?} = `{:?}`",
e.kind,
e.span.clone(),
line.slice(e.span.clone())
);
println!(" | {}", line); println!(" | {}", line);
println!( println!(
" {}{}-- Here", " {}{}",
" ".repeat(e.span.start), " ".repeat(e.span.start),
"^".repeat((e.span.end - e.span.start).max(1)) "^".repeat((e.span.end - e.span.start).max(1))
); );

View file

@ -126,7 +126,7 @@ impl Value {
match self { match self {
Value::Int(i) => Ok(i), Value::Int(i) => Ok(i),
_ => Err(Error { _ => Err(Error {
kind: ErrorKind::TypeError(format!("Expected int, got {}", self)), kind: ErrorKind::TypeError(format!("expected int, got {}", self)),
span: span.clone(), span: span.clone(),
}), }),
} }