From 39c52782ca2eb541fd14c493832ae1aadf43ae18 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 25 Mar 2023 20:47:33 -0700 Subject: [PATCH] Improved error formatting --- src/main.rs | 3 ++- src/parser.rs | 30 ++++++++++++++++++++++++++++-- src/parser/find_subs.rs | 10 +++++++++- src/parser/groupify.rs | 34 +++++++++++++++++++++++++++------- src/parser/tokenize.rs | 7 +++---- 5 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 38f7cbe..85245d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -129,10 +129,11 @@ fn main() -> Result<(), std::io::Error> { }, Err((l, e)) => { write!( - stdout, "{}{}{} {e:?}{}\r\n", + stdout, "{}{}{} {}{}\r\n", color::Fg(color::Red), " ".repeat(l.pos + 4), "^".repeat(l.len), + e.to_message(), color::Fg(color::Reset), )?; } diff --git a/src/parser.rs b/src/parser.rs index e3e59bc..34a35ab 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -255,11 +255,37 @@ pub struct LineLocation { /// If we cannot parse a string, one of these is returned. #[derive(Debug)] pub enum ParserError { - MissingCloseParen, + //MissingCloseParen, ExtraCloseParen, EmptyGroup, Syntax, - BadNumber + Undefined(String), + BadNumber +} + +impl ParserError { + pub fn to_message(&self) -> String { + match self { + //ParserError::MissingCloseParen => { + // String::from("This group is never closed") + //}, + ParserError::ExtraCloseParen => { + String::from("Extra close parenthesis") + }, + ParserError::EmptyGroup => { + String::from("Groups can't be empty") + }, + ParserError::Syntax => { + String::from("Syntax") + }, + ParserError::Undefined(s) => { + format!("\"{s}\" isn't defined") + }, + ParserError::BadNumber => { + String::from("Invalid number") + } + } + } } diff --git a/src/parser/find_subs.rs b/src/parser/find_subs.rs index 06ad1a8..ac2d9f4 100644 --- a/src/parser/find_subs.rs +++ b/src/parser/find_subs.rs @@ -34,7 +34,7 @@ pub fn p_find_subs( }, Token::PreWord(_, s) => { - match &s[..] { + let target = match &s[..] { // Greek letters "alpha" => {Some("α")}, "beta" => {Some("β")}, @@ -62,7 +62,15 @@ pub fn p_find_subs( "omega" => {Some("ω")} _ => {None} + }; + + // Update preword contents too. + // This makes sure future prints of this token + // contain substituted text too. + if target.is_some() {* + s = String::from(target.unwrap()); } + target }, _ => {None} diff --git a/src/parser/groupify.rs b/src/parser/groupify.rs index 45058a4..279ccce 100644 --- a/src/parser/groupify.rs +++ b/src/parser/groupify.rs @@ -73,7 +73,7 @@ pub fn p_groupify(mut g: VecDeque) -> Result { v_now.push_back(t); lookback(v_now)?; @@ -82,23 +82,25 @@ pub fn p_groupify(mut g: VecDeque) -> Result { let n = match s.parse() { Ok(n) => n, - Err(_) => return Err((*l, ParserError::BadNumber)) + Err(_) => return Err((l, ParserError::BadNumber)) }; - v_now.push_back(Token::Number(*l, n)); + v_now.push_back(Token::Number(l, n)); lookback(v_now)?; }, Token::PreWord(l, s) => { + // This method must support both plain text and + // unicode versions of each word. v_now.push_back(match &s[..] { - "mod" => { Token::PreOperator(*l, Operator::ModuloLong) }, - "pi" => { Token::Constant(*l, 3.141592653, String::from("π")) }, - _ => { return Err((*l, ParserError::Syntax)); } + "mod" => { Token::PreOperator(l, Operator::ModuloLong) }, + "π"|"pi" => { Token::Constant(l, 3.141592653, String::from("π")) }, + _ => { return Err((l, ParserError::Undefined(s))); } }); lookback(v_now)?; }, Token::PreGroupStart(l) => { - levels.push((*l, VecDeque::with_capacity(8))); + levels.push((l, VecDeque::with_capacity(8))); i_level += 1; }, @@ -129,10 +131,28 @@ pub fn p_groupify(mut g: VecDeque) -> Result VecDeque { for (i, c) in input.chars().enumerate() { - match c { // The minus sign can be both a Negative and an Operator. // Needs special treatment. @@ -82,8 +81,8 @@ pub fn p_tokenize(input: &String) -> VecDeque { // Operator // Always one character - '*'|'/'|'+'| - '^'|'!'|'%' + '*'|'×'|'/'|'÷'| + '+'|'^'|'!'|'%' => { if t.is_some() { g.push_back(update_line_location(t.unwrap(), i)); } t = Some(Token::PreOperator( @@ -136,7 +135,7 @@ pub fn p_tokenize(input: &String) -> VecDeque { }; } - if t.is_some() { g.push_back(update_line_location(t.unwrap(), input.len())); } + if t.is_some() { g.push_back(update_line_location(t.unwrap(), input.chars().count())); } return g; } \ No newline at end of file