From e67a5c4696d70d2a4a3e59178293a8c8bf91db7b Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 3 Aug 2023 22:13:38 -0700 Subject: [PATCH] Added formattedtext and better error class --- src/command/mod.rs | 281 ++++++++++++++--------------------- src/entry/unix/unix.rs | 216 ++++++++++----------------- src/errors.rs | 95 ++++++++++++ src/evaluate/evaluate.rs | 16 +- src/evaluate/function.rs | 62 ++++---- src/evaluate/mod.rs | 48 +----- src/evaluate/operator.rs | 113 ++++++-------- src/formattedtext.rs | 104 +++++++++++++ src/main.rs | 2 + src/parser/mod.rs | 12 +- src/parser/parsererror.rs | 32 ---- src/parser/stage/groupify.rs | 19 +-- src/parser/stage/treeify.rs | 48 +++--- src/parser/token.rs | 6 +- 14 files changed, 522 insertions(+), 532 deletions(-) create mode 100644 src/errors.rs create mode 100644 src/formattedtext.rs delete mode 100644 src/parser/parsererror.rs diff --git a/src/command/mod.rs b/src/command/mod.rs index e6a1ee3..e16aaee 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,15 +1,7 @@ -use std::io::Write; use crate::context::Context; use crate::parser::Constant; use crate::parser::substitute; - -use termion::{ - raw::RawTerminal, - color, - style, - clear, - cursor -}; +use crate::formattedtext::FormattedText; pub fn is_command( s: &String @@ -30,159 +22,132 @@ pub fn is_command( } #[inline(always)] -fn draw_greeter(stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - write!( - stdout, - concat!( - "{a} ###### {b} @@@@@@\r\n", - "{a} # ##{b}@@ @\r\n", - "{a} ## #{b}@ @@\r\n", - "{a} {b}@@@@@@@@@@@@@{a}\r\n", - "{b} @@ @{a}# ##\r\n", - "{b} @ @@{a}## #\r\n", - "{b} @@@@@@ {a} ###### {r}\r\n", - " {t}Daisy{r} {v}v{ver}{r}\r\n", - "\n" - ), - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - a = color::Fg(color::Magenta), - b = color::Fg(color::White), - t = format!("{}{}", color::Fg(color::White), style::Bold), - v = format!("{}{}", color::Fg(color::White), style::Italic), - ver = env!("CARGO_PKG_VERSION"), - )?; - - return Ok(()); +fn greeter() -> FormattedText { + return FormattedText::new( + format!( + concat!( + "[a] ###### [n] @@@@@@\r\n", + "[a] # ##[n]@@ @\r\n", + "[a] ## #[n]@ @@\r\n", + "[a] [n]@@@@@@@@@@@@@[a]\r\n", + "[n] @@ @[a]# ##\r\n", + "[n] @ @@[a]## #\r\n", + "[n] @@@@@@ [a] ###### [n]\r\n", + " [t]Daisy[n] [i]v{ver}[n]\r\n", + "\n" + ), + ver = env!("CARGO_PKG_VERSION") + ) + ); } #[inline(always)] pub fn do_command( - stdout: &mut RawTerminal, s: &String, context: &mut Context -) -> Result<(), std::io::Error> { +) -> FormattedText { let args: Vec<&str> = s.split(" ").collect(); let first = args[0]; match first { "help" => { - draw_greeter(stdout)?; + let mut t = greeter(); - write!(stdout, + t.push( concat!( "Daisy is a high-precision, general-purpose\r\n", "scientific calculator.\r\n", "\n", " - Use Up/Down arrows to navigate history.\r\n", " - Use Ctrl-C or Ctrl-D to quit.\r\n", - " - Use {c}ans{r} to reference the last result.\r\n", - " - Use {c}var = 1337{r} to define varibles.\r\n", + " - Use [c]ans[n] to reference the last result.\r\n", + " - Use [c]var = 1337[n] to define varibles.\r\n", "\n", - "╞═══════════════ {t}Commands{r} ═══════════════╡\r\n", - " {c}help{r} Show this help\r\n", - " {c}clear{r} Clear the terminal\r\n", - " {c}quit{r} Exit daisy\r\n", - //" {c}units{r} List available units\r\n", - " {c}consts{r} List built-in constants\r\n", - " {c}ops{r} List built-in operators\r\n", - " {c}fns{r} List built-in functions\r\n", - " {c}vars{r} List user-defined variables\r\n", - " {c}del{r} Delete a variable\r\n", + "╞═══════════════ [t]Commands[n] ═══════════════╡\r\n", + " [c]help[n] Show this help\r\n", + " [c]clear[n] Clear the terminal\r\n", + " [c]quit[n] Exit daisy\r\n", + //" [c]units[n] List available units\r\n", + " [c]consts[n] List built-in constants\r\n", + " [c]ops[n] List built-in operators\r\n", + " [c]fns[n] List built-in functions\r\n", + " [c]vars[n] List user-defined variables\r\n", + " [c]del[n] Delete a variable\r\n", "\n\n", - ), + ) + ); - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic), - t = format!("{}{}", color::Fg(color::Magenta), style::Bold) - )?; + return t; }, "clear" => { - write!( - stdout, - "{}{}", - clear::All, - cursor::Goto(1, 1) - )?; + return FormattedText::new("[clear]".to_string()); }, "ops" | "operators" => { - write!(stdout, + return FormattedText::new( concat!( "\r\n", "Operators, sorted by priority (high to low).\r\n", "High-piority operators are applied first.\r\n\n", - "╞═════ {t}Operator{r} ═════╪═════ {t}Syntax{r} ═════╡\r\n", - " function {c}sin, cos, etc{r}\r\n", - " factorial {c}!{r}\r\n", - " powers {c}^, **{r}\r\n", - " implicit multiply {c}3π, 3(2+1), etc{r}\r\n", - " square root {c}sqrt, rt, √{r}\r\n", - " negate {c}-3, -(1 + 2){r}\r\n", - " modulo (short) {c}%{r}\r\n", - " multiply, divide {c}*, /, ×, ÷{r}\r\n", - " add, subtract {c}+, -{r}\r\n", - " unit conversion {c}to{r}\r\n", - " division (long) {c}per{r}\r\n", - " modulo (long) {c}mod{r}\r\n", + "╞═════ [t]Operator[n] ═════╪═════ [t]Syntax[n] ═════╡\r\n", + " function [c]sin, cos, etc[n]\r\n", + " factorial [c]![n]\r\n", + " powers [c]^, **[n]\r\n", + " implicit multiply [c]3π, 3(2+1), etc[n]\r\n", + " square root [c]sqrt, rt, √[n]\r\n", + " negate [c]-3, -(1 + 2)[n]\r\n", + " modulo (short) [c]%[n]\r\n", + " multiply, divide [c]*, /, ×, ÷[n]\r\n", + " add, subtract [c]+, -[n]\r\n", + " unit conversion [c]to[n]\r\n", + " division (long) [c]per[n]\r\n", + " modulo (long) [c]mod[n]\r\n", "\n\n" - ), - - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic), - t = format!("{}{}", color::Fg(color::Magenta), style::Bold) - )?; + ).to_string() + ); }, "fns" | "functions" => { - write!(stdout, + return FormattedText::new( concat!( - "\r\n╞═══════ {t}Function{r} ═══════╪══════ {t}Syntax{r} ══════╡\r\n", - " absolute value {c}abs{r}\r\n", - " floor, ceiling, round {c}floor, ceil, round{r}\r\n", - " log base e {c}ln{r}\r\n", - " log base 10 {c}log{r}\r\n", - " sin, arcsin, cosecant {c}sin, asin, csc{r}\r\n", - " cos, arccos, secant {c}cos, acos, secant{r}\r\n", - " tan, arctan, cotan {c}tan, atan, cot{r}\r\n", - " hyperbolic sin, etc {c}sinh, asinh, csch{r}\r\n", - " hyperbolic cos, etc {c}cosh, acosh, sech{r}\r\n", - " hyperbolic tan, etc {c}tanh, atanh, coth{r}\r\n", + "\r\n╞═══════ [t]Function[n] ═══════╪══════ [t]Syntax[n] ══════╡\r\n", + " absolute value [c]abs[n]\r\n", + " floor, ceiling, round [c]floor, ceil, round[n]\r\n", + " log base e [c]ln[n]\r\n", + " log base 10 [c]log[n]\r\n", + " sin, arcsin, cosecant [c]sin, asin, csc[n]\r\n", + " cos, arccos, secant [c]cos, acos, secant[n]\r\n", + " tan, arctan, cotan [c]tan, atan, cot[n]\r\n", + " hyperbolic sin, etc [c]sinh, asinh, csch[n]\r\n", + " hyperbolic cos, etc [c]cosh, acosh, sech[n]\r\n", + " hyperbolic tan, etc [c]tanh, atanh, coth[n]\r\n", "\n", - " Celsius to Kelvin {c}fromC, fromCelsius{r}\r\n", - " Kelvin to Celsius {c}toC, toCelsius{r}\r\n", - " Fahrenheit to Kelvin {c}fromF, fromFahrenheit{r}\r\n", - " Kelvin to Fahrenheit {c}toF, toFahrenheit{r}\r\n", + " Celsius to Kelvin [c]fromC, fromCelsius[n]\r\n", + " Kelvin to Celsius [c]toC, toCelsius[n]\r\n", + " Fahrenheit to Kelvin [c]fromF, fromFahrenheit[n]\r\n", + " Kelvin to Fahrenheit [c]toF, toFahrenheit[n]\r\n", "\n", - " convert to base unit {c}tobase{r}\r\n", - " remove units {c}nounit{r}\r\n", + " convert to base unit [c]tobase[n]\r\n", + " remove units [c]nounit[n]\r\n", "\n\n" - ), - - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic), - t = format!("{}{}", color::Fg(color::Magenta), style::Bold) - )?; + ).to_string() + ); }, "vars" => { let v = context.get_variables(); if v.len() == 0 { - write!(stdout, - "You have not defined any variables.\r\n\n", - )?; - return Ok(()); + return FormattedText::new( + "You have not defined any variables\r\n\n".to_string() + ); } - write!(stdout, - "\r\n╞═══ {t}User-Defined Variables{r} ═══╡\r\n", - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - t = format!("{}{}", color::Fg(color::Magenta), style::Bold) - )?; - - + let mut t = FormattedText::new( + "\r\n╞═══ [t]User-Defined Variables[n] ═══╡\r\n".to_string() + ); let mut longest = 0; for (key, _) in v { @@ -194,30 +159,23 @@ pub fn do_command( for (key, value) in v { let padding = " ".repeat(longest - key.len()); - write!(stdout, - concat!( - " {k}{p} = {c}{v}{r}\r\n", - ), - k = key, v = value.to_string(), - p = padding, - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic), - )?; + t.push(&format!( + " {key}{padding} = [c]{v}[n]\r\n", + v = value.to_string(), + )); } - write!(stdout, - "\r\n\n", - )?; + t.push("\r\n\n"); + return t; }, "consts" | "constants" => { let a = Constant::all_consts(); - write!(stdout, - "\r\n╞═══ {t}Built-in Constants{r} ═══╡\r\n", - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - t = format!("{}{}", color::Fg(color::Magenta), style::Bold) - )?; + let mut t = FormattedText::new( + "\r\n╞═══ [t]Built-in Constants[n] ═══╡\r\n".to_string() + ); + for c in a { let Some(p) = c.pretty_name() else { continue }; @@ -226,66 +184,43 @@ pub fn do_command( // your padding length is too short. let padding = " ".repeat(25 - p.chars().count()); - write!(stdout, - " {n}{p}: {c}{s}{r}", - p = padding, - n = p, + t.push(&format!( + " {p}{padding}: [c]{s}[n]", s = c.source_strings().join(", "), + )); - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic), - )?; - - write!(stdout, "\r\n")?; + t.push(&"\n"); } - write!(stdout, - "\r\n\n", - )?; + t.push(&"\n\n"); + return t; }, "del" | "delete" => { if args.len() != 2 { - write!(stdout, - "{c}{cmd}{r} {t}takes exactly two arguments.{r}\r\n\n", - cmd = first, - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - t = format!("{}{}", color::Fg(color::Red), style::Bold), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic) - )?; - return Ok(()); + return FormattedText::new( + format!( + "[c]{first}[n] [t]takes exactly two arguments.[n]\r\n\n", + ) + ); } let v = args[1].to_string(); let v = substitute(&v); let r = context.delete_variable(&v); - match r { - Ok(()) => { - /*write!(stdout, - "Deleted variable {c}{v}{r}\r\n\n", - v = v, - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic) - )?;*/ - }, - + return match r { + Ok(()) => { FormattedText::new("".to_string()) }, Err(()) => { - write!(stdout, - "{c}{v}{r} {t}isn't a variable.{r}\r\n\n", - v = v, - r = format!("{}{}", color::Fg(color::Reset), style::Reset), - t = format!("{}{}", color::Fg(color::Red), style::Bold), - c = format!("{}{}", color::Fg(color::LightBlack), style::Italic) - )?; + FormattedText::new( + format!( + "[c]{v}[n] [t]isn't a variable.[n]\r\n\n", + ) + ) } - } - - return Ok(()); + }; }, _ => unreachable!("Bad command!") }; - - return Ok(()); } diff --git a/src/entry/unix/unix.rs b/src/entry/unix/unix.rs index f9d9263..677b666 100644 --- a/src/entry/unix/unix.rs +++ b/src/entry/unix/unix.rs @@ -13,10 +13,13 @@ use termion::{ }; use super::promptbuffer::PromptBuffer; +use crate::errors::DaisyError; +use crate::formattedtext::FormattedText; use crate::parser; use crate::command; use crate::evaluate; use crate::context::Context; +use crate::parser::LineLocation; use crate::parser::substitute; @@ -25,38 +28,21 @@ fn do_expression( stdout: &mut RawTerminal, s: &String, context: &mut Context -) -> Result { +) -> Result { + + // Parse string #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g = parser::parse(&s, context); + let g = parser::parse(&s, context)?; #[cfg(debug_assertions)] RawTerminal::activate_raw_mode(&stdout).unwrap(); - // Check for parse errors - if let Err((l, e)) = g { - write!( - stdout, "{}{}{}{}{}{}\r\n", - color::Fg(color::Red), - style::Bold, - " ".repeat(l.pos + 4), - "^".repeat(l.len), - color::Fg(color::Reset), - style::Reset, - ).unwrap(); - - write!( - stdout, " {}{}Parse Error: {}{}{}\r\n\n", - style::Bold, - color::Fg(color::Red), - style::Reset, - e.to_string(), - color::Fg(color::Reset), - ).unwrap(); - - return Err(()); - } - - let Ok(g) = g else {panic!()}; + // Evaluate expression + #[cfg(debug_assertions)] + RawTerminal::suspend_raw_mode(&stdout).unwrap(); + let g_evaluated = evaluate::evaluate(&g, context, false)?; + #[cfg(debug_assertions)] + RawTerminal::activate_raw_mode(&stdout).unwrap(); // Display parsed string write!( @@ -66,29 +52,20 @@ fn do_expression( g.to_string() ).unwrap(); - // Evaluate expression - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g_evaluated = evaluate::evaluate(&g, context, false); - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - // Show output - if let Ok(q) = g_evaluated { + // Display result write!( stdout, "\n {}{}={} {}{}\r\n\n", style::Bold, color::Fg(color::Green), style::Reset, - q.to_string_outer(), + g_evaluated.to_string_outer(), color::Fg(color::Reset) ).unwrap(); - return Ok(q); - } else { - match g_evaluated { - Ok(_) => unreachable!(), + return Ok(g_evaluated); + +/* Err((l, e)) => { // Display user input let s = substitute(&s); @@ -99,7 +76,6 @@ fn do_expression( s ).unwrap(); - write!( stdout, "{}{}{}{}{}{}\r\n", color::Fg(color::Red), @@ -111,7 +87,7 @@ fn do_expression( ).unwrap(); write!( - stdout, " {}{}Evaluation Error: {}{}{}\r\n\n", + stdout, " {}{}Error: {}{}{}\r\n\n", style::Bold, color::Fg(color::Red), style::Reset, @@ -121,8 +97,7 @@ fn do_expression( } } } - - return Err(()); + */ } @@ -131,58 +106,35 @@ fn do_assignment( stdout: &mut RawTerminal, s: &String, context: &mut Context -) { +) -> Result<(), (LineLocation, DaisyError)> { let parts = s.split("=").collect::>(); if parts.len() != 2 { - write!( - stdout, " {}{}Parse Error: {}Syntax{}\r\n\n", - style::Bold, - color::Fg(color::Red), - style::Reset, - color::Fg(color::Reset), - ).unwrap(); - return; + return Err(( + LineLocation::new_zero(), + DaisyError::Syntax + )); } - let offset = parts[0].chars().count() + 1; + //let offset = parts[0].chars().count() + 1; let left = parts[0].trim().to_string(); let right = parts[1].trim().to_string(); let right = substitute(&right); let left = substitute(&left); + + if !context.valid_varible(&left) { + return Err(( + LineLocation::new_zero(), + DaisyError::Syntax + )); + } #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g = parser::parse(&right, context); + let g = parser::parse(&right, context)?; #[cfg(debug_assertions)] RawTerminal::activate_raw_mode(&stdout).unwrap(); - // Check for parse errors - if let Err((l, e)) = g { - write!( - stdout, "{}{}{}{}{}{}\r\n", - color::Fg(color::Red), - style::Bold, - " ".repeat(l.pos + offset + 4), - "^".repeat(l.len), - color::Fg(color::Reset), - style::Reset, - ).unwrap(); - - write!( - stdout, " {}{}Parse Error: {}{}{}\r\n\n", - style::Bold, - color::Fg(color::Red), - style::Reset, - e.to_string(), - color::Fg(color::Reset), - ).unwrap(); - - return; - } - - let Ok(g) = g else {panic!()}; - // Display parsed string write!( stdout, " {}{}=>{}{} {left} = {}\r\n\n", @@ -194,61 +146,15 @@ fn do_assignment( // Evaluate expression #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g_evaluated = evaluate::evaluate(&g, context, true); + let g_evaluated = evaluate::evaluate(&g, context, false)?; #[cfg(debug_assertions)] RawTerminal::activate_raw_mode(&stdout).unwrap(); - // Show output - if let Ok(q) = g_evaluated { - let r = context.push_var(left.to_string(), q); - if r.is_err() { - write!( - stdout, " {}{}Definition failed: {}bad variable name{}\r\n\n", - style::Bold, - color::Fg(color::Red), - style::Reset, - color::Fg(color::Reset), - ).unwrap(); - } - return; - - } else { - match g_evaluated { - Ok(_) => unreachable!(), - - Err((l, e)) => { - // Display user input - let s = substitute(&s); - write!( - stdout, "\n{}{}==>{}{} {}\r\n", - style::Bold, color::Fg(color::Red), - style::Reset, color::Fg(color::Reset), - s - ).unwrap(); - - - write!( - stdout, "{}{}{}{}{}{}\r\n", - color::Fg(color::Red), - style::Bold, - " ".repeat(l.pos + offset + 4), - "^".repeat(l.len), - color::Fg(color::Reset), - style::Reset, - ).unwrap(); - - write!( - stdout, " {}{}Evaluation Error: {}{}{}\r\n\n", - style::Bold, - color::Fg(color::Red), - style::Reset, - e.to_string(), - color::Fg(color::Reset), - ).unwrap(); - } - } - } + context.push_var(left.to_string(), g_evaluated).unwrap(); + return Ok(()); } + + #[inline(always)] pub fn main() -> Result<(), std::io::Error> { let mut stdout = stdout().into_raw_mode().unwrap(); @@ -259,7 +165,8 @@ pub fn main() -> Result<(), std::io::Error> { // Handle command-line arguments let args: Vec = env::args().collect(); if args.iter().any(|s| s == "--help") { - command::do_command(&mut stdout, &String::from("help"), &mut context)?; + let t = command::do_command(&String::from("help"), &mut context); + t.write(&mut stdout)?; return Ok(()); } else if args.iter().any(|s| s == "--version") { write!(stdout, "Daisy v{}\r\n", env!("CARGO_PKG_VERSION"))?; @@ -288,12 +195,47 @@ pub fn main() -> Result<(), std::io::Error> { if in_str.trim() == "quit" { break 'outer; } else if command::is_command(&in_str) { - command::do_command(&mut stdout, &in_str, &mut context)?; + let t = command::do_command(&in_str, &mut context); + t.write(&mut stdout)?; } else if in_str.contains("=") { - do_assignment(&mut stdout, &in_str, &mut context); + let r = do_assignment(&mut stdout, &in_str, &mut context); + if let Err((l, e)) = r { + + let t = FormattedText::new( + format!( + concat!( + "{}[e]{}[n]\n", + " {}\n" + ), + " ".repeat(l.pos + 4), + "^".repeat(l.len), + e.text().to_string(), + ) + ); + + t.write(&mut stdout).unwrap(); + } } else { let r = do_expression(&mut stdout, &in_str, &mut context); - if let Ok(t) = r { context.push_hist(t); } + if let Ok(t) = r { + context.push_hist(t); + } else { + let Err((l, e)) = r else { unreachable!() }; + + let t = FormattedText::new( + format!( + concat!( + "{}[e]{}[n]\n", + " {}\n" + ), + " ".repeat(l.pos + 4), + "^".repeat(l.len), + e.text().to_string(), + ) + ); + + t.write(&mut stdout).unwrap(); + } } break; diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..418847c --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,95 @@ +use crate::formattedtext::FormattedText; + +#[derive(Debug)] +pub enum DaisyError { + + // Parser errors + //MissingCloseParen, + ExtraCloseParen, + EmptyGroup, + Syntax, + BadNumber, + + // Evaluation errors + BadMath, + TooBig, + ZeroDivision, + IncompatibleUnit, + IncompatibleUnits(String, String), + Undefined(String), + EvaluationError, + BadArguments(String, usize, usize) +} + +impl DaisyError { + pub fn text(&self) -> FormattedText { + match self { + //DaisyError::MissingCloseParen => { + // String::from("Missing close parenthesis") + //}, + DaisyError::ExtraCloseParen => { + return FormattedText::new( + "[e]Syntax Error:[n] Extra close parenthesis".to_string() + ); + }, + DaisyError::EmptyGroup => { + return FormattedText::new( + "[e]Syntax Error:[n] Groups can't be empty".to_string() + ); + }, + DaisyError::Syntax => { + return FormattedText::new( + "[e]Syntax Error[n]".to_string() + ); + }, + DaisyError::BadNumber => { + return FormattedText::new( + "[e]Syntax Error:[n] Invalid number".to_string() + ); + } + + DaisyError::BadMath => { + return FormattedText::new( + "[e]Evaluation Error:[n] Failed to evaluate expression".to_string() + ); + }, + DaisyError::TooBig => { + return FormattedText::new( + "[e]Evaluation Error:[n] Number too big".to_string() + ); + + }, + DaisyError::ZeroDivision => { + return FormattedText::new( + "[e]Evaluation Error:[n] Division by zero".to_string() + ); + }, + DaisyError::IncompatibleUnit => { + return FormattedText::new( + "[e]Evaluation Error:[n] Incompatible unit".to_string() + ); + }, + DaisyError::IncompatibleUnits(a, b) => { + return FormattedText::new(format!( + "[e]Evaluation Error:[n] Incompatible units ([c]{a}[n] and [c]{b}[n])" + )); + }, + DaisyError::Undefined(s) => { + return FormattedText::new(format!( + "[e]Evaluation Error:[n] [c]{s}[n] is not defined" + )); + }, + DaisyError::EvaluationError => { + return FormattedText::new( + "[e]Evaluation Error:[n] Could not evaluate".to_string() + ); + }, + DaisyError::BadArguments(s, want, got) => { + return FormattedText::new(format!( + "[e]Evaluation Error:[n] [c]{s}[n] takes {want} argument{}, got {got}", + if *want == 1 {""} else {"s"}, + )); + } + } + } +} diff --git a/src/evaluate/evaluate.rs b/src/evaluate/evaluate.rs index df50812..670cf80 100644 --- a/src/evaluate/evaluate.rs +++ b/src/evaluate/evaluate.rs @@ -2,12 +2,20 @@ use crate::parser::Expression; use crate::parser::Operator; use crate::context::Context; use crate::parser::LineLocation; +use crate::errors::DaisyError; use super::operator::eval_operator; use super::function::eval_function; -use super::EvalError; -pub fn evaluate(t: &Expression, context: &mut Context, allow_incomplete: bool) -> Result { + +pub fn evaluate( + t: &Expression, + context: &mut Context, + allow_incomplete: bool +) -> Result< + Expression, + (LineLocation, DaisyError) +> { // Keeps track of our position in the expression tree. // For example, the coordinates [0, 2, 1] are interpreted as follows: @@ -54,7 +62,7 @@ pub fn evaluate(t: &Expression, context: &mut Context, allow_incomplete: bool) - // Error if variable is undefined. // Comment this to allow floating varables. - if v.is_none() { return Err((*l, EvalError::Undefined(s.clone()))); } + if v.is_none() { return Err((*l, DaisyError::Undefined(s.clone()))); } v }, @@ -76,7 +84,7 @@ pub fn evaluate(t: &Expression, context: &mut Context, allow_incomplete: bool) - if let Expression::Quantity(_, _) = g {} else { let l = g.get_linelocation(); - return Err((l, EvalError::EvaluationError)) + return Err((l, DaisyError::EvaluationError)) } } diff --git a/src/evaluate/function.rs b/src/evaluate/function.rs index 1255b4a..005523d 100644 --- a/src/evaluate/function.rs +++ b/src/evaluate/function.rs @@ -6,7 +6,7 @@ use crate::quantity::FreeUnit; use crate::quantity::WholeUnit; use crate::quantity::Quantity; use crate::quantity::Scalar; -use super::EvalError; +use crate::errors::DaisyError; // If unitless, do nothing @@ -26,7 +26,7 @@ fn to_radians(q: Quantity) -> Result { -pub fn eval_function(g: &Expression) -> Result { +pub fn eval_function(g: &Expression) -> Result { let Expression::Operator(loc, Operator::Function(f), args) = g else {unreachable!()}; @@ -37,7 +37,7 @@ pub fn eval_function(g: &Expression) -> Result Result { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.abs())); }, Function::Floor => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.floor())); }, Function::Ceil => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.ceil())); }, Function::Round => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.round())); }, Function::NaturalLog => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.ln())); }, Function::TenLog => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.log10())); }, Function::Sin => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.sin())); }, Function::Cos => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.cos())); }, Function::Tan => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.tan())); }, Function::Csc => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.csc())); }, Function::Sec => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.sec())); }, Function::Cot => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.cot())); }, Function::Sinh => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.sinh())); }, Function::Cosh => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.cosh())); }, Function::Tanh => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.tanh())); }, Function::Csch => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.csch())); }, Function::Sech => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.sech())); }, Function::Coth => { - let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, EvalError::IncompatibleUnit)); }; + let Ok(q) = to_radians(q.clone()) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)); }; return Ok(Expression::Quantity(*loc + *l, q.coth())); }, Function::Asin => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.asin())); }, Function::Acos => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.acos())); }, Function::Atan => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.atan())); }, Function::Asinh => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.asinh())); }, Function::Acosh => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.acosh())); }, Function::Atanh => { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} return Ok(Expression::Quantity(*loc + *l, q.atanh())); }, @@ -155,7 +155,7 @@ pub fn eval_function(g: &Expression) -> Result { let mut k = Quantity::new_rational(1f64).unwrap(); k.insert_unit(FreeUnit::from_whole(WholeUnit::Kelvin), Scalar::new_rational(1f64).unwrap()); - let Some(q) = q.convert_to(k) else { return Err((*loc + *l, EvalError::IncompatibleUnit)) }; + let Some(q) = q.convert_to(k) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)) }; let mut r = q.without_unit(); r += Quantity::new_rational(-273.15f64).unwrap(); @@ -165,7 +165,7 @@ pub fn eval_function(g: &Expression) -> Result { let mut k = Quantity::new_rational(1f64).unwrap(); k.insert_unit(FreeUnit::from_whole(WholeUnit::Kelvin), Scalar::new_rational(1f64).unwrap()); - let Some(q) = q.convert_to(k) else { return Err((*loc + *l, EvalError::IncompatibleUnit)) }; + let Some(q) = q.convert_to(k) else { return Err((*loc + *l, DaisyError::IncompatibleUnit)) }; let mut r = q.without_unit(); r *= Quantity::new_rational_from_frac(9i64, 5i64).unwrap(); @@ -175,7 +175,7 @@ pub fn eval_function(g: &Expression) -> Result { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} let mut r = Quantity::new_rational(273.15f64).unwrap(); r += q.clone(); @@ -184,7 +184,7 @@ pub fn eval_function(g: &Expression) -> Result { - if !q.unitless() { return Err((*loc + *l, EvalError::IncompatibleUnit));} + if !q.unitless() { return Err((*loc + *l, DaisyError::IncompatibleUnit));} let mut r = q.clone(); r += Quantity::new_rational(459.67).unwrap(); diff --git a/src/evaluate/mod.rs b/src/evaluate/mod.rs index aad128e..52eb871 100644 --- a/src/evaluate/mod.rs +++ b/src/evaluate/mod.rs @@ -2,50 +2,4 @@ mod operator; mod function; mod evaluate; -pub use self::evaluate::evaluate; - -#[derive(Debug)] -pub enum EvalError { - BadMath, - TooBig, - ZeroDivision, - IncompatibleUnit, - IncompatibleUnits(String, String), - Undefined(String), - EvaluationError, - BadArguments(String, usize, usize) -} - - -impl ToString for EvalError { - fn to_string(&self) -> String { - match self { - EvalError::BadMath => { - String::from("Failed to evaluate expression") - }, - EvalError::TooBig => { - String::from("Number too big") - }, - EvalError::ZeroDivision => { - String::from("Division by zero") - }, - EvalError::IncompatibleUnit => { - String::from("Incompatible unit") - }, - EvalError::IncompatibleUnits(a, b) => { - format!("Incompatible units ({a} and {b})") - }, - EvalError::Undefined(s) => { - format!("{s} is undefined") - }, - EvalError::EvaluationError => { - String::from("Could not evaluate") - }, - EvalError::BadArguments(s, want, got) => { - format!("{s} takes {want} argument{}, got {got}", - if *want == 1 {""} else {"s"}, - ) - } - } - } -} +pub use self::evaluate::evaluate; \ No newline at end of file diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index d127a0a..c389139 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -3,11 +3,11 @@ use crate::parser::LineLocation; use crate::quantity::Quantity; use crate::parser::Operator; use crate::parser::Expression; -use super::EvalError; use crate::context::Context; +use crate::errors::DaisyError; -pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result, (LineLocation, EvalError)> { +pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result, (LineLocation, DaisyError)> { let Expression::Operator(op_loc, op, args) = g else {panic!()}; @@ -49,44 +49,26 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result { - let mut sum: Quantity; - let mut loc: LineLocation; - if let Expression::Quantity(l, s) = &args[0] { - sum = s.clone(); - loc = *l; - } else { return Ok(None); }; + if args.len() != 2 { panic!() }; + let a = &args[0]; + let b = &args[1]; - - // Flag that is set to true if we find incompatible units. - // We don't stop right away because we need to add all linelocations - // to show a pretty error. - let mut incompatible_units = false; - - let mut i: usize = 1; - while i < args.len() { - let j = &args[i]; - if let Expression::Quantity(l, v) = j { - - if !sum.unit.compatible_with(&v.unit) { - incompatible_units = true; + if let Expression::Quantity(la, a) = a { + if let Expression::Quantity(lb, b) = b { + if !a.unit.compatible_with(&b.unit) { + return Err(( + *la + *lb + *op_loc, + DaisyError::IncompatibleUnits( + a.convert_to_base().unit.to_string(), + b.convert_to_base().unit.to_string() + ) + )); } - - if !incompatible_units { sum += v.clone(); } - loc += *l; - } else { - if incompatible_units { - return Err((loc + *op_loc, EvalError::IncompatibleUnit)); - } - return Ok(None); + return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() + b.clone()))); } - i += 1; } - if incompatible_units { - return Err((loc + *op_loc, EvalError::IncompatibleUnit)); - } - - return Ok(Some(Expression::Quantity(loc + *op_loc, sum))); + return Ok(None); }, Operator::Subtract => { @@ -96,6 +78,15 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result Result Result { - let mut prod: Quantity; - let mut loc: LineLocation; - if let Expression::Quantity(l, s) = &args[0] { - prod = s.clone(); - loc = *l; - } else { return Ok(None); }; + if args.len() != 2 { panic!() }; + let a = &args[0]; + let b = &args[1]; - let mut i: usize = 1; - while i < args.len() { - let j = &args[i]; - if let Expression::Quantity(l, v) = j { - prod *= v.clone(); - loc += *l; - } else { return Ok(None); } - i += 1; + if let Expression::Quantity(la, a) = a { + if let Expression::Quantity(lb, b) = b { + return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() * b.clone()))); + } } - return Ok(Some(Expression::Quantity(loc + *op_loc, prod))); + + return Ok(None); }, Operator::ModuloLong @@ -151,12 +136,12 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result Result Result Result Result &Quantity::new_rational(50_000f64).unwrap() { return Err((*l + *op_loc, EvalError::TooBig)); } + if !v.fract().is_zero() { return Err((*l + *op_loc, DaisyError::BadMath)); } + if v > &Quantity::new_rational(50_000f64).unwrap() { return Err((*l + *op_loc, DaisyError::TooBig)); } let mut prod = Quantity::new_rational(1f64).unwrap(); let mut u = v.clone(); diff --git a/src/formattedtext.rs b/src/formattedtext.rs new file mode 100644 index 0000000..b59b4b7 --- /dev/null +++ b/src/formattedtext.rs @@ -0,0 +1,104 @@ +use std::io::Write; +use termion::raw::RawTerminal; +use termion::color; +use termion::style; +use termion::clear; +use termion::cursor; +use std::ops::Add; + + +#[derive(Debug)] +#[derive(Clone)] +pub struct FormattedText { + text: String +} + +impl ToString for FormattedText { + fn to_string(&self) -> String { return self.text.clone(); } +} + + +impl FormattedText { + + pub fn new(s: String) -> FormattedText { + return FormattedText { + text: s + } + } + + pub fn push(&mut self, s: &str) { + self.text.push_str(s); + } + + + pub fn write(&self, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { + + if self.text == "[clear]" { + write!( + stdout, + "{}{}", + clear::All, + cursor::Goto(1, 1) + )?; + return Ok(()); + } + + + let mut s = String::new(); + let mut chars = self.text.chars(); + + while let Some(c) = chars.next() { + match c { + '[' => { + let a = chars.next().unwrap(); + + // Handle double [[ as escaped [ + if a == '[' { s.push('['); } + + let b = chars.next().unwrap(); + + match (a, b) { + ('n', ']') => { // Normal text + s.push_str(&format!("{}{}", color::Fg(color::Reset), style::Reset)); + }, + ('i', ']') => { // Normal italic text + s.push_str(&format!("{}{}", color::Fg(color::Reset), style::Italic)); + }, + ('t', ']') => { // Title text + s.push_str(&format!("{}{}", color::Fg(color::Magenta), style::Bold)); + }, + ('a', ']') => { // Colored text + s.push_str(&format!("{}{}", color::Fg(color::Magenta), style::Reset)); + }, + ('e', ']') => { // Error titles + s.push_str(&format!("{}{}", color::Fg(color::Red), style::Bold)); + }, + ('c', ']') => { // Console text + s.push_str(&format!("{}{}", color::Fg(color::LightBlack), style::Italic)); + }, + + _ => { + s.push('['); + s.push(a); + s.push(b); + } + } + }, + '\n' => { s.push_str("\r\n") }, + _ => s.push(c) + } + } + + write!(stdout, "{}", s)?; + return Ok(()); + } +} + + +impl Add for FormattedText { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + return FormattedText::new(format!("{}{}", self.text, other.text)); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9830ace..1ccd622 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ pub mod command; pub mod quantity; pub mod evaluate; pub mod context; +pub mod errors; +pub mod formattedtext; mod entry; use crate::entry::main_e; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index bb342a8..5b49b39 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,14 +1,10 @@ mod stage; mod token; -mod parsererror; mod expression; mod linelocation; -use self::{ - token::Token, - parsererror::ParserError, -}; +use self::token::Token; pub use self::{ expression::Expression, @@ -19,11 +15,11 @@ pub use self::{ }; use crate::context::Context; - +use crate::errors::DaisyError; pub fn parse( s: &String, context: &Context -) -> Result { +) -> Result { let expressions = stage::tokenize(s); let (_, expressions) = stage::find_subs(expressions); @@ -33,7 +29,7 @@ pub fn parse( return Ok(g); } -pub fn parse_no_context(s: &String) -> Result { +pub fn parse_no_context(s: &String) -> Result { parse(s, &Context::new()) } diff --git a/src/parser/parsererror.rs b/src/parser/parsererror.rs deleted file mode 100644 index 15639d2..0000000 --- a/src/parser/parsererror.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// Types of parser errors. -/// If we cannot parse a string, one of these is returned. -#[derive(Debug)] -pub enum ParserError { - //MissingCloseParen, - ExtraCloseParen, - EmptyGroup, - Syntax, - BadNumber -} - -impl ToString for ParserError { - fn to_string(&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::BadNumber => { - String::from("Invalid number") - } - } - } -} diff --git a/src/parser/stage/groupify.rs b/src/parser/stage/groupify.rs index 47908cb..f88c40d 100644 --- a/src/parser/stage/groupify.rs +++ b/src/parser/stage/groupify.rs @@ -3,14 +3,15 @@ use std::collections::VecDeque; use super::super::{ Token, LineLocation, - ParserError, Operator }; +use crate::errors::DaisyError; + fn lookback_signs( g: &mut VecDeque -) -> Result<(), (LineLocation, ParserError)> { +) -> Result<(), (LineLocation, DaisyError)> { // Convert `-` operators to `neg` operators // Delete `+`s that mean "positive" instead of "add" @@ -99,7 +100,7 @@ fn lookback_signs( // Inserts implicit operators fn lookback( g: &mut VecDeque -) -> Result<(), (LineLocation, ParserError)> { +) -> Result<(), (LineLocation, DaisyError)> { lookback_signs(g)?; @@ -178,7 +179,7 @@ fn lookback( // The following are syntax errors (Token::Quantity(la,_), Token::Quantity(lb,_)) => { - return Err((*la + *lb, ParserError::Syntax)); + return Err((*la + *lb, DaisyError::Syntax)); }, _ => {g.insert(i-1, b); g.insert(i-1, a);} } @@ -195,7 +196,7 @@ pub fn groupify( mut g: VecDeque ) -> Result< Token, - (LineLocation, ParserError) + (LineLocation, DaisyError) > { let last_linelocation: LineLocation = *g.back().unwrap().get_line_location(); @@ -220,8 +221,8 @@ pub fn groupify( Token::GroupEnd(l) => { let l = *l_now + l; - if i_level == 0 { return Err((l, ParserError::ExtraCloseParen)) } - if v_now.len() == 0 { return Err((l, ParserError::EmptyGroup)) } + if i_level == 0 { return Err((l, DaisyError::ExtraCloseParen)) } + if v_now.len() == 0 { return Err((l, DaisyError::EmptyGroup)) } i_level -= 1; @@ -243,7 +244,7 @@ pub fn groupify( // Error on missing parenthesis if levels.len() != 1 { let (l, _) = levels.pop().unwrap(); - return Err((l, ParserError::MissingCloseParen)) + return Err((l, DaisyError::MissingCloseParen)) } */ @@ -252,7 +253,7 @@ pub fn groupify( let (l, mut v) = levels.pop().unwrap(); let (_, v_now) = levels.last_mut().unwrap(); - if v.len() == 0 { return Err((l, ParserError::EmptyGroup)) } + if v.len() == 0 { return Err((l, DaisyError::EmptyGroup)) } lookback(&mut v)?; v_now.push_back(Token::Group(l, v)); diff --git a/src/parser/stage/treeify.rs b/src/parser/stage/treeify.rs index 37698a5..5e12955 100644 --- a/src/parser/stage/treeify.rs +++ b/src/parser/stage/treeify.rs @@ -1,9 +1,9 @@ use std::collections::VecDeque; use crate::context::Context; +use crate::errors::DaisyError; use super::super::{ Token, - ParserError, LineLocation, Expression, Operator @@ -13,7 +13,7 @@ fn treeify_binary( i: usize, g_inner: &mut VecDeque, context: &Context -) -> Result { +) -> Result { let this: &Token = &g_inner[i]; @@ -23,7 +23,7 @@ fn treeify_binary( Token::Operator(l, _) => l, _ => panic!() }; - return Err((*l, ParserError::Syntax)); // left argument is empty + return Err((*l, DaisyError::Syntax)); // left argument is empty } @@ -35,7 +35,7 @@ fn treeify_binary( Token::Operator(l, _) => l, _ => panic!() }; - return Err((*l, ParserError::Syntax)); // Left argument is empty + return Err((*l, DaisyError::Syntax)); // Left argument is empty } }; @@ -47,7 +47,7 @@ fn treeify_binary( Token::Operator(l, _) => l, _ => panic!() }; - return Err((*l, ParserError::Syntax)); // right argument is empty + return Err((*l, DaisyError::Syntax)); // right argument is empty } }; @@ -57,7 +57,7 @@ fn treeify_binary( if let Token::Operator(l, s) = left { let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string let o = o.unwrap(); if { @@ -67,13 +67,13 @@ fn treeify_binary( return Ok(false); } else { let tl = *this.get_line_location() + *l; - return Err((tl, ParserError::Syntax)); // left argument isn't valid + return Err((tl, DaisyError::Syntax)); // left argument isn't valid } } if let Token::Operator(l, s) = right { let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string let o = o.unwrap(); if { @@ -83,7 +83,7 @@ fn treeify_binary( return Ok(false); } else { let tl = *this.get_line_location() + *l; - return Err((tl, ParserError::Syntax)); // right argument isn't valid (two operators next to each other) + return Err((tl, DaisyError::Syntax)); // right argument isn't valid (two operators next to each other) } } @@ -92,7 +92,7 @@ fn treeify_binary( let this_op = { let Token::Operator(l, s) = this else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // bad operator string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // bad operator string o.unwrap() }; @@ -100,14 +100,14 @@ fn treeify_binary( let left_op = if i > 1 { let Token::Operator(l, s) = &g_inner[i-2] else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad operator string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad operator string Some(o.unwrap()) } else { None }; let right_op = if i < g_inner.len()-2 { let Token::Operator(l, s) = &g_inner[i+2] else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad operator string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad operator string Some(o.unwrap()) } else { None }; @@ -159,7 +159,7 @@ fn treeify_unary( g_inner: &mut VecDeque, left_associative: bool, context: &Context -) -> Result { +) -> Result { let this: &Token = &g_inner[i]; let next: &Token; @@ -172,7 +172,7 @@ fn treeify_unary( Token::Operator(l, _) => l, _ => panic!() }; - return Err((*l, ParserError::Syntax)); // argument is missing + return Err((*l, DaisyError::Syntax)); // argument is missing } }; } else { @@ -184,7 +184,7 @@ fn treeify_unary( Token::Operator(l, _) => l, _ => panic!() }; - return Err((*l, ParserError::Syntax)); // argument is missing + return Err((*l, DaisyError::Syntax)); // argument is missing } }; } @@ -204,7 +204,7 @@ fn treeify_unary( // Previous operator is invalid return Err(( *this.get_line_location(), - ParserError::Syntax + DaisyError::Syntax )); } } @@ -212,14 +212,14 @@ fn treeify_unary( if let Token::Operator(l, _) = next { let tl = *this.get_line_location() + *l; // Argument is invalid - return Err((tl, ParserError::Syntax)); + return Err((tl, DaisyError::Syntax)); } else { // This operator let this_op = { let Token::Operator(l, s) = this else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string o.unwrap() }; @@ -228,14 +228,14 @@ fn treeify_unary( if i > 1 { let Token::Operator(l, s) = &g_inner[i-2] else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string Some(o.unwrap()) } else { None } } else { if i < g_inner.len()-2 { let Token::Operator(l, s) = &g_inner[i+2] else {panic!()}; let o = Operator::from_string(s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } // Bad string + if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string Some(o.unwrap()) } else { None } }; @@ -285,7 +285,7 @@ fn treeify_unary( pub fn treeify( mut g: Token, context: &Context -) -> Result { +) -> Result { let (l, g_inner): (LineLocation, &mut VecDeque) = match g { Token::Group(l, ref mut x) => (l, x), _ => panic!() @@ -293,7 +293,7 @@ pub fn treeify( if g_inner.len() == 0 { // This shouldn't ever happen. - return Err((l, ParserError::EmptyGroup)); + return Err((l, DaisyError::EmptyGroup)); } let mut left_associative = true; @@ -315,7 +315,7 @@ pub fn treeify( let this_op = match &g_inner[i] { Token::Operator(l, s) => { let o = Operator::from_string(&s); - if o.is_none() { return Err((*l, ParserError::Syntax)); } + if o.is_none() { return Err((*l, DaisyError::Syntax)); } o.unwrap() }, _ => { @@ -356,7 +356,7 @@ pub fn treeify( return match g { // Catch edge cases Token::Operator(l, _) => { - Err((l, ParserError::Syntax)) + Err((l, DaisyError::Syntax)) }, Token::Group(_,_) => { treeify(g, context) diff --git a/src/parser/token.rs b/src/parser/token.rs index f119b03..ba6c62f 100644 --- a/src/parser/token.rs +++ b/src/parser/token.rs @@ -2,10 +2,10 @@ use std::collections::VecDeque; use crate::quantity::Unit; use crate::quantity::Quantity; use crate::context::Context; +use crate::errors::DaisyError; use super::{ LineLocation, - ParserError, Expression, Constant }; @@ -57,7 +57,7 @@ impl Token { } #[inline(always)] - pub fn to_expression(self, context: &Context) -> Result{ + pub fn to_expression(self, context: &Context) -> Result{ match self { Token::Quantity(l, mut s) => { @@ -71,7 +71,7 @@ impl Token { let r = Quantity::new_rational_from_string(&s); if r.is_none() { - return Err((l, ParserError::BadNumber)) + return Err((l, DaisyError::BadNumber)) } return Ok(Expression::Quantity(l, r.unwrap()));