generated from PlexSheep/baserepo
basic filter and error display
This commit is contained in:
parent
ff560c63ff
commit
f8f88e79c3
|
@ -129,6 +129,13 @@ fn main() {
|
||||||
|
|
||||||
debug!("exporssion: {}", expr);
|
debug!("exporssion: {}", expr);
|
||||||
let r = Calculator::oneshot(expr);
|
let r = Calculator::oneshot(expr);
|
||||||
println!("{}", r.unwrap());
|
match r {
|
||||||
|
Ok(r) => {
|
||||||
|
println!("{r}");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Could not compute: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub enum Function {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
SyntaxError
|
SyntaxError(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -120,6 +120,17 @@ impl<T: num_traits::PrimInt> From<T> for NumVal where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Display Errors with a nice little reason
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::SyntaxError(reason) => {
|
||||||
|
write!(f, "Syntax Error: {}", reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
impl<T: PrimInt> From<T> for Value where
|
impl<T: PrimInt> From<T> for Value where
|
||||||
|
|
|
@ -40,6 +40,7 @@ use crate::logger::{trace, debug, info, warn, error};
|
||||||
/// This struct does not do anything at the moment, but aims to be the target for high level
|
/// This struct does not do anything at the moment, but aims to be the target for high level
|
||||||
/// control. Instead of using the [`Calculator`], you could just use the [`Term`] struct for
|
/// control. Instead of using the [`Calculator`], you could just use the [`Term`] struct for
|
||||||
/// lower level control.
|
/// lower level control.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Calculator;
|
pub struct Calculator;
|
||||||
|
|
||||||
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
|
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -65,8 +66,9 @@ impl Calculator {
|
||||||
t.prepare()?;
|
t.prepare()?;
|
||||||
t.process()?;
|
t.process()?;
|
||||||
if t.result.is_none() {
|
if t.result.is_none() {
|
||||||
error!("Term was processed but no result was assigned.");
|
let reason = format!("Term was processed but no result was assigned.");
|
||||||
return Err(Error::SyntaxError)
|
// FIXME: unfitting error type
|
||||||
|
return Err(Error::SyntaxError(reason))
|
||||||
}
|
}
|
||||||
return t.result.unwrap()
|
return t.result.unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,8 @@ enum Token {
|
||||||
pub struct Term {
|
pub struct Term {
|
||||||
/// the original expression to calculate
|
/// the original expression to calculate
|
||||||
pub original: String,
|
pub original: String,
|
||||||
|
/// the filtered text of the expression, only with relevant information
|
||||||
|
pub text: String,
|
||||||
/// the calculated result, may be of diffrent types, see [`crate::math::calculator::result`].
|
/// the calculated result, may be of diffrent types, see [`crate::math::calculator::result`].
|
||||||
pub result: Option<Result<Value>>,
|
pub result: Option<Result<Value>>,
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
@ -76,6 +78,7 @@ impl Term {
|
||||||
return Ok(
|
return Ok(
|
||||||
Term {
|
Term {
|
||||||
original: orig,
|
original: orig,
|
||||||
|
text: String::new(), // will be initialized in `prepare()`
|
||||||
result: None,
|
result: None,
|
||||||
operator_stack: Vec::new(),
|
operator_stack: Vec::new(),
|
||||||
output_queue: VecDeque::new()
|
output_queue: VecDeque::new()
|
||||||
|
@ -85,22 +88,20 @@ impl Term {
|
||||||
|
|
||||||
/// Prepare the term for the processing.
|
/// Prepare the term for the processing.
|
||||||
pub fn prepare(&mut self) -> Result<()> {
|
pub fn prepare(&mut self) -> Result<()> {
|
||||||
// TODO: shunting yard
|
trace!("preparing term: {:#?}", self);
|
||||||
|
self.text = Self::filter(&self.original)?;
|
||||||
|
|
||||||
// Storage for unfinished tokens
|
// Storage for unfinished tokens
|
||||||
let mut unfinished_chars: Vec<char> = Vec::new();
|
let mut unfinished_chars: Vec<char> = Vec::new();
|
||||||
|
|
||||||
for (index, c) in self.original.chars().enumerate() {
|
for (index, c) in self.original.chars().enumerate() {
|
||||||
if !c.is_alphanumeric() {
|
|
||||||
// TODO: allow any unicode char to be a variable
|
|
||||||
warn!("'{c}' is not a valid character, only alphanumeric input is allowed.");
|
|
||||||
return Err(Error::SyntaxError);
|
|
||||||
}
|
|
||||||
// this will be a mess, but it has to be before i can sort the mess.
|
// this will be a mess, but it has to be before i can sort the mess.
|
||||||
match c {
|
match c {
|
||||||
|
// TODO: make function to check if character is an operator, use it
|
||||||
_ => {
|
_ => {
|
||||||
warn!("The meaning of '{c}' could not be identified.");
|
let reason = format!("The meaning of '{c}' could not be identified.");
|
||||||
return Err(Error::SyntaxError);
|
warn!(reason);
|
||||||
|
return Err(Error::SyntaxError(reason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,14 +126,54 @@ impl Term {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// only leave relevant chars for calculation
|
/// only leave relevant chars for calculation
|
||||||
fn filter(s: String) -> String {
|
// TODO: make function to check if character is an operator, use it
|
||||||
|
fn filter(s: &str) -> Result<String> {
|
||||||
|
// pre checks
|
||||||
|
// NOTE: Apperently, "alphanumeric" in Rust is a pretty broad term.
|
||||||
|
// Even CJK characters or Japanese Kana are allowed:
|
||||||
|
// - 'さ' alphanumeric
|
||||||
|
// - '数' alphanumeric
|
||||||
|
// - '学' alphanumeric
|
||||||
|
// - '+' not alphanumeric
|
||||||
|
for c in s.chars() {
|
||||||
|
#[cfg(debug_assertions)] {
|
||||||
|
debug!("filter checks for '{c}':
|
||||||
|
alphanumeric: {}
|
||||||
|
allowed special: {}
|
||||||
|
EXCEPT IF
|
||||||
|
ascii control: {}
|
||||||
|
",
|
||||||
|
!c.is_alphanumeric(),
|
||||||
|
!Self::is_allowed_special_c(&c),
|
||||||
|
c.is_ascii_control(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!c.is_alphanumeric() ||
|
||||||
|
!Self::is_allowed_special_c(&c)
|
||||||
|
)
|
||||||
|
&&
|
||||||
|
(
|
||||||
|
c.is_ascii_control()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// TODO: allow any unicode char to be a variable
|
||||||
|
let reason = format!("'{c}' is not a valid character, only alphanumeric, punctuation, operators are allowed.");
|
||||||
|
warn!(reason);
|
||||||
|
return Err(Error::SyntaxError(reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out single chars
|
||||||
let mut filtered = String::new();
|
let mut filtered = String::new();
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
if !Self::is_ignore(&c) {
|
if !Self::is_ignore(&c) {
|
||||||
filtered.push(c);
|
filtered.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filtered
|
|
||||||
|
return Ok(filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check if we should ignore this character
|
/// check if we should ignore this character
|
||||||
|
@ -142,6 +183,14 @@ impl Term {
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// allowed special chars
|
||||||
|
fn is_allowed_special_c(c: &char) -> bool {
|
||||||
|
match *c {
|
||||||
|
'+' | '-' | '*' | '/' | '%' => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Reference in New Issue