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::context::Context;
|
||||||
use crate::parser::Constant;
|
use crate::parser::Constant;
|
||||||
use crate::parser::substitute;
|
use crate::parser::substitute;
|
||||||
|
use crate::formattedtext::FormattedText;
|
||||||
use termion::{
|
|
||||||
raw::RawTerminal,
|
|
||||||
color,
|
|
||||||
style,
|
|
||||||
clear,
|
|
||||||
cursor
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn is_command(
|
pub fn is_command(
|
||||||
s: &String
|
s: &String
|
||||||
|
@ -30,159 +22,132 @@ pub fn is_command(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn draw_greeter(stdout: &mut RawTerminal<std::io::Stdout>) -> Result<(), std::io::Error> {
|
fn greeter() -> FormattedText {
|
||||||
write!(
|
return FormattedText::new(
|
||||||
stdout,
|
format!(
|
||||||
concat!(
|
concat!(
|
||||||
"{a} ###### {b} @@@@@@\r\n",
|
"[a] ###### [n] @@@@@@\r\n",
|
||||||
"{a} # ##{b}@@ @\r\n",
|
"[a] # ##[n]@@ @\r\n",
|
||||||
"{a} ## #{b}@ @@\r\n",
|
"[a] ## #[n]@ @@\r\n",
|
||||||
"{a} {b}@@@@@@@@@@@@@{a}\r\n",
|
"[a] [n]@@@@@@@@@@@@@[a]\r\n",
|
||||||
"{b} @@ @{a}# ##\r\n",
|
"[n] @@ @[a]# ##\r\n",
|
||||||
"{b} @ @@{a}## #\r\n",
|
"[n] @ @@[a]## #\r\n",
|
||||||
"{b} @@@@@@ {a} ###### {r}\r\n",
|
"[n] @@@@@@ [a] ###### [n]\r\n",
|
||||||
" {t}Daisy{r} {v}v{ver}{r}\r\n",
|
" [t]Daisy[n] [i]v{ver}[n]\r\n",
|
||||||
"\n"
|
"\n"
|
||||||
),
|
),
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
ver = env!("CARGO_PKG_VERSION")
|
||||||
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(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn do_command(
|
pub fn do_command(
|
||||||
stdout: &mut RawTerminal<std::io::Stdout>,
|
|
||||||
s: &String,
|
s: &String,
|
||||||
context: &mut Context
|
context: &mut Context
|
||||||
) -> Result<(), std::io::Error> {
|
) -> FormattedText {
|
||||||
let args: Vec<&str> = s.split(" ").collect();
|
let args: Vec<&str> = s.split(" ").collect();
|
||||||
let first = args[0];
|
let first = args[0];
|
||||||
|
|
||||||
match first {
|
match first {
|
||||||
"help" => {
|
"help" => {
|
||||||
draw_greeter(stdout)?;
|
let mut t = greeter();
|
||||||
|
|
||||||
write!(stdout,
|
t.push(
|
||||||
concat!(
|
concat!(
|
||||||
"Daisy is a high-precision, general-purpose\r\n",
|
"Daisy is a high-precision, general-purpose\r\n",
|
||||||
"scientific calculator.\r\n",
|
"scientific calculator.\r\n",
|
||||||
"\n",
|
"\n",
|
||||||
" - Use Up/Down arrows to navigate history.\r\n",
|
" - Use Up/Down arrows to navigate history.\r\n",
|
||||||
" - Use Ctrl-C or Ctrl-D to quit.\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]ans[n] to reference the last result.\r\n",
|
||||||
" - Use {c}var = 1337{r} to define varibles.\r\n",
|
" - Use [c]var = 1337[n] to define varibles.\r\n",
|
||||||
"\n",
|
"\n",
|
||||||
"╞═══════════════ {t}Commands{r} ═══════════════╡\r\n",
|
"╞═══════════════ [t]Commands[n] ═══════════════╡\r\n",
|
||||||
" {c}help{r} Show this help\r\n",
|
" [c]help[n] Show this help\r\n",
|
||||||
" {c}clear{r} Clear the terminal\r\n",
|
" [c]clear[n] Clear the terminal\r\n",
|
||||||
" {c}quit{r} Exit daisy\r\n",
|
" [c]quit[n] Exit daisy\r\n",
|
||||||
//" {c}units{r} List available units\r\n",
|
//" [c]units[n] List available units\r\n",
|
||||||
" {c}consts{r} List built-in constants\r\n",
|
" [c]consts[n] List built-in constants\r\n",
|
||||||
" {c}ops{r} List built-in operators\r\n",
|
" [c]ops[n] List built-in operators\r\n",
|
||||||
" {c}fns{r} List built-in functions\r\n",
|
" [c]fns[n] List built-in functions\r\n",
|
||||||
" {c}vars{r} List user-defined variables\r\n",
|
" [c]vars[n] List user-defined variables\r\n",
|
||||||
" {c}del{r} Delete a variable\r\n",
|
" [c]del[n] Delete a variable\r\n",
|
||||||
"\n\n",
|
"\n\n",
|
||||||
),
|
)
|
||||||
|
);
|
||||||
|
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
return t;
|
||||||
c = format!("{}{}", color::Fg(color::LightBlack), style::Italic),
|
|
||||||
t = format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"clear" => {
|
"clear" => {
|
||||||
write!(
|
return FormattedText::new("[clear]".to_string());
|
||||||
stdout,
|
|
||||||
"{}{}",
|
|
||||||
clear::All,
|
|
||||||
cursor::Goto(1, 1)
|
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ops" | "operators" => {
|
"ops" | "operators" => {
|
||||||
write!(stdout,
|
return FormattedText::new(
|
||||||
concat!(
|
concat!(
|
||||||
"\r\n",
|
"\r\n",
|
||||||
"Operators, sorted by priority (high to low).\r\n",
|
"Operators, sorted by priority (high to low).\r\n",
|
||||||
"High-piority operators are applied first.\r\n\n",
|
"High-piority operators are applied first.\r\n\n",
|
||||||
"╞═════ {t}Operator{r} ═════╪═════ {t}Syntax{r} ═════╡\r\n",
|
"╞═════ [t]Operator[n] ═════╪═════ [t]Syntax[n] ═════╡\r\n",
|
||||||
" function {c}sin, cos, etc{r}\r\n",
|
" function [c]sin, cos, etc[n]\r\n",
|
||||||
" factorial {c}!{r}\r\n",
|
" factorial [c]![n]\r\n",
|
||||||
" powers {c}^, **{r}\r\n",
|
" powers [c]^, **[n]\r\n",
|
||||||
" implicit multiply {c}3π, 3(2+1), etc{r}\r\n",
|
" implicit multiply [c]3π, 3(2+1), etc[n]\r\n",
|
||||||
" square root {c}sqrt, rt, √{r}\r\n",
|
" square root [c]sqrt, rt, √[n]\r\n",
|
||||||
" negate {c}-3, -(1 + 2){r}\r\n",
|
" negate [c]-3, -(1 + 2)[n]\r\n",
|
||||||
" modulo (short) {c}%{r}\r\n",
|
" modulo (short) [c]%[n]\r\n",
|
||||||
" multiply, divide {c}*, /, ×, ÷{r}\r\n",
|
" multiply, divide [c]*, /, ×, ÷[n]\r\n",
|
||||||
" add, subtract {c}+, -{r}\r\n",
|
" add, subtract [c]+, -[n]\r\n",
|
||||||
" unit conversion {c}to{r}\r\n",
|
" unit conversion [c]to[n]\r\n",
|
||||||
" division (long) {c}per{r}\r\n",
|
" division (long) [c]per[n]\r\n",
|
||||||
" modulo (long) {c}mod{r}\r\n",
|
" modulo (long) [c]mod[n]\r\n",
|
||||||
"\n\n"
|
"\n\n"
|
||||||
),
|
).to_string()
|
||||||
|
);
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
|
||||||
c = format!("{}{}", color::Fg(color::LightBlack), style::Italic),
|
|
||||||
t = format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"fns" | "functions" => {
|
"fns" | "functions" => {
|
||||||
write!(stdout,
|
return FormattedText::new(
|
||||||
concat!(
|
concat!(
|
||||||
"\r\n╞═══════ {t}Function{r} ═══════╪══════ {t}Syntax{r} ══════╡\r\n",
|
"\r\n╞═══════ [t]Function[n] ═══════╪══════ [t]Syntax[n] ══════╡\r\n",
|
||||||
" absolute value {c}abs{r}\r\n",
|
" absolute value [c]abs[n]\r\n",
|
||||||
" floor, ceiling, round {c}floor, ceil, round{r}\r\n",
|
" floor, ceiling, round [c]floor, ceil, round[n]\r\n",
|
||||||
" log base e {c}ln{r}\r\n",
|
" log base e [c]ln[n]\r\n",
|
||||||
" log base 10 {c}log{r}\r\n",
|
" log base 10 [c]log[n]\r\n",
|
||||||
" sin, arcsin, cosecant {c}sin, asin, csc{r}\r\n",
|
" sin, arcsin, cosecant [c]sin, asin, csc[n]\r\n",
|
||||||
" cos, arccos, secant {c}cos, acos, secant{r}\r\n",
|
" cos, arccos, secant [c]cos, acos, secant[n]\r\n",
|
||||||
" tan, arctan, cotan {c}tan, atan, cot{r}\r\n",
|
" tan, arctan, cotan [c]tan, atan, cot[n]\r\n",
|
||||||
" hyperbolic sin, etc {c}sinh, asinh, csch{r}\r\n",
|
" hyperbolic sin, etc [c]sinh, asinh, csch[n]\r\n",
|
||||||
" hyperbolic cos, etc {c}cosh, acosh, sech{r}\r\n",
|
" hyperbolic cos, etc [c]cosh, acosh, sech[n]\r\n",
|
||||||
" hyperbolic tan, etc {c}tanh, atanh, coth{r}\r\n",
|
" hyperbolic tan, etc [c]tanh, atanh, coth[n]\r\n",
|
||||||
"\n",
|
"\n",
|
||||||
" Celsius to Kelvin {c}fromC, fromCelsius{r}\r\n",
|
" Celsius to Kelvin [c]fromC, fromCelsius[n]\r\n",
|
||||||
" Kelvin to Celsius {c}toC, toCelsius{r}\r\n",
|
" Kelvin to Celsius [c]toC, toCelsius[n]\r\n",
|
||||||
" Fahrenheit to Kelvin {c}fromF, fromFahrenheit{r}\r\n",
|
" Fahrenheit to Kelvin [c]fromF, fromFahrenheit[n]\r\n",
|
||||||
" Kelvin to Fahrenheit {c}toF, toFahrenheit{r}\r\n",
|
" Kelvin to Fahrenheit [c]toF, toFahrenheit[n]\r\n",
|
||||||
"\n",
|
"\n",
|
||||||
" convert to base unit {c}tobase{r}\r\n",
|
" convert to base unit [c]tobase[n]\r\n",
|
||||||
" remove units {c}nounit{r}\r\n",
|
" remove units [c]nounit[n]\r\n",
|
||||||
"\n\n"
|
"\n\n"
|
||||||
),
|
).to_string()
|
||||||
|
);
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
|
||||||
c = format!("{}{}", color::Fg(color::LightBlack), style::Italic),
|
|
||||||
t = format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"vars" => {
|
"vars" => {
|
||||||
let v = context.get_variables();
|
let v = context.get_variables();
|
||||||
|
|
||||||
if v.len() == 0 {
|
if v.len() == 0 {
|
||||||
write!(stdout,
|
return FormattedText::new(
|
||||||
"You have not defined any variables.\r\n\n",
|
"You have not defined any variables\r\n\n".to_string()
|
||||||
)?;
|
);
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stdout,
|
let mut t = FormattedText::new(
|
||||||
"\r\n╞═══ {t}User-Defined Variables{r} ═══╡\r\n",
|
"\r\n╞═══ [t]User-Defined Variables[n] ═══╡\r\n".to_string()
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
);
|
||||||
t = format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let mut longest = 0;
|
let mut longest = 0;
|
||||||
for (key, _) in v {
|
for (key, _) in v {
|
||||||
|
@ -194,30 +159,23 @@ pub fn do_command(
|
||||||
for (key, value) in v {
|
for (key, value) in v {
|
||||||
let padding = " ".repeat(longest - key.len());
|
let padding = " ".repeat(longest - key.len());
|
||||||
|
|
||||||
write!(stdout,
|
t.push(&format!(
|
||||||
concat!(
|
" {key}{padding} = [c]{v}[n]\r\n",
|
||||||
" {k}{p} = {c}{v}{r}\r\n",
|
v = value.to_string(),
|
||||||
),
|
));
|
||||||
k = key, v = value.to_string(),
|
|
||||||
p = padding,
|
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
|
||||||
c = format!("{}{}", color::Fg(color::LightBlack), style::Italic),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stdout,
|
t.push("\r\n\n");
|
||||||
"\r\n\n",
|
return t;
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"consts" | "constants" => {
|
"consts" | "constants" => {
|
||||||
let a = Constant::all_consts();
|
let a = Constant::all_consts();
|
||||||
|
|
||||||
write!(stdout,
|
let mut t = FormattedText::new(
|
||||||
"\r\n╞═══ {t}Built-in Constants{r} ═══╡\r\n",
|
"\r\n╞═══ [t]Built-in Constants[n] ═══╡\r\n".to_string()
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
);
|
||||||
t = format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for c in a {
|
for c in a {
|
||||||
let Some(p) = c.pretty_name() else { continue };
|
let Some(p) = c.pretty_name() else { continue };
|
||||||
|
@ -226,66 +184,43 @@ pub fn do_command(
|
||||||
// your padding length is too short.
|
// your padding length is too short.
|
||||||
let padding = " ".repeat(25 - p.chars().count());
|
let padding = " ".repeat(25 - p.chars().count());
|
||||||
|
|
||||||
write!(stdout,
|
t.push(&format!(
|
||||||
" {n}{p}: {c}{s}{r}",
|
" {p}{padding}: [c]{s}[n]",
|
||||||
p = padding,
|
|
||||||
n = p,
|
|
||||||
s = c.source_strings().join(", "),
|
s = c.source_strings().join(", "),
|
||||||
|
));
|
||||||
|
|
||||||
r = format!("{}{}", color::Fg(color::Reset), style::Reset),
|
t.push(&"\n");
|
||||||
c = format!("{}{}", color::Fg(color::LightBlack), style::Italic),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
write!(stdout, "\r\n")?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stdout,
|
t.push(&"\n\n");
|
||||||
"\r\n\n",
|
return t;
|
||||||
)?;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"del" | "delete" => {
|
"del" | "delete" => {
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
write!(stdout,
|
return FormattedText::new(
|
||||||
"{c}{cmd}{r} {t}takes exactly two arguments.{r}\r\n\n",
|
format!(
|
||||||
cmd = first,
|
"[c]{first}[n] [t]takes exactly two arguments.[n]\r\n\n",
|
||||||
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(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let v = args[1].to_string();
|
let v = args[1].to_string();
|
||||||
let v = substitute(&v);
|
let v = substitute(&v);
|
||||||
let r = context.delete_variable(&v);
|
let r = context.delete_variable(&v);
|
||||||
|
|
||||||
match r {
|
return match r {
|
||||||
Ok(()) => {
|
Ok(()) => { FormattedText::new("".to_string()) },
|
||||||
/*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)
|
|
||||||
)?;*/
|
|
||||||
},
|
|
||||||
|
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
write!(stdout,
|
FormattedText::new(
|
||||||
"{c}{v}{r} {t}isn't a variable.{r}\r\n\n",
|
format!(
|
||||||
v = v,
|
"[c]{v}[n] [t]isn't a variable.[n]\r\n\n",
|
||||||
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(());
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => unreachable!("Bad command!")
|
_ => unreachable!("Bad command!")
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,13 @@ use termion::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::promptbuffer::PromptBuffer;
|
use super::promptbuffer::PromptBuffer;
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
use crate::formattedtext::FormattedText;
|
||||||
use crate::parser;
|
use crate::parser;
|
||||||
use crate::command;
|
use crate::command;
|
||||||
use crate::evaluate;
|
use crate::evaluate;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::parser::LineLocation;
|
||||||
use crate::parser::substitute;
|
use crate::parser::substitute;
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,38 +28,21 @@ fn do_expression(
|
||||||
stdout: &mut RawTerminal<std::io::Stdout>,
|
stdout: &mut RawTerminal<std::io::Stdout>,
|
||||||
s: &String,
|
s: &String,
|
||||||
context: &mut Context
|
context: &mut Context
|
||||||
) -> Result<parser::Expression, ()> {
|
) -> Result<parser::Expression, (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
|
// Parse string
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
||||||
let g = parser::parse(&s, context);
|
let g = parser::parse(&s, context)?;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
||||||
|
|
||||||
// Check for parse errors
|
// Evaluate expression
|
||||||
if let Err((l, e)) = g {
|
#[cfg(debug_assertions)]
|
||||||
write!(
|
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
||||||
stdout, "{}{}{}{}{}{}\r\n",
|
let g_evaluated = evaluate::evaluate(&g, context, false)?;
|
||||||
color::Fg(color::Red),
|
#[cfg(debug_assertions)]
|
||||||
style::Bold,
|
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
||||||
" ".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!()};
|
|
||||||
|
|
||||||
// Display parsed string
|
// Display parsed string
|
||||||
write!(
|
write!(
|
||||||
|
@ -66,29 +52,20 @@ fn do_expression(
|
||||||
g.to_string()
|
g.to_string()
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Evaluate expression
|
// Display result
|
||||||
#[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 {
|
|
||||||
write!(
|
write!(
|
||||||
stdout, "\n {}{}={} {}{}\r\n\n",
|
stdout, "\n {}{}={} {}{}\r\n\n",
|
||||||
style::Bold,
|
style::Bold,
|
||||||
color::Fg(color::Green),
|
color::Fg(color::Green),
|
||||||
style::Reset,
|
style::Reset,
|
||||||
q.to_string_outer(),
|
g_evaluated.to_string_outer(),
|
||||||
color::Fg(color::Reset)
|
color::Fg(color::Reset)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
return Ok(q);
|
|
||||||
|
|
||||||
} else {
|
return Ok(g_evaluated);
|
||||||
match g_evaluated {
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
Err((l, e)) => {
|
Err((l, e)) => {
|
||||||
// Display user input
|
// Display user input
|
||||||
let s = substitute(&s);
|
let s = substitute(&s);
|
||||||
|
@ -99,7 +76,6 @@ fn do_expression(
|
||||||
s
|
s
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
stdout, "{}{}{}{}{}{}\r\n",
|
stdout, "{}{}{}{}{}{}\r\n",
|
||||||
color::Fg(color::Red),
|
color::Fg(color::Red),
|
||||||
|
@ -111,7 +87,7 @@ fn do_expression(
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
stdout, " {}{}Evaluation Error: {}{}{}\r\n\n",
|
stdout, " {}{}Error: {}{}{}\r\n\n",
|
||||||
style::Bold,
|
style::Bold,
|
||||||
color::Fg(color::Red),
|
color::Fg(color::Red),
|
||||||
style::Reset,
|
style::Reset,
|
||||||
|
@ -121,8 +97,7 @@ fn do_expression(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,58 +106,35 @@ fn do_assignment(
|
||||||
stdout: &mut RawTerminal<std::io::Stdout>,
|
stdout: &mut RawTerminal<std::io::Stdout>,
|
||||||
s: &String,
|
s: &String,
|
||||||
context: &mut Context
|
context: &mut Context
|
||||||
) {
|
) -> Result<(), (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
let parts = s.split("=").collect::<Vec<&str>>();
|
let parts = s.split("=").collect::<Vec<&str>>();
|
||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
write!(
|
return Err((
|
||||||
stdout, " {}{}Parse Error: {}Syntax{}\r\n\n",
|
LineLocation::new_zero(),
|
||||||
style::Bold,
|
DaisyError::Syntax
|
||||||
color::Fg(color::Red),
|
));
|
||||||
style::Reset,
|
|
||||||
color::Fg(color::Reset),
|
|
||||||
).unwrap();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = parts[0].chars().count() + 1;
|
//let offset = parts[0].chars().count() + 1;
|
||||||
let left = parts[0].trim().to_string();
|
let left = parts[0].trim().to_string();
|
||||||
let right = parts[1].trim().to_string();
|
let right = parts[1].trim().to_string();
|
||||||
let right = substitute(&right);
|
let right = substitute(&right);
|
||||||
let left = substitute(&left);
|
let left = substitute(&left);
|
||||||
|
|
||||||
|
|
||||||
|
if !context.valid_varible(&left) {
|
||||||
|
return Err((
|
||||||
|
LineLocation::new_zero(),
|
||||||
|
DaisyError::Syntax
|
||||||
|
));
|
||||||
|
}
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
||||||
let g = parser::parse(&right, context);
|
let g = parser::parse(&right, context)?;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
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
|
// Display parsed string
|
||||||
write!(
|
write!(
|
||||||
stdout, " {}{}=>{}{} {left} = {}\r\n\n",
|
stdout, " {}{}=>{}{} {left} = {}\r\n\n",
|
||||||
|
@ -194,61 +146,15 @@ fn do_assignment(
|
||||||
// Evaluate expression
|
// Evaluate expression
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::suspend_raw_mode(&stdout).unwrap();
|
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)]
|
#[cfg(debug_assertions)]
|
||||||
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
RawTerminal::activate_raw_mode(&stdout).unwrap();
|
||||||
|
|
||||||
// Show output
|
context.push_var(left.to_string(), g_evaluated).unwrap();
|
||||||
if let Ok(q) = g_evaluated {
|
return Ok(());
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn main() -> Result<(), std::io::Error> {
|
pub fn main() -> Result<(), std::io::Error> {
|
||||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||||
|
@ -259,7 +165,8 @@ pub fn main() -> Result<(), std::io::Error> {
|
||||||
// Handle command-line arguments
|
// Handle command-line arguments
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.iter().any(|s| s == "--help") {
|
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(());
|
return Ok(());
|
||||||
} else if args.iter().any(|s| s == "--version") {
|
} else if args.iter().any(|s| s == "--version") {
|
||||||
write!(stdout, "Daisy v{}\r\n", env!("CARGO_PKG_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" {
|
if in_str.trim() == "quit" {
|
||||||
break 'outer;
|
break 'outer;
|
||||||
} else if command::is_command(&in_str) {
|
} 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("=") {
|
} 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 {
|
} else {
|
||||||
let r = do_expression(&mut stdout, &in_str, &mut context);
|
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;
|
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::parser::Operator;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::parser::LineLocation;
|
use crate::parser::LineLocation;
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
use super::operator::eval_operator;
|
use super::operator::eval_operator;
|
||||||
use super::function::eval_function;
|
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.
|
// Keeps track of our position in the expression tree.
|
||||||
// For example, the coordinates [0, 2, 1] are interpreted as follows:
|
// 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.
|
// Error if variable is undefined.
|
||||||
// Comment this to allow floating varables.
|
// 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
|
v
|
||||||
},
|
},
|
||||||
|
@ -76,7 +84,7 @@ pub fn evaluate(t: &Expression, context: &mut Context, allow_incomplete: bool) -
|
||||||
if let Expression::Quantity(_, _) = g {}
|
if let Expression::Quantity(_, _) = g {}
|
||||||
else {
|
else {
|
||||||
let l = g.get_linelocation();
|
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::WholeUnit;
|
||||||
use crate::quantity::Quantity;
|
use crate::quantity::Quantity;
|
||||||
use crate::quantity::Scalar;
|
use crate::quantity::Scalar;
|
||||||
use super::EvalError;
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
|
|
||||||
// If unitless, do nothing
|
// 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!()};
|
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 {
|
if let Expression::Tuple(l, v) = a {
|
||||||
return Err((
|
return Err((
|
||||||
*l + *loc,
|
*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 => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.abs()));
|
||||||
},
|
},
|
||||||
Function::Floor => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.floor()));
|
||||||
},
|
},
|
||||||
Function::Ceil => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.ceil()));
|
||||||
},
|
},
|
||||||
Function::Round => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.round()));
|
||||||
},
|
},
|
||||||
Function::NaturalLog => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.ln()));
|
||||||
},
|
},
|
||||||
Function::TenLog => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.log10()));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Function::Sin => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.sin()));
|
||||||
},
|
},
|
||||||
Function::Cos => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.cos()));
|
||||||
},
|
},
|
||||||
Function::Tan => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.tan()));
|
||||||
},
|
},
|
||||||
Function::Csc => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.csc()));
|
||||||
},
|
},
|
||||||
Function::Sec => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.sec()));
|
||||||
},
|
},
|
||||||
Function::Cot => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.cot()));
|
||||||
},
|
},
|
||||||
Function::Sinh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.sinh()));
|
||||||
},
|
},
|
||||||
Function::Cosh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.cosh()));
|
||||||
},
|
},
|
||||||
Function::Tanh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.tanh()));
|
||||||
},
|
},
|
||||||
Function::Csch => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.csch()));
|
||||||
},
|
},
|
||||||
Function::Sech => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.sech()));
|
||||||
},
|
},
|
||||||
Function::Coth => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.coth()));
|
||||||
},
|
},
|
||||||
Function::Asin => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.asin()));
|
||||||
},
|
},
|
||||||
Function::Acos => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.acos()));
|
||||||
},
|
},
|
||||||
Function::Atan => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.atan()));
|
||||||
},
|
},
|
||||||
Function::Asinh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.asinh()));
|
||||||
},
|
},
|
||||||
Function::Acosh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.acosh()));
|
||||||
},
|
},
|
||||||
Function::Atanh => {
|
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()));
|
return Ok(Expression::Quantity(*loc + *l, q.atanh()));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalEr
|
||||||
Function::ToCelsius => {
|
Function::ToCelsius => {
|
||||||
let mut k = Quantity::new_rational(1f64).unwrap();
|
let mut k = Quantity::new_rational(1f64).unwrap();
|
||||||
k.insert_unit(FreeUnit::from_whole(WholeUnit::Kelvin), Scalar::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();
|
let mut r = q.without_unit();
|
||||||
r += Quantity::new_rational(-273.15f64).unwrap();
|
r += Quantity::new_rational(-273.15f64).unwrap();
|
||||||
|
@ -165,7 +165,7 @@ pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalEr
|
||||||
Function::ToFahrenheit => {
|
Function::ToFahrenheit => {
|
||||||
let mut k = Quantity::new_rational(1f64).unwrap();
|
let mut k = Quantity::new_rational(1f64).unwrap();
|
||||||
k.insert_unit(FreeUnit::from_whole(WholeUnit::Kelvin), Scalar::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();
|
let mut r = q.without_unit();
|
||||||
r *= Quantity::new_rational_from_frac(9i64, 5i64).unwrap();
|
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));
|
return Ok(Expression::Quantity(*loc + *l, r));
|
||||||
},
|
},
|
||||||
Function::FromCelsius => {
|
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();
|
let mut r = Quantity::new_rational(273.15f64).unwrap();
|
||||||
r += q.clone();
|
r += q.clone();
|
||||||
|
@ -184,7 +184,7 @@ pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalEr
|
||||||
return Ok(Expression::Quantity(*loc + *l, r));
|
return Ok(Expression::Quantity(*loc + *l, r));
|
||||||
},
|
},
|
||||||
Function::FromFahrenheit => {
|
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();
|
let mut r = q.clone();
|
||||||
r += Quantity::new_rational(459.67).unwrap();
|
r += Quantity::new_rational(459.67).unwrap();
|
||||||
|
|
|
@ -3,49 +3,3 @@ mod function;
|
||||||
mod evaluate;
|
mod evaluate;
|
||||||
|
|
||||||
pub use self::evaluate::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::quantity::Quantity;
|
||||||
use crate::parser::Operator;
|
use crate::parser::Operator;
|
||||||
use crate::parser::Expression;
|
use crate::parser::Expression;
|
||||||
use super::EvalError;
|
|
||||||
use crate::context::Context;
|
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!()};
|
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 => {
|
Operator::Add => {
|
||||||
let mut sum: Quantity;
|
if args.len() != 2 { panic!() };
|
||||||
let mut loc: LineLocation;
|
let a = &args[0];
|
||||||
if let Expression::Quantity(l, s) = &args[0] {
|
let b = &args[1];
|
||||||
sum = s.clone();
|
|
||||||
loc = *l;
|
|
||||||
} else { return Ok(None); };
|
|
||||||
|
|
||||||
|
if let Expression::Quantity(la, a) = a {
|
||||||
// Flag that is set to true if we find incompatible units.
|
if let Expression::Quantity(lb, b) = b {
|
||||||
// We don't stop right away because we need to add all linelocations
|
if !a.unit.compatible_with(&b.unit) {
|
||||||
// to show a pretty error.
|
return Err((
|
||||||
let mut incompatible_units = false;
|
*la + *lb + *op_loc,
|
||||||
|
DaisyError::IncompatibleUnits(
|
||||||
let mut i: usize = 1;
|
a.convert_to_base().unit.to_string(),
|
||||||
while i < args.len() {
|
b.convert_to_base().unit.to_string()
|
||||||
let j = &args[i];
|
)
|
||||||
if let Expression::Quantity(l, v) = j {
|
));
|
||||||
|
}
|
||||||
if !sum.unit.compatible_with(&v.unit) {
|
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() + b.clone())));
|
||||||
incompatible_units = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !incompatible_units { sum += v.clone(); }
|
|
||||||
loc += *l;
|
|
||||||
} else {
|
|
||||||
if incompatible_units {
|
|
||||||
return Err((loc + *op_loc, EvalError::IncompatibleUnit));
|
|
||||||
}
|
|
||||||
return Ok(None);
|
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 => {
|
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(la, a) = a {
|
||||||
if let Expression::Quantity(lb, b) = b {
|
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())));
|
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(la, a) = a {
|
||||||
if let Expression::Quantity(lb, b) = b {
|
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())));
|
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::ImplicitMultiply |
|
||||||
Operator::Multiply => {
|
Operator::Multiply => {
|
||||||
let mut prod: Quantity;
|
if args.len() != 2 { panic!() };
|
||||||
let mut loc: LineLocation;
|
let a = &args[0];
|
||||||
if let Expression::Quantity(l, s) = &args[0] {
|
let b = &args[1];
|
||||||
prod = s.clone();
|
|
||||||
loc = *l;
|
|
||||||
} else { return Ok(None); };
|
|
||||||
|
|
||||||
let mut i: usize = 1;
|
if let Expression::Quantity(la, a) = a {
|
||||||
while i < args.len() {
|
if let Expression::Quantity(lb, b) = b {
|
||||||
let j = &args[i];
|
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, a.clone() * b.clone())));
|
||||||
if let Expression::Quantity(l, v) = j {
|
|
||||||
prod *= v.clone();
|
|
||||||
loc += *l;
|
|
||||||
} else { return Ok(None); }
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
return Ok(Some(Expression::Quantity(loc + *op_loc, prod)));
|
}
|
||||||
|
|
||||||
|
return Ok(None);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::ModuloLong
|
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 let Expression::Quantity(lb, vb) = b {
|
||||||
|
|
||||||
if !(va.unitless() && vb.unitless()) {
|
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 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, EvalError::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, EvalError::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())));
|
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, va.clone() % vb.clone())));
|
||||||
} else { return Ok(None); }
|
} else { return Ok(None); }
|
||||||
|
@ -174,7 +159,7 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result<Option<Ex
|
||||||
if n.is_none() {
|
if n.is_none() {
|
||||||
return Err((
|
return Err((
|
||||||
*la + *lb + *op_loc,
|
*la + *lb + *op_loc,
|
||||||
EvalError::IncompatibleUnits(
|
DaisyError::IncompatibleUnits(
|
||||||
va.convert_to_base().unit.to_string(),
|
va.convert_to_base().unit.to_string(),
|
||||||
vb.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];
|
let a = &args[0];
|
||||||
|
|
||||||
if let Expression::Quantity(l, v) = a {
|
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());
|
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)));
|
return Ok(Some(Expression::Quantity(*l, p)));
|
||||||
} else { return Ok(None); }
|
} 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 let Expression::Quantity(lb, vb) = b {
|
||||||
|
|
||||||
if !vb.unitless() {
|
if !vb.unitless() {
|
||||||
return Err((*lb, EvalError::IncompatibleUnit));
|
return Err((*lb, DaisyError::IncompatibleUnit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if va.is_zero() && vb.is_negative() {
|
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());
|
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)));
|
return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, p)));
|
||||||
} else { return Ok(None); }
|
} else { return Ok(None); }
|
||||||
} 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 let Expression::Quantity(l, v) = args {
|
||||||
|
|
||||||
if !v.unitless() {
|
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.fract().is_zero() { return Err((*l + *op_loc, DaisyError::BadMath)); }
|
||||||
if v > &Quantity::new_rational(50_000f64).unwrap() { return Err((*l + *op_loc, EvalError::TooBig)); }
|
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 prod = Quantity::new_rational(1f64).unwrap();
|
||||||
let mut u = v.clone();
|
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 quantity;
|
||||||
pub mod evaluate;
|
pub mod evaluate;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod errors;
|
||||||
|
pub mod formattedtext;
|
||||||
|
|
||||||
mod entry;
|
mod entry;
|
||||||
use crate::entry::main_e;
|
use crate::entry::main_e;
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
mod stage;
|
mod stage;
|
||||||
|
|
||||||
mod token;
|
mod token;
|
||||||
mod parsererror;
|
|
||||||
mod expression;
|
mod expression;
|
||||||
mod linelocation;
|
mod linelocation;
|
||||||
|
|
||||||
use self::{
|
use self::token::Token;
|
||||||
token::Token,
|
|
||||||
parsererror::ParserError,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
expression::Expression,
|
expression::Expression,
|
||||||
|
@ -19,11 +15,11 @@ pub use self::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
s: &String, context: &Context
|
s: &String, context: &Context
|
||||||
) -> Result<Expression, (LineLocation, ParserError)> {
|
) -> Result<Expression, (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
let expressions = stage::tokenize(s);
|
let expressions = stage::tokenize(s);
|
||||||
let (_, expressions) = stage::find_subs(expressions);
|
let (_, expressions) = stage::find_subs(expressions);
|
||||||
|
@ -33,7 +29,7 @@ pub fn parse(
|
||||||
return Ok(g);
|
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())
|
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::{
|
use super::super::{
|
||||||
Token,
|
Token,
|
||||||
LineLocation,
|
LineLocation,
|
||||||
ParserError,
|
|
||||||
Operator
|
Operator
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
|
|
||||||
fn lookback_signs(
|
fn lookback_signs(
|
||||||
g: &mut VecDeque<Token>
|
g: &mut VecDeque<Token>
|
||||||
) -> Result<(), (LineLocation, ParserError)> {
|
) -> Result<(), (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
// Convert `-` operators to `neg` operators
|
// Convert `-` operators to `neg` operators
|
||||||
// Delete `+`s that mean "positive" instead of "add"
|
// Delete `+`s that mean "positive" instead of "add"
|
||||||
|
@ -99,7 +100,7 @@ fn lookback_signs(
|
||||||
// Inserts implicit operators
|
// Inserts implicit operators
|
||||||
fn lookback(
|
fn lookback(
|
||||||
g: &mut VecDeque<Token>
|
g: &mut VecDeque<Token>
|
||||||
) -> Result<(), (LineLocation, ParserError)> {
|
) -> Result<(), (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
lookback_signs(g)?;
|
lookback_signs(g)?;
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ fn lookback(
|
||||||
// The following are syntax errors
|
// The following are syntax errors
|
||||||
(Token::Quantity(la,_), Token::Quantity(lb,_))
|
(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);}
|
_ => {g.insert(i-1, b); g.insert(i-1, a);}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,7 @@ pub fn groupify(
|
||||||
mut g: VecDeque<Token>
|
mut g: VecDeque<Token>
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Token,
|
Token,
|
||||||
(LineLocation, ParserError)
|
(LineLocation, DaisyError)
|
||||||
> {
|
> {
|
||||||
|
|
||||||
let last_linelocation: LineLocation = *g.back().unwrap().get_line_location();
|
let last_linelocation: LineLocation = *g.back().unwrap().get_line_location();
|
||||||
|
@ -220,8 +221,8 @@ pub fn groupify(
|
||||||
Token::GroupEnd(l) => {
|
Token::GroupEnd(l) => {
|
||||||
let l = *l_now + l;
|
let l = *l_now + l;
|
||||||
|
|
||||||
if i_level == 0 { return Err((l, ParserError::ExtraCloseParen)) }
|
if i_level == 0 { return Err((l, DaisyError::ExtraCloseParen)) }
|
||||||
if v_now.len() == 0 { return Err((l, ParserError::EmptyGroup)) }
|
if v_now.len() == 0 { return Err((l, DaisyError::EmptyGroup)) }
|
||||||
|
|
||||||
i_level -= 1;
|
i_level -= 1;
|
||||||
|
|
||||||
|
@ -243,7 +244,7 @@ pub fn groupify(
|
||||||
// Error on missing parenthesis
|
// Error on missing parenthesis
|
||||||
if levels.len() != 1 {
|
if levels.len() != 1 {
|
||||||
let (l, _) = levels.pop().unwrap();
|
let (l, _) = levels.pop().unwrap();
|
||||||
return Err((l, ParserError::MissingCloseParen))
|
return Err((l, DaisyError::MissingCloseParen))
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -252,7 +253,7 @@ pub fn groupify(
|
||||||
let (l, mut v) = levels.pop().unwrap();
|
let (l, mut v) = levels.pop().unwrap();
|
||||||
let (_, v_now) = levels.last_mut().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)?;
|
lookback(&mut v)?;
|
||||||
|
|
||||||
v_now.push_back(Token::Group(l, v));
|
v_now.push_back(Token::Group(l, v));
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
use super::super::{
|
use super::super::{
|
||||||
Token,
|
Token,
|
||||||
ParserError,
|
|
||||||
LineLocation,
|
LineLocation,
|
||||||
Expression,
|
Expression,
|
||||||
Operator
|
Operator
|
||||||
|
@ -13,7 +13,7 @@ fn treeify_binary(
|
||||||
i: usize,
|
i: usize,
|
||||||
g_inner: &mut VecDeque<Token>,
|
g_inner: &mut VecDeque<Token>,
|
||||||
context: &Context
|
context: &Context
|
||||||
) -> Result<bool, (LineLocation, ParserError)> {
|
) -> Result<bool, (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
let this: &Token = &g_inner[i];
|
let this: &Token = &g_inner[i];
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ fn treeify_binary(
|
||||||
Token::Operator(l, _) => l,
|
Token::Operator(l, _) => l,
|
||||||
_ => panic!()
|
_ => 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,
|
Token::Operator(l, _) => l,
|
||||||
_ => panic!()
|
_ => 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,
|
Token::Operator(l, _) => l,
|
||||||
_ => panic!()
|
_ => 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 {
|
if let Token::Operator(l, s) = left {
|
||||||
let o = Operator::from_string(s);
|
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();
|
let o = o.unwrap();
|
||||||
|
|
||||||
if {
|
if {
|
||||||
|
@ -67,13 +67,13 @@ fn treeify_binary(
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
} else {
|
} else {
|
||||||
let tl = *this.get_line_location() + *l;
|
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 {
|
if let Token::Operator(l, s) = right {
|
||||||
let o = Operator::from_string(s);
|
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();
|
let o = o.unwrap();
|
||||||
|
|
||||||
if {
|
if {
|
||||||
|
@ -83,7 +83,7 @@ fn treeify_binary(
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
} else {
|
} else {
|
||||||
let tl = *this.get_line_location() + *l;
|
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 this_op = {
|
||||||
let Token::Operator(l, s) = this else {panic!()};
|
let Token::Operator(l, s) = this else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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()
|
o.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,14 +100,14 @@ fn treeify_binary(
|
||||||
let left_op = if i > 1 {
|
let left_op = if i > 1 {
|
||||||
let Token::Operator(l, s) = &g_inner[i-2] else {panic!()};
|
let Token::Operator(l, s) = &g_inner[i-2] else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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())
|
Some(o.unwrap())
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
let right_op = if i < g_inner.len()-2 {
|
let right_op = if i < g_inner.len()-2 {
|
||||||
let Token::Operator(l, s) = &g_inner[i+2] else {panic!()};
|
let Token::Operator(l, s) = &g_inner[i+2] else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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())
|
Some(o.unwrap())
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ fn treeify_unary(
|
||||||
g_inner: &mut VecDeque<Token>,
|
g_inner: &mut VecDeque<Token>,
|
||||||
left_associative: bool,
|
left_associative: bool,
|
||||||
context: &Context
|
context: &Context
|
||||||
) -> Result<bool, (LineLocation, ParserError)> {
|
) -> Result<bool, (LineLocation, DaisyError)> {
|
||||||
|
|
||||||
let this: &Token = &g_inner[i];
|
let this: &Token = &g_inner[i];
|
||||||
let next: &Token;
|
let next: &Token;
|
||||||
|
@ -172,7 +172,7 @@ fn treeify_unary(
|
||||||
Token::Operator(l, _) => l,
|
Token::Operator(l, _) => l,
|
||||||
_ => panic!()
|
_ => panic!()
|
||||||
};
|
};
|
||||||
return Err((*l, ParserError::Syntax)); // argument is missing
|
return Err((*l, DaisyError::Syntax)); // argument is missing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,7 +184,7 @@ fn treeify_unary(
|
||||||
Token::Operator(l, _) => l,
|
Token::Operator(l, _) => l,
|
||||||
_ => panic!()
|
_ => 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
|
// Previous operator is invalid
|
||||||
return Err((
|
return Err((
|
||||||
*this.get_line_location(),
|
*this.get_line_location(),
|
||||||
ParserError::Syntax
|
DaisyError::Syntax
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,14 +212,14 @@ fn treeify_unary(
|
||||||
if let Token::Operator(l, _) = next {
|
if let Token::Operator(l, _) = next {
|
||||||
let tl = *this.get_line_location() + *l;
|
let tl = *this.get_line_location() + *l;
|
||||||
// Argument is invalid
|
// Argument is invalid
|
||||||
return Err((tl, ParserError::Syntax));
|
return Err((tl, DaisyError::Syntax));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// This operator
|
// This operator
|
||||||
let this_op = {
|
let this_op = {
|
||||||
let Token::Operator(l, s) = this else {panic!()};
|
let Token::Operator(l, s) = this else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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()
|
o.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -228,14 +228,14 @@ fn treeify_unary(
|
||||||
if i > 1 {
|
if i > 1 {
|
||||||
let Token::Operator(l, s) = &g_inner[i-2] else {panic!()};
|
let Token::Operator(l, s) = &g_inner[i-2] else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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())
|
Some(o.unwrap())
|
||||||
} else { None }
|
} else { None }
|
||||||
} else {
|
} else {
|
||||||
if i < g_inner.len()-2 {
|
if i < g_inner.len()-2 {
|
||||||
let Token::Operator(l, s) = &g_inner[i+2] else {panic!()};
|
let Token::Operator(l, s) = &g_inner[i+2] else {panic!()};
|
||||||
let o = Operator::from_string(s);
|
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())
|
Some(o.unwrap())
|
||||||
} else { None }
|
} else { None }
|
||||||
};
|
};
|
||||||
|
@ -285,7 +285,7 @@ fn treeify_unary(
|
||||||
pub fn treeify(
|
pub fn treeify(
|
||||||
mut g: Token,
|
mut g: Token,
|
||||||
context: &Context
|
context: &Context
|
||||||
) -> Result<Expression, (LineLocation, ParserError)> {
|
) -> Result<Expression, (LineLocation, DaisyError)> {
|
||||||
let (l, g_inner): (LineLocation, &mut VecDeque<Token>) = match g {
|
let (l, g_inner): (LineLocation, &mut VecDeque<Token>) = match g {
|
||||||
Token::Group(l, ref mut x) => (l, x),
|
Token::Group(l, ref mut x) => (l, x),
|
||||||
_ => panic!()
|
_ => panic!()
|
||||||
|
@ -293,7 +293,7 @@ pub fn treeify(
|
||||||
|
|
||||||
if g_inner.len() == 0 {
|
if g_inner.len() == 0 {
|
||||||
// This shouldn't ever happen.
|
// This shouldn't ever happen.
|
||||||
return Err((l, ParserError::EmptyGroup));
|
return Err((l, DaisyError::EmptyGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut left_associative = true;
|
let mut left_associative = true;
|
||||||
|
@ -315,7 +315,7 @@ pub fn treeify(
|
||||||
let this_op = match &g_inner[i] {
|
let this_op = match &g_inner[i] {
|
||||||
Token::Operator(l, s) => {
|
Token::Operator(l, s) => {
|
||||||
let o = Operator::from_string(&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()
|
o.unwrap()
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -356,7 +356,7 @@ pub fn treeify(
|
||||||
return match g {
|
return match g {
|
||||||
// Catch edge cases
|
// Catch edge cases
|
||||||
Token::Operator(l, _) => {
|
Token::Operator(l, _) => {
|
||||||
Err((l, ParserError::Syntax))
|
Err((l, DaisyError::Syntax))
|
||||||
},
|
},
|
||||||
Token::Group(_,_) => {
|
Token::Group(_,_) => {
|
||||||
treeify(g, context)
|
treeify(g, context)
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::VecDeque;
|
||||||
use crate::quantity::Unit;
|
use crate::quantity::Unit;
|
||||||
use crate::quantity::Quantity;
|
use crate::quantity::Quantity;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::errors::DaisyError;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
LineLocation,
|
LineLocation,
|
||||||
ParserError,
|
|
||||||
Expression,
|
Expression,
|
||||||
Constant
|
Constant
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,7 @@ impl Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[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 {
|
match self {
|
||||||
Token::Quantity(l, mut s) => {
|
Token::Quantity(l, mut s) => {
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ impl Token {
|
||||||
let r = Quantity::new_rational_from_string(&s);
|
let r = Quantity::new_rational_from_string(&s);
|
||||||
|
|
||||||
if r.is_none() {
|
if r.is_none() {
|
||||||
return Err((l, ParserError::BadNumber))
|
return Err((l, DaisyError::BadNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Expression::Quantity(l, r.unwrap()));
|
return Ok(Expression::Quantity(l, r.unwrap()));
|
||||||
|
|
Loading…
Reference in New Issue