diff --git a/src/compile.rs b/src/compile.rs index 2fb69a6..5ecbcd8 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -11,7 +11,7 @@ use bumpalo::Bump; use std::cell::RefCell; use std::rc::Rc; -type CResult = Result; +type CResult = Result; #[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>) -> Rc> { @@ -54,7 +55,7 @@ struct Compiler<'bc> { pub fn compile<'bc>( ast: &Program, bytecode_bump: &'bc Bump, -) -> Result>, CompileError> { +) -> Result>, 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, -} - -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 { - self.note.clone() - } -} - #[cfg(test)] mod test {} diff --git a/src/errors.rs b/src/errors.rs index ecee830..b114a98 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -62,23 +62,41 @@ mod span { } } -pub trait CompilerError { - fn span(&self) -> Span; - fn message(&self) -> String; - fn note(&self) -> Option; +#[derive(Debug, Clone)] +pub struct CompilerError { + pub span: Span, + pub message: String, + pub note: Option, } -pub fn display_error(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: {}{}", diff --git a/src/lex.rs b/src/lex.rs index aceba87..78af59b 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -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 { - 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 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()) + } + }, } } } diff --git a/src/parse.rs b/src/parse.rs index 5b3f4e0..e8aeeb8 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -26,7 +26,7 @@ where pub fn parse<'ast, 'code>( tokens: impl Iterator, LexError>> + 'code, ast_bump: &'ast Bump, -) -> Result, ParseErr<'code>> { +) -> Result, 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 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 { - match self { - ParseErr::LexError(err) => err.note(), - _ => None, +// todo: remove this and ParseErr +impl From> 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, + }, } } }