From a69fea553866dd3af00a5d4a74129e2c83c07834 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sat, 31 Dec 2022 14:00:23 +0100
Subject: [PATCH] Errors - ErrorHandler now includes info about the stage that
 failed

---
 src/errors.rs  | 28 +++++++++++++++++++---------
 src/main.rs    | 13 ++++++++-----
 src/scanner.rs | 12 +++++++++---
 3 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/src/errors.rs b/src/errors.rs
index 7d9db21..c67869d 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,25 +1,36 @@
 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
 /// current error status.
 #[derive(Default, Debug)]
 pub struct ErrorHandler {
-    had_error: bool,
+    had_error: Option<ErrorType>,
 }
 
 impl ErrorHandler {
     /// Check whether this handler reported an error.
-    pub fn had_error(&self) -> bool {
+    pub fn had_error(&self) -> Option<ErrorType> {
         self.had_error
     }
 
     /// Report an error.
-    pub fn error(&mut self, line: usize, message: &str) {
-        self.report(line, "", message)
+    pub fn error(&mut self, err_type: ErrorType, line: usize, message: &str) {
+        self.report(err_type, line, "", message)
     }
 
-    fn report(&mut self, line: usize, pos: &str, message: &str) {
-        self.had_error = true;
+    fn report(&mut self, err_type: ErrorType, line: usize, pos: &str, message: &str) {
+        if self.had_error.is_none() {
+            self.had_error = Option::Some(err_type);
+        }
         println!("[line {line}] Error{pos}: {message}")
     }
 }
@@ -49,11 +60,10 @@ impl ParserError {
 
     /// Report the error to an error handler.
     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.
 #[derive(Debug, Clone)]
 pub struct InterpreterError {
@@ -72,6 +82,6 @@ impl InterpreterError {
 
     /// Report the error to an error handler.
     pub fn report(&self, err_hdl: &mut ErrorHandler) {
-        err_hdl.report(self.line, "", &self.message);
+        err_hdl.report(ErrorType::Runtime, self.line, "", &self.message);
     }
 }
diff --git a/src/main.rs b/src/main.rs
index c3e7a7a..7ba4541 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@ use std::{
 };
 
 use ast::AstDumper;
-use errors::ErrorHandler;
+use errors::{ErrorHandler, ErrorType};
 use interpreter::evaluate;
 use parser::Parser;
 use scanner::Scanner;
@@ -72,13 +72,16 @@ fn main() -> Result<(), ExitCode> {
     let args: Vec<String> = env::args().skip(1).collect();
     let n_args = args.len();
     if n_args == 0 {
-        run_prompt()
+        run_prompt();
+        Ok(())
     } else if n_args == 1 {
-        if run_file(&args[0]).had_error() {
-            return Err(ExitCode::from(65));
+        match run_file(&args[0]).had_error() {
+            None => Ok(()),
+            Some(ErrorType::Parse) => Err(ExitCode::from(65)),
+            Some(ErrorType::Runtime) => Err(ExitCode::from(70)),
         }
     } else {
         println!("Usage: slox [script]");
+        Err(ExitCode::from(1))
     }
-    Ok(())
 }
diff --git a/src/scanner.rs b/src/scanner.rs
index 5228002..f9dcf38 100644
--- a/src/scanner.rs
+++ b/src/scanner.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
 use lazy_static::lazy_static;
 
 use crate::{
+    errors::ErrorType,
     tokens::{Token, TokenType},
     ErrorHandler,
 };
@@ -135,7 +136,11 @@ impl Scanner {
             // Identifiers
             ch if ch.is_ascii_alphabetic() => self.identifier(),
             // 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() {
-            err_hdl.error(self.line, "unterminated string");
+            err_hdl.error(ErrorType::Parse, self.line, "unterminated string");
         } else {
             self.current += 1; // Last '"'
             let value = self.get_substring(self.start + 1, self.current - 1);
@@ -177,6 +182,7 @@ impl Scanner {
         match tok_string.parse::<f64>() {
             Err(e) => {
                 err_hdl.error(
+                    ErrorType::Parse,
                     self.line,
                     &format!(
                         "Could not parse {} as a floating point number: {:?}",
@@ -207,7 +213,7 @@ impl Scanner {
         let mut depth = 1;
         loop {
             if self.is_at_end() {
-                err_hdl.error(self.line, "unterminated block comment");
+                err_hdl.error(ErrorType::Parse, self.line, "unterminated block comment");
                 return;
             }