Added formattedtext and better error class

pull/2/head
Mark 2023-08-03 22:13:38 -07:00
parent 8076990a41
commit e67a5c4696
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
14 changed files with 522 additions and 532 deletions

View File

@ -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<std::io::Stdout>) -> Result<(), std::io::Error> {
write!(
stdout,
fn greeter() -> FormattedText {
return FormattedText::new(
format!(
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",
"[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"
),
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(());
ver = env!("CARGO_PKG_VERSION")
)
);
}
#[inline(always)]
pub fn do_command(
stdout: &mut RawTerminal<std::io::Stdout>,
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(());
}

View File

@ -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<std::io::Stdout>,
s: &String,
context: &mut Context
) -> Result<parser::Expression, ()> {
) -> Result<parser::Expression, (LineLocation, DaisyError)> {
// 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<std::io::Stdout>,
s: &String,
context: &mut Context
) {
) -> Result<(), (LineLocation, DaisyError)> {
let parts = s.split("=").collect::<Vec<&str>>();
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();
context.push_var(left.to_string(), g_evaluated).unwrap();
return Ok(());
}
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();
}
}
}
}
#[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<String> = 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;

95
src/errors.rs Normal file
View File

@ -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"},
));
}
}
}
}

View File

@ -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<Expression, (LineLocation, EvalError)> {
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))
}
}

View File

@ -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<Quantity, ()> {
pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalError)> {
pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, DaisyError)> {
let Expression::Operator(loc, Operator::Function(f), args) = g else {unreachable!()};
@ -37,7 +37,7 @@ pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalEr
if let Expression::Tuple(l, v) = a {
return Err((
*l + *loc,
EvalError::BadArguments(f.to_string(), 1, v.len())
DaisyError::BadArguments(f.to_string(), 1, v.len())
))
};
@ -51,102 +51,102 @@ pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalEr
Function::Abs => {
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<Expression, (LineLocation, EvalEr
Function::ToCelsius => {
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<Expression, (LineLocation, EvalEr
Function::ToFahrenheit => {
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<Expression, (LineLocation, EvalEr
return Ok(Expression::Quantity(*loc + *l, r));
},
Function::FromCelsius => {
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<Expression, (LineLocation, EvalEr
return Ok(Expression::Quantity(*loc + *l, r));
},
Function::FromFahrenheit => {
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();

View File

@ -3,49 +3,3 @@ 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"},
)
}
}
}
}

View File

@ -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<Option<Expression>, (LineLocation, EvalError)> {
pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Expression>, (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<Option<Ex
},
Operator::Add => {
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()
)
));
}
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() + b.clone())));
}
}
if !incompatible_units { sum += v.clone(); }
loc += *l;
} else {
if incompatible_units {
return Err((loc + *op_loc, EvalError::IncompatibleUnit));
}
return Ok(None);
}
i += 1;
}
if incompatible_units {
return Err((loc + *op_loc, EvalError::IncompatibleUnit));
}
return Ok(Some(Expression::Quantity(loc + *op_loc, sum)));
},
Operator::Subtract => {
@ -96,6 +78,15 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
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()
)
));
}
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() - b.clone())));
}
}
@ -112,7 +103,7 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
if let Expression::Quantity(la, a) = a {
if let Expression::Quantity(lb, b) = b {
if b.is_zero() { return Err((*la + *lb + *op_loc, EvalError::ZeroDivision)); }
if b.is_zero() { return Err((*la + *lb + *op_loc, DaisyError::ZeroDivision)); }
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() / b.clone())));
}
}
@ -122,23 +113,17 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
Operator::ImplicitMultiply |
Operator::Multiply => {
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<Option<Ex
if let Expression::Quantity(lb, vb) = b {
if !(va.unitless() && vb.unitless()) {
return Err((*la + *lb + *op_loc, EvalError::IncompatibleUnit));
return Err((*la + *lb + *op_loc, DaisyError::IncompatibleUnit));
}
if vb <= &Quantity::new_rational(1f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
if vb <= &Quantity::new_rational(1f64).unwrap() { return Err((*la + *lb + *op_loc, DaisyError::BadMath)); }
if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, DaisyError::BadMath)); }
if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, DaisyError::BadMath)); }
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, va.clone() % vb.clone())));
} else { return Ok(None); }
@ -174,7 +159,7 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
if n.is_none() {
return Err((
*la + *lb + *op_loc,
EvalError::IncompatibleUnits(
DaisyError::IncompatibleUnits(
va.convert_to_base().unit.to_string(),
vb.convert_to_base().unit.to_string()
)
@ -191,9 +176,9 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
let a = &args[0];
if let Expression::Quantity(l, v) = a {
if v.is_negative() { return Err((*l + *op_loc, EvalError::BadMath)); }
if v.is_negative() { return Err((*l + *op_loc, DaisyError::BadMath)); }
let p = v.pow(Quantity::new_rational_from_string("0.5").unwrap());
if p.is_nan() {return Err((*l + *op_loc, EvalError::BadMath));}
if p.is_nan() {return Err((*l + *op_loc, DaisyError::BadMath));}
return Ok(Some(Expression::Quantity(*l, p)));
} else { return Ok(None); }
},
@ -207,15 +192,15 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
if let Expression::Quantity(lb, vb) = b {
if !vb.unitless() {
return Err((*lb, EvalError::IncompatibleUnit));
return Err((*lb, DaisyError::IncompatibleUnit));
}
if va.is_zero() && vb.is_negative() {
return Err((*la + *lb + *op_loc, EvalError::ZeroDivision));
return Err((*la + *lb + *op_loc, DaisyError::ZeroDivision));
}
let p = va.pow(vb.clone());
if p.is_nan() {return Err((*la + *lb + *op_loc, EvalError::BadMath));}
if p.is_nan() {return Err((*la + *lb + *op_loc, DaisyError::BadMath));}
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, p)));
} else { return Ok(None); }
} else { return Ok(None); }
@ -228,11 +213,11 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
if let Expression::Quantity(l, v) = args {
if !v.unitless() {
return Err((*l + *op_loc, EvalError::IncompatibleUnit));
return Err((*l + *op_loc, DaisyError::IncompatibleUnit));
}
if !v.fract().is_zero() { return Err((*l + *op_loc, EvalError::BadMath)); }
if v > &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();

104
src/formattedtext.rs Normal file
View File

@ -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<std::io::Stdout>) -> 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));
}
}

View File

@ -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;

View File

@ -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<Expression, (LineLocation, ParserError)> {
) -> Result<Expression, (LineLocation, DaisyError)> {
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<Expression, (LineLocation, ParserError)> {
pub fn parse_no_context(s: &String) -> Result<Expression, (LineLocation, DaisyError)> {
parse(s, &Context::new())
}

View File

@ -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")
}
}
}
}

View File

@ -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<Token>
) -> 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<Token>
) -> 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<Token>
) -> 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));

View File

@ -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<Token>,
context: &Context
) -> Result<bool, (LineLocation, ParserError)> {
) -> Result<bool, (LineLocation, DaisyError)> {
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<Token>,
left_associative: bool,
context: &Context
) -> Result<bool, (LineLocation, ParserError)> {
) -> Result<bool, (LineLocation, DaisyError)> {
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<Expression, (LineLocation, ParserError)> {
) -> Result<Expression, (LineLocation, DaisyError)> {
let (l, g_inner): (LineLocation, &mut VecDeque<Token>) = 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)

View File

@ -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<Expression, (LineLocation, ParserError)>{
pub fn to_expression(self, context: &Context) -> Result<Expression, (LineLocation, DaisyError)>{
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()));