Errors - ErrorHandler now includes info about the stage that failed

This commit is contained in:
Emmanuel BENOîT 2022-12-31 14:00:23 +01:00
parent da50da4e08
commit a69fea5538
3 changed files with 36 additions and 17 deletions

View file

@ -1,25 +1,36 @@
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
/// The type of an error.
#[derive(Clone, Copy, Debug)]
pub enum ErrorType {
/// The error occurred while parsing the source code.
Parse,
/// The error occurred while trying to run the program.
Runtime,
}
/// Error handler. Can be used to print error messages; will also retain the /// Error handler. Can be used to print error messages; will also retain the
/// current error status. /// current error status.
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ErrorHandler { pub struct ErrorHandler {
had_error: bool, had_error: Option<ErrorType>,
} }
impl ErrorHandler { impl ErrorHandler {
/// Check whether this handler reported an error. /// Check whether this handler reported an error.
pub fn had_error(&self) -> bool { pub fn had_error(&self) -> Option<ErrorType> {
self.had_error self.had_error
} }
/// Report an error. /// Report an error.
pub fn error(&mut self, line: usize, message: &str) { pub fn error(&mut self, err_type: ErrorType, line: usize, message: &str) {
self.report(line, "", message) self.report(err_type, line, "", message)
} }
fn report(&mut self, line: usize, pos: &str, message: &str) { fn report(&mut self, err_type: ErrorType, line: usize, pos: &str, message: &str) {
self.had_error = true; if self.had_error.is_none() {
self.had_error = Option::Some(err_type);
}
println!("[line {line}] Error{pos}: {message}") println!("[line {line}] Error{pos}: {message}")
} }
} }
@ -49,11 +60,10 @@ impl ParserError {
/// Report the error to an error handler. /// Report the error to an error handler.
pub fn report(&self, err_hdl: &mut ErrorHandler) { pub fn report(&self, err_hdl: &mut ErrorHandler) {
err_hdl.report(self.line, &self.pos, &self.message); err_hdl.report(ErrorType::Parse, self.line, &self.pos, &self.message);
} }
} }
/// An error that occurred while trying to evaluate the code. /// An error that occurred while trying to evaluate the code.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InterpreterError { pub struct InterpreterError {
@ -72,6 +82,6 @@ impl InterpreterError {
/// Report the error to an error handler. /// Report the error to an error handler.
pub fn report(&self, err_hdl: &mut ErrorHandler) { pub fn report(&self, err_hdl: &mut ErrorHandler) {
err_hdl.report(self.line, "", &self.message); err_hdl.report(ErrorType::Runtime, self.line, "", &self.message);
} }
} }

View file

@ -12,7 +12,7 @@ use std::{
}; };
use ast::AstDumper; use ast::AstDumper;
use errors::ErrorHandler; use errors::{ErrorHandler, ErrorType};
use interpreter::evaluate; use interpreter::evaluate;
use parser::Parser; use parser::Parser;
use scanner::Scanner; use scanner::Scanner;
@ -72,13 +72,16 @@ fn main() -> Result<(), ExitCode> {
let args: Vec<String> = env::args().skip(1).collect(); let args: Vec<String> = env::args().skip(1).collect();
let n_args = args.len(); let n_args = args.len();
if n_args == 0 { if n_args == 0 {
run_prompt() run_prompt();
Ok(())
} else if n_args == 1 { } else if n_args == 1 {
if run_file(&args[0]).had_error() { match run_file(&args[0]).had_error() {
return Err(ExitCode::from(65)); None => Ok(()),
Some(ErrorType::Parse) => Err(ExitCode::from(65)),
Some(ErrorType::Runtime) => Err(ExitCode::from(70)),
} }
} else { } else {
println!("Usage: slox [script]"); println!("Usage: slox [script]");
Err(ExitCode::from(1))
} }
Ok(())
} }

View file

@ -3,6 +3,7 @@ use std::collections::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use crate::{ use crate::{
errors::ErrorType,
tokens::{Token, TokenType}, tokens::{Token, TokenType},
ErrorHandler, ErrorHandler,
}; };
@ -135,7 +136,11 @@ impl Scanner {
// Identifiers // Identifiers
ch if ch.is_ascii_alphabetic() => self.identifier(), ch if ch.is_ascii_alphabetic() => self.identifier(),
// Anything else is an error // Anything else is an error
ch => err_hdl.error(self.line, &format!("unexpected character {:#?}", ch)), ch => err_hdl.error(
ErrorType::Parse,
self.line,
&format!("unexpected character {:#?}", ch),
),
} }
} }
@ -153,7 +158,7 @@ impl Scanner {
} }
if self.is_at_end() { if self.is_at_end() {
err_hdl.error(self.line, "unterminated string"); err_hdl.error(ErrorType::Parse, self.line, "unterminated string");
} else { } else {
self.current += 1; // Last '"' self.current += 1; // Last '"'
let value = self.get_substring(self.start + 1, self.current - 1); let value = self.get_substring(self.start + 1, self.current - 1);
@ -177,6 +182,7 @@ impl Scanner {
match tok_string.parse::<f64>() { match tok_string.parse::<f64>() {
Err(e) => { Err(e) => {
err_hdl.error( err_hdl.error(
ErrorType::Parse,
self.line, self.line,
&format!( &format!(
"Could not parse {} as a floating point number: {:?}", "Could not parse {} as a floating point number: {:?}",
@ -207,7 +213,7 @@ impl Scanner {
let mut depth = 1; let mut depth = 1;
loop { loop {
if self.is_at_end() { if self.is_at_end() {
err_hdl.error(self.line, "unterminated block comment"); err_hdl.error(ErrorType::Parse, self.line, "unterminated block comment");
return; return;
} }