From c83cd23d49c5f60028ce395ea944adfc22b60529 Mon Sep 17 00:00:00 2001 From: Araozu Date: Tue, 24 Jan 2023 10:01:09 -0500 Subject: [PATCH] Add functions for error handling (merge) --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/error_handling/lex_error.rs | 102 ++++++++++++++++++++++++++++++++ src/error_handling/mod.rs | 27 +++++++++ src/lexic/mod.rs | 10 ++-- src/lexic/scanner/number.rs | 3 +- src/lexic/scanner/string.rs | 3 +- src/main.rs | 14 ++++- src/repl/mod.rs | 8 ++- 9 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 src/error_handling/lex_error.rs create mode 100644 src/error_handling/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b70f350..b12dee6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,7 @@ name = "misti" version = "0.0.3" dependencies = [ "chrono", + "owo-colors", ] [[package]] @@ -199,6 +200,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "proc-macro2" version = "1.0.47" diff --git a/Cargo.toml b/Cargo.toml index f16e7c6..0035c6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] chrono = "0.4.23" +owo-colors = "3.5.0" diff --git a/src/error_handling/lex_error.rs b/src/error_handling/lex_error.rs new file mode 100644 index 0000000..803f3b0 --- /dev/null +++ b/src/error_handling/lex_error.rs @@ -0,0 +1,102 @@ +use std::collections::VecDeque; +use super::{PrintableError, LexError}; + +impl PrintableError for LexError { + fn get_error_str(&self, chars: &Vec) -> String { + String::from("D:") + } +} + +/// Extracts a line of code from `chars`. `pos` indicates a position, +/// from where to extract the line. +/// +/// Ex. Given: +/// - `input = "first line\nsecond line\nthird line"` +/// - `pos = 15` +/// +/// this function should return `"second line"` +fn get_line(chars: &Vec, pos: usize) -> String { + let mut result_chars = VecDeque::::new(); + + // Push chars to the front until a new line is found + let mut before_pos = pos; + loop { + let current_char = chars[before_pos]; + + if current_char == '\n' { + break; + } + + result_chars.push_front(current_char); + + if before_pos == 0 { + break; + } + + before_pos -= 1; + } + + // Push chars to the end until a new line is found + let mut after_pos = pos + 1; + let char_count = chars.len(); + while after_pos < char_count { + let current_char = chars[after_pos]; + + if current_char == '\n' { + break; + } + + result_chars.push_back(current_char); + after_pos += 1; + } + + result_chars.iter().collect::() +} + + + +#[cfg(test)] +mod tests { + use crate::lexic; + use super::*; + + #[test] + fn test() { + let input = String::from("val name' = 20"); + let result = lexic::get_tokens(&input); + + match result { + Ok(_) => assert!(false), + Err(err_data) => { + let chars: Vec = input.chars().into_iter().collect(); + let err_str = err_data.get_error_str(&chars); + assert_ne!( + "\n\ + val name' = 20\n\ + . ^\n\ + \n\ + Invalid character at line 1, pos 9", + err_str, + ); + } + } + } + + #[test] + fn should_extract_line() { + let input = String::from("first line\nsecond line\nthird line"); + let chars: Vec = input.chars().into_iter().collect(); + + let result = get_line(&chars, 15); + + assert_eq!("second line", result); + + + let input = String::from("val binding = 322"); + let chars: Vec = input.chars().into_iter().collect(); + + let result = get_line(&chars, 6); + + assert_eq!("val binding = 322", result); + } +} diff --git a/src/error_handling/mod.rs b/src/error_handling/mod.rs new file mode 100644 index 0000000..8ded5ac --- /dev/null +++ b/src/error_handling/mod.rs @@ -0,0 +1,27 @@ +mod lex_error; + +pub trait PrintableError { + fn get_error_str(&self, chars: &Vec) -> String; +} + +#[derive(Debug)] +pub enum MistiError { + Lex(LexError) +} + +#[derive(Debug)] +pub struct LexError { + pub position: usize, + pub reason: String, +} + + +impl PrintableError for MistiError { + fn get_error_str(&self, chars: &Vec) -> String { + match self { + Self::Lex(err) => err.get_error_str(chars) + } + } +} + + diff --git a/src/lexic/mod.rs b/src/lexic/mod.rs index 828b0dd..e6f2d4d 100755 --- a/src/lexic/mod.rs +++ b/src/lexic/mod.rs @@ -1,8 +1,8 @@ mod utils; mod scanner; -mod lex_error; + use super::token::{self, Token}; -use lex_error::LexError; +use crate::error_handling::{MistiError, LexError}; type Chars = Vec; @@ -34,7 +34,7 @@ pub enum LexResult { /// Scans and returns all the tokens in the input String -pub fn get_tokens(input: &String) -> Result, LexError> { +pub fn get_tokens(input: &String) -> Result, MistiError> { let chars: Vec = input.chars().into_iter().collect(); let mut results = Vec::new(); let mut current_pos: usize = 0; @@ -48,7 +48,9 @@ pub fn get_tokens(input: &String) -> Result, LexError> { LexResult::None(next_pos) => { current_pos = next_pos; }, - LexResult::Err(reason) => return Err(reason), + LexResult::Err(error_info) => { + return Err(MistiError::Lex(error_info)); + } } } diff --git a/src/lexic/scanner/number.rs b/src/lexic/scanner/number.rs index 8203e0e..95ba406 100755 --- a/src/lexic/scanner/number.rs +++ b/src/lexic/scanner/number.rs @@ -1,7 +1,8 @@ use crate::lexic::{ token::{self, Token}, - utils, LexResult, lex_error::LexError, + utils, LexResult, }; +use crate::error_handling::LexError; /// Function to scan a number /// diff --git a/src/lexic/scanner/string.rs b/src/lexic/scanner/string.rs index db2c77e..34f12cf 100755 --- a/src/lexic/scanner/string.rs +++ b/src/lexic/scanner/string.rs @@ -1,7 +1,8 @@ use crate::lexic::{ token, - utils, LexResult, lex_error::LexError, + utils, LexResult, }; +use crate::error_handling::LexError; /// Function to scan a string /// diff --git a/src/main.rs b/src/main.rs index e44afc5..7d1ea23 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,25 @@ use std::io; use chrono::{prelude::Utc, Datelike}; +// Module to handle the repl and its compilation mod repl; -mod syntax; -mod lexic; +// Defines the types of tokens and provides functions to create them mod token; +// Module to handle lexical analysis +mod syntax; +// Module to handle syntactic analysis +mod lexic; +// Module to handle semantic analysis mod semantic; +// Defines the AST mod ast_types; +// Defines the Symbol table and operations within mod symbol_table; +// Transforms an AST to JS mod codegen; +mod error_handling; + const VERSION: &str = "0.0.1"; fn get_copyright() -> String { diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 616224f..97f306b 100755 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1,5 +1,6 @@ use std::io::{self, Write}; +use crate::error_handling::PrintableError; use crate::symbol_table::SymbolTable; use crate::token::Token; @@ -10,14 +11,15 @@ use super::codegen; /// Executes Lexical analysis, handles errors and calls build_ast for the next phase fn compile(input: &String) { - let _tokens = lexic::get_tokens(input); + let tokens = lexic::get_tokens(input); - match _tokens { + match tokens { Ok(tokens) => { build_ast(tokens); }, Err(error) => { - eprintln!("Error scanning.\n{} at pos {}", error.reason, error.position) + let chars: Vec = input.chars().into_iter().collect(); + eprintln!("{}", error.get_error_str(&chars)) } }