improve error types

This commit is contained in:
nora 2021-12-30 13:59:17 +01:00
parent 78f8382502
commit 219a7b7e37
4 changed files with 107 additions and 120 deletions

View file

@ -11,7 +11,7 @@ use bumpalo::Bump;
use std::cell::RefCell;
use std::rc::Rc;
type CResult<T> = Result<T, CompileError>;
type CResult<T> = Result<T, CompilerError>;
#[derive(Debug, Default)]
struct Env {
@ -30,8 +30,9 @@ impl Env {
})
}
lookup_inner(self, name)
.ok_or_else(|| CompileError::new(name.span, format!("variable {} not found", name.sym)))
lookup_inner(self, name).ok_or_else(|| {
CompilerError::new(name.span, format!("variable {} not found", name.sym))
})
}
fn new_inner(outer: Rc<RefCell<Self>>) -> Rc<RefCell<Self>> {
@ -54,7 +55,7 @@ struct Compiler<'bc> {
pub fn compile<'bc>(
ast: &Program,
bytecode_bump: &'bc Bump,
) -> Result<Vec<'bc, FnBlock<'bc>>, CompileError> {
) -> Result<Vec<'bc, FnBlock<'bc>>, CompilerError> {
let mut compiler = Compiler {
blocks: Vec::new_in(bytecode_bump),
current_block: 0,
@ -285,36 +286,5 @@ enum StackChange {
Grow = 1,
}
#[derive(Debug)]
pub struct CompileError {
span: Span,
message: String,
note: Option<String>,
}
impl CompileError {
fn new(span: Span, message: String) -> Self {
Self {
span,
message,
note: None,
}
}
}
impl CompilerError for CompileError {
fn span(&self) -> Span {
self.span
}
fn message(&self) -> String {
self.message.clone()
}
fn note(&self) -> Option<String> {
self.note.clone()
}
}
#[cfg(test)]
mod test {}

View file

@ -62,23 +62,41 @@ mod span {
}
}
pub trait CompilerError {
fn span(&self) -> Span;
fn message(&self) -> String;
fn note(&self) -> Option<String>;
#[derive(Debug, Clone)]
pub struct CompilerError {
pub span: Span,
pub message: String,
pub note: Option<String>,
}
pub fn display_error<E>(source: &str, error: E)
where
E: CompilerError + Debug,
{
impl CompilerError {
pub fn new(span: Span, message: String) -> Self {
Self {
span,
message,
note: None,
}
}
pub fn with_note(span: Span, message: String, note: String) -> Self {
Self {
span,
message,
note: Some(note),
}
}
}
pub fn display_error(source: &str, error: CompilerError) {
let span = error.span;
let mut chars = 0;
let lines = source.split_inclusive('\n').enumerate();
for (idx, line) in lines {
if chars + line.len() > error.span().start {
let offset_on_line = error.span().start - chars;
if chars + line.len() > span.start {
let offset_on_line = span.start - chars;
println!("{}error: {}{}", RED, error.message(), RESET);
println!("{}error: {}{}", RED, error.message, RESET);
println!(" {}|{}", CYAN, RESET);
println!("{}{:>5} |{} {}", CYAN, idx + 1, RESET, line);
print!(" {}|{} ", CYAN, RESET);
@ -86,10 +104,10 @@ where
"{}{}{}{}",
" ".repeat(offset_on_line),
RED,
"^".repeat(error.span().len()),
"^".repeat(span.len()),
RESET,
);
if let Some(note) = error.note() {
if let Some(note) = error.note {
println!(" {}|{}", CYAN, RESET);
println!(
" {}|{} {}note: {}{}",

View file

@ -367,34 +367,37 @@ impl LexError {
}
}
impl CompilerError for LexError {
fn span(&self) -> Span {
self.span
}
fn message(&self) -> String {
match &self.kind {
LexErrorKind::InvalidCharacter(char) => format!("Unexpected character: '{}'", char),
LexErrorKind::InvalidFloat(_) => "Invalid number".to_string(),
LexErrorKind::FloatInfiniteLiteral => "Number literal too long".to_string(),
LexErrorKind::UnclosedStringLiteral => "String literal not closed".to_string(),
LexErrorKind::SingleBang => "Expected '=' after '!'".to_string(),
}
}
fn note(&self) -> Option<String> {
match &self.kind {
LexErrorKind::InvalidCharacter(_) => {
Some("Character is not allowed outside of string literals and comments".to_string())
}
LexErrorKind::InvalidFloat(err) => Some(err.to_string()),
LexErrorKind::FloatInfiniteLiteral => Some(
"A number literal cannot be larger than a 64 bit float can represent".to_string(),
),
LexErrorKind::UnclosedStringLiteral => Some("Close the literal using '\"'".to_string()),
LexErrorKind::SingleBang => {
Some("If you meant to use it for negation, use `not`".to_string())
}
impl From<LexError> for CompilerError {
fn from(error: LexError) -> Self {
Self {
span: { error.span },
message: {
match &error.kind {
LexErrorKind::InvalidCharacter(char) => {
format!("Unexpected character: '{}'", char)
}
LexErrorKind::InvalidFloat(_) => "Invalid number".to_string(),
LexErrorKind::FloatInfiniteLiteral => "Number literal too long".to_string(),
LexErrorKind::UnclosedStringLiteral => "String literal not closed".to_string(),
LexErrorKind::SingleBang => "Expected '=' after '!'".to_string(),
}
},
note: match &error.kind {
LexErrorKind::InvalidCharacter(_) => Some(
"Character is not allowed outside of string literals and comments".to_string(),
),
LexErrorKind::InvalidFloat(err) => Some(err.to_string()),
LexErrorKind::FloatInfiniteLiteral => Some(
"A number literal cannot be larger than a 64 bit float can represent"
.to_string(),
),
LexErrorKind::UnclosedStringLiteral => {
Some("Close the literal using '\"'".to_string())
}
LexErrorKind::SingleBang => {
Some("If you meant to use it for negation, use `not`".to_string())
}
},
}
}
}

View file

@ -26,7 +26,7 @@ where
pub fn parse<'ast, 'code>(
tokens: impl Iterator<Item = Result<Token<'code>, LexError>> + 'code,
ast_bump: &'ast Bump,
) -> Result<Program<'ast>, ParseErr<'code>> {
) -> Result<Program<'ast>, CompilerError> {
let mut parser = Parser {
tokens: tokens.peekable(),
depth: 0,
@ -690,55 +690,51 @@ pub enum ParseErr<'code> {
LexError(LexError),
}
// todo: unify error handling
impl From<LexError> for ParseErr<'_> {
fn from(err: LexError) -> Self {
Self::LexError(err)
}
}
impl CompilerError for ParseErr<'_> {
fn span(&self) -> Span {
match self {
ParseErr::MismatchedKind {
actual: Token { span, .. },
..
} => *span,
ParseErr::InvalidTokenPrimary(Token { span, .. }) => *span,
ParseErr::EofExpecting(_) => Span::dummy(),
ParseErr::Eof(_) => Span::dummy(),
ParseErr::BreakOutsideLoop(span) => *span,
ParseErr::ReturnOutsideFunction(span) => *span,
ParseErr::MaxDepth(span) => *span,
ParseErr::LexError(err) => err.span,
}
}
fn message(&self) -> String {
match self {
ParseErr::MismatchedKind { expected, actual } => {
format!("expected `{:?}`, received `{:?}`", expected, actual.kind)
}
ParseErr::InvalidTokenPrimary(token) => {
format!("invalid token in expression: `{:?}`", token.kind)
}
ParseErr::EofExpecting(token) => {
format!("reached EOF searching for `{:?}`", token)
}
ParseErr::Eof(message) => {
format!("reached EOF while parsing `{}`", message)
}
ParseErr::BreakOutsideLoop(_) => "break used outside of loop".to_string(),
ParseErr::ReturnOutsideFunction(_) => "return used outside of function".to_string(),
ParseErr::MaxDepth(_) => "reached maximal nesting depth".to_string(),
ParseErr::LexError(err) => err.message(),
}
}
fn note(&self) -> Option<String> {
match self {
ParseErr::LexError(err) => err.note(),
_ => None,
// todo: remove this and ParseErr
impl From<ParseErr<'_>> for CompilerError {
fn from(error: ParseErr<'_>) -> Self {
Self {
span: match &error {
ParseErr::MismatchedKind {
actual: Token { span, .. },
..
} => *span,
ParseErr::InvalidTokenPrimary(Token { span, .. }) => *span,
ParseErr::EofExpecting(_) => Span::dummy(),
ParseErr::Eof(_) => Span::dummy(),
ParseErr::BreakOutsideLoop(span) => *span,
ParseErr::ReturnOutsideFunction(span) => *span,
ParseErr::MaxDepth(span) => *span,
ParseErr::LexError(err) => err.span,
},
message: match &error {
ParseErr::MismatchedKind { expected, actual } => {
format!("expected `{:?}`, received `{:?}`", expected, actual.kind)
}
ParseErr::InvalidTokenPrimary(token) => {
format!("invalid token in expression: `{:?}`", token.kind)
}
ParseErr::EofExpecting(token) => {
format!("reached EOF searching for `{:?}`", token)
}
ParseErr::Eof(message) => {
format!("reached EOF while parsing `{}`", message)
}
ParseErr::BreakOutsideLoop(_) => "break used outside of loop".to_string(),
ParseErr::ReturnOutsideFunction(_) => "return used outside of function".to_string(),
ParseErr::MaxDepth(_) => "reached maximal nesting depth".to_string(),
ParseErr::LexError(err) => err.message(),
},
note: match error {
ParseErr::LexError(err) => err.note(),
_ => None,
},
}
}
}