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

View file

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

View file

@ -367,34 +367,37 @@ impl LexError {
} }
} }
impl CompilerError for LexError { impl From<LexError> for CompilerError {
fn span(&self) -> Span { fn from(error: LexError) -> Self {
self.span Self {
} span: { error.span },
message: {
fn message(&self) -> String { match &error.kind {
match &self.kind { LexErrorKind::InvalidCharacter(char) => {
LexErrorKind::InvalidCharacter(char) => format!("Unexpected character: '{}'", char), format!("Unexpected character: '{}'", char)
LexErrorKind::InvalidFloat(_) => "Invalid number".to_string(), }
LexErrorKind::FloatInfiniteLiteral => "Number literal too long".to_string(), LexErrorKind::InvalidFloat(_) => "Invalid number".to_string(),
LexErrorKind::UnclosedStringLiteral => "String literal not closed".to_string(), LexErrorKind::FloatInfiniteLiteral => "Number literal too long".to_string(),
LexErrorKind::SingleBang => "Expected '=' after '!'".to_string(), LexErrorKind::UnclosedStringLiteral => "String literal not closed".to_string(),
} LexErrorKind::SingleBang => "Expected '=' after '!'".to_string(),
} }
},
fn note(&self) -> Option<String> { note: match &error.kind {
match &self.kind { LexErrorKind::InvalidCharacter(_) => Some(
LexErrorKind::InvalidCharacter(_) => { "Character is not allowed outside of string literals and comments".to_string(),
Some("Character is not allowed outside of string literals and comments".to_string()) ),
} LexErrorKind::InvalidFloat(err) => Some(err.to_string()),
LexErrorKind::InvalidFloat(err) => Some(err.to_string()), LexErrorKind::FloatInfiniteLiteral => Some(
LexErrorKind::FloatInfiniteLiteral => Some( "A number literal cannot be larger than a 64 bit float can represent"
"A number literal cannot be larger than a 64 bit float can represent".to_string(), .to_string(),
), ),
LexErrorKind::UnclosedStringLiteral => Some("Close the literal using '\"'".to_string()), LexErrorKind::UnclosedStringLiteral => {
LexErrorKind::SingleBang => { Some("Close the literal using '\"'".to_string())
Some("If you meant to use it for negation, use `not`".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>( pub fn parse<'ast, 'code>(
tokens: impl Iterator<Item = Result<Token<'code>, LexError>> + 'code, tokens: impl Iterator<Item = Result<Token<'code>, LexError>> + 'code,
ast_bump: &'ast Bump, ast_bump: &'ast Bump,
) -> Result<Program<'ast>, ParseErr<'code>> { ) -> Result<Program<'ast>, CompilerError> {
let mut parser = Parser { let mut parser = Parser {
tokens: tokens.peekable(), tokens: tokens.peekable(),
depth: 0, depth: 0,
@ -690,55 +690,51 @@ pub enum ParseErr<'code> {
LexError(LexError), LexError(LexError),
} }
// todo: unify error handling
impl From<LexError> for ParseErr<'_> { impl From<LexError> for ParseErr<'_> {
fn from(err: LexError) -> Self { fn from(err: LexError) -> Self {
Self::LexError(err) Self::LexError(err)
} }
} }
impl CompilerError for ParseErr<'_> { // todo: remove this and ParseErr
fn span(&self) -> Span { impl From<ParseErr<'_>> for CompilerError {
match self { fn from(error: ParseErr<'_>) -> Self {
ParseErr::MismatchedKind { Self {
actual: Token { span, .. }, span: match &error {
.. ParseErr::MismatchedKind {
} => *span, actual: Token { span, .. },
ParseErr::InvalidTokenPrimary(Token { span, .. }) => *span, ..
ParseErr::EofExpecting(_) => Span::dummy(), } => *span,
ParseErr::Eof(_) => Span::dummy(), ParseErr::InvalidTokenPrimary(Token { span, .. }) => *span,
ParseErr::BreakOutsideLoop(span) => *span, ParseErr::EofExpecting(_) => Span::dummy(),
ParseErr::ReturnOutsideFunction(span) => *span, ParseErr::Eof(_) => Span::dummy(),
ParseErr::MaxDepth(span) => *span, ParseErr::BreakOutsideLoop(span) => *span,
ParseErr::LexError(err) => err.span, ParseErr::ReturnOutsideFunction(span) => *span,
} ParseErr::MaxDepth(span) => *span,
} ParseErr::LexError(err) => err.span,
},
fn message(&self) -> String { message: match &error {
match self { ParseErr::MismatchedKind { expected, actual } => {
ParseErr::MismatchedKind { expected, actual } => { format!("expected `{:?}`, received `{:?}`", expected, actual.kind)
format!("expected `{:?}`, received `{:?}`", expected, actual.kind) }
} ParseErr::InvalidTokenPrimary(token) => {
ParseErr::InvalidTokenPrimary(token) => { format!("invalid token in expression: `{:?}`", token.kind)
format!("invalid token in expression: `{:?}`", token.kind) }
} ParseErr::EofExpecting(token) => {
ParseErr::EofExpecting(token) => { format!("reached EOF searching for `{:?}`", token)
format!("reached EOF searching for `{:?}`", token) }
} ParseErr::Eof(message) => {
ParseErr::Eof(message) => { format!("reached EOF while parsing `{}`", message)
format!("reached EOF while parsing `{}`", message) }
} ParseErr::BreakOutsideLoop(_) => "break used outside of loop".to_string(),
ParseErr::BreakOutsideLoop(_) => "break used outside of loop".to_string(), ParseErr::ReturnOutsideFunction(_) => "return used outside of function".to_string(),
ParseErr::ReturnOutsideFunction(_) => "return used outside of function".to_string(), ParseErr::MaxDepth(_) => "reached maximal nesting depth".to_string(),
ParseErr::MaxDepth(_) => "reached maximal nesting depth".to_string(), ParseErr::LexError(err) => err.message(),
ParseErr::LexError(err) => err.message(), },
} note: match error {
} ParseErr::LexError(err) => err.note(),
_ => None,
fn note(&self) -> Option<String> { },
match self {
ParseErr::LexError(err) => err.note(),
_ => None,
} }
} }
} }