basic filter and error display

This commit is contained in:
Christoph J. Scherr 2023-09-12 22:49:40 +02:00
parent ff560c63ff
commit f8f88e79c3
4 changed files with 83 additions and 14 deletions

View File

@ -129,6 +129,13 @@ fn main() {
debug!("exporssion: {}", expr);
let r = Calculator::oneshot(expr);
println!("{}", r.unwrap());
match r {
Ok(r) => {
println!("{r}");
}
Err(err) => {
error!("Could not compute: {err}");
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -72,7 +72,7 @@ pub enum Function {
#[non_exhaustive]
#[derive(Debug)]
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

View File

@ -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
/// control. Instead of using the [`Calculator`], you could just use the [`Term`] struct for
/// lower level control.
#[derive(Debug)]
pub struct Calculator;
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
@ -65,8 +66,9 @@ impl Calculator {
t.prepare()?;
t.process()?;
if t.result.is_none() {
error!("Term was processed but no result was assigned.");
return Err(Error::SyntaxError)
let reason = format!("Term was processed but no result was assigned.");
// FIXME: unfitting error type
return Err(Error::SyntaxError(reason))
}
return t.result.unwrap()
}

View File

@ -58,6 +58,8 @@ enum Token {
pub struct Term {
/// the original expression to calculate
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`].
pub result: Option<Result<Value>>,
/////////////////////////////////////
@ -76,6 +78,7 @@ impl Term {
return Ok(
Term {
original: orig,
text: String::new(), // will be initialized in `prepare()`
result: None,
operator_stack: Vec::new(),
output_queue: VecDeque::new()
@ -85,22 +88,20 @@ impl Term {
/// Prepare the term for the processing.
pub fn prepare(&mut self) -> Result<()> {
// TODO: shunting yard
trace!("preparing term: {:#?}", self);
self.text = Self::filter(&self.original)?;
// Storage for unfinished tokens
let mut unfinished_chars: Vec<char> = Vec::new();
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.
match c {
// TODO: make function to check if character is an operator, use it
_ => {
warn!("The meaning of '{c}' could not be identified.");
return Err(Error::SyntaxError);
let reason = format!("The meaning of '{c}' could not be identified.");
warn!(reason);
return Err(Error::SyntaxError(reason));
}
}
}
@ -125,14 +126,54 @@ impl Term {
}
/// 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();
for c in s.chars() {
if !Self::is_ignore(&c) {
filtered.push(c);
}
}
return filtered
return Ok(filtered)
}
/// check if we should ignore this character
@ -142,6 +183,14 @@ impl Term {
_ => false
}
}
/// allowed special chars
fn is_allowed_special_c(c: &char) -> bool {
match *c {
'+' | '-' | '*' | '/' | '%' => true,
_ => false
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////