Improved error formatting

pull/2/head
Mark 2023-03-25 20:47:33 -07:00
parent aca8fa072d
commit 39c52782ca
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
5 changed files with 69 additions and 15 deletions

View File

@ -129,10 +129,11 @@ fn main() -> Result<(), std::io::Error> {
}, },
Err((l, e)) => { Err((l, e)) => {
write!( write!(
stdout, "{}{}{} {e:?}{}\r\n", stdout, "{}{}{} {}{}\r\n",
color::Fg(color::Red), color::Fg(color::Red),
" ".repeat(l.pos + 4), " ".repeat(l.pos + 4),
"^".repeat(l.len), "^".repeat(l.len),
e.to_message(),
color::Fg(color::Reset), color::Fg(color::Reset),
)?; )?;
} }

View File

@ -255,13 +255,39 @@ pub struct LineLocation {
/// If we cannot parse a string, one of these is returned. /// If we cannot parse a string, one of these is returned.
#[derive(Debug)] #[derive(Debug)]
pub enum ParserError { pub enum ParserError {
MissingCloseParen, //MissingCloseParen,
ExtraCloseParen, ExtraCloseParen,
EmptyGroup, EmptyGroup,
Syntax, Syntax,
Undefined(String),
BadNumber 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")
}
}
}
}
/// Parse a user string. This is the only method that should be used /// Parse a user string. This is the only method that should be used
/// outside this module. /// outside this module.

View File

@ -34,7 +34,7 @@ pub fn p_find_subs(
}, },
Token::PreWord(_, s) => { Token::PreWord(_, s) => {
match &s[..] { let target = match &s[..] {
// Greek letters // Greek letters
"alpha" => {Some("α")}, "alpha" => {Some("α")},
"beta" => {Some("β")}, "beta" => {Some("β")},
@ -62,7 +62,15 @@ pub fn p_find_subs(
"omega" => {Some("ω")} "omega" => {Some("ω")}
_ => {None} _ => {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} _ => {None}

View File

@ -73,7 +73,7 @@ pub fn p_groupify(mut g: VecDeque<Token>) -> Result<Token, (LineLocation, Parser
let t = g.pop_front().unwrap(); let t = g.pop_front().unwrap();
let (l_now, v_now) = levels.last_mut().unwrap(); let (l_now, v_now) = levels.last_mut().unwrap();
match &t { match t {
Token::PreOperator(_, _) => { Token::PreOperator(_, _) => {
v_now.push_back(t); v_now.push_back(t);
lookback(v_now)?; lookback(v_now)?;
@ -82,23 +82,25 @@ pub fn p_groupify(mut g: VecDeque<Token>) -> Result<Token, (LineLocation, Parser
Token::PreNumber(l, s) => { Token::PreNumber(l, s) => {
let n = match s.parse() { let n = match s.parse() {
Ok(n) => n, 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)?; lookback(v_now)?;
}, },
Token::PreWord(l, s) => { Token::PreWord(l, s) => {
// This method must support both plain text and
// unicode versions of each word.
v_now.push_back(match &s[..] { v_now.push_back(match &s[..] {
"mod" => { Token::PreOperator(*l, Operator::ModuloLong) }, "mod" => { Token::PreOperator(l, Operator::ModuloLong) },
"pi" => { Token::Constant(*l, 3.141592653, String::from("π")) }, "π"|"pi" => { Token::Constant(l, 3.141592653, String::from("π")) },
_ => { return Err((*l, ParserError::Syntax)); } _ => { return Err((l, ParserError::Undefined(s))); }
}); });
lookback(v_now)?; lookback(v_now)?;
}, },
Token::PreGroupStart(l) => { Token::PreGroupStart(l) => {
levels.push((*l, VecDeque::with_capacity(8))); levels.push((l, VecDeque::with_capacity(8)));
i_level += 1; i_level += 1;
}, },
@ -129,10 +131,28 @@ pub fn p_groupify(mut g: VecDeque<Token>) -> Result<Token, (LineLocation, Parser
} }
} }
/*
// Error on missing parenthesis
if levels.len() != 1 { if levels.len() != 1 {
let (l, _) = levels.pop().unwrap(); let (l, _) = levels.pop().unwrap();
return Err((l, ParserError::MissingCloseParen)) return Err((l, ParserError::MissingCloseParen))
} }
*/
// Auto-close parenthesis
while levels.len() != 1 {
let (l, v) = levels.pop().unwrap();
let (_, v_now) = levels.last_mut().unwrap();
// Catch empty groups
if v.len() == 0 {
return Err((l, ParserError::EmptyGroup))
}
v_now.push_back(Token::PreGroup(l, v));
lookback(v_now)?;
}
let (_, v) = levels.pop().unwrap(); let (_, v) = levels.pop().unwrap();
return Ok(Token::PreGroup(LineLocation{pos:0, len:0}, v)); return Ok(Token::PreGroup(LineLocation{pos:0, len:0}, v));

View File

@ -33,7 +33,6 @@ pub fn p_tokenize(input: &String) -> VecDeque<Token> {
for (i, c) in input.chars().enumerate() { for (i, c) in input.chars().enumerate() {
match c { match c {
// The minus sign can be both a Negative and an Operator. // The minus sign can be both a Negative and an Operator.
// Needs special treatment. // Needs special treatment.
@ -82,8 +81,8 @@ pub fn p_tokenize(input: &String) -> VecDeque<Token> {
// Operator // Operator
// Always one character // Always one character
'*'|'/'|'+'| '*'|'×'|'/'|'|
'^'|'!'|'%' '+'|'^'|'!'|'%'
=> { => {
if t.is_some() { g.push_back(update_line_location(t.unwrap(), i)); } if t.is_some() { g.push_back(update_line_location(t.unwrap(), i)); }
t = Some(Token::PreOperator( t = Some(Token::PreOperator(
@ -136,7 +135,7 @@ pub fn p_tokenize(input: &String) -> VecDeque<Token> {
}; };
} }
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; return g;
} }