mirror of https://github.com/rm-dr/daisy
Added formattedtext and better error class
parent
8076990a41
commit
e67a5c4696
|
@ -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,
|
||||
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<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(());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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<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;
|
||||
|
|
|
@ -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"},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
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<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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()));
|
||||
|
|
Loading…
Reference in New Issue