mirror of
				https://github.com/rm-dr/daisy
				synced 2025-11-04 04:31:50 -08:00 
			
		
		
		
	Improved error formatting
This commit is contained in:
		@@ -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),
 | 
			
		||||
								)?;
 | 
			
		||||
							}
 | 
			
		||||
 
 | 
			
		||||
@@ -255,13 +255,39 @@ pub struct LineLocation {
 | 
			
		||||
/// If we cannot parse a string, one of these is returned.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum ParserError {
 | 
			
		||||
	MissingCloseParen,
 | 
			
		||||
	//MissingCloseParen,
 | 
			
		||||
	ExtraCloseParen,
 | 
			
		||||
	EmptyGroup,
 | 
			
		||||
	Syntax,
 | 
			
		||||
	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")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Parse a user string. This is the only method that should be used
 | 
			
		||||
/// outside this module.
 | 
			
		||||
 
 | 
			
		||||
@@ -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}
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ pub fn p_groupify(mut g: VecDeque<Token>) -> Result<Token, (LineLocation, Parser
 | 
			
		||||
		let t = g.pop_front().unwrap();
 | 
			
		||||
		let (l_now, v_now) = levels.last_mut().unwrap();
 | 
			
		||||
 | 
			
		||||
		match &t {
 | 
			
		||||
		match t {
 | 
			
		||||
			Token::PreOperator(_, _) => {
 | 
			
		||||
				v_now.push_back(t);
 | 
			
		||||
				lookback(v_now)?;
 | 
			
		||||
@@ -82,23 +82,25 @@ pub fn p_groupify(mut g: VecDeque<Token>) -> Result<Token, (LineLocation, Parser
 | 
			
		||||
			Token::PreNumber(l, s) => {
 | 
			
		||||
				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<Token>) -> Result<Token, (LineLocation, Parser
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	// Error on missing parenthesis
 | 
			
		||||
	if levels.len() != 1 {
 | 
			
		||||
		let (l, _) = levels.pop().unwrap();
 | 
			
		||||
		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();
 | 
			
		||||
	return Ok(Token::PreGroup(LineLocation{pos:0, len:0}, v));
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,6 @@ pub fn p_tokenize(input: &String) -> VecDeque<Token> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	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<Token> {
 | 
			
		||||
 | 
			
		||||
			// 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<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;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user