2025-06-22 12:09:21 +12:00
use tracing ::{ debug , error } ;
use once_cell ::sync ::Lazy ;
#[ derive(Debug) ]
pub struct Calculation {
pub equation : String ,
pub result : String ,
}
pub fn calculate ( query : & str ) -> Option < Calculation > {
debug! ( " Got query {} " , query ) ;
let mut parser = Parser ::new ( Lexer ::new ( query ) ) ;
2025-09-05 20:58:21 +12:00
let tree = parser . parse ( ) ? ;
let res = tree . eval ( ) ;
2025-09-05 20:37:13 +12:00
debug! ( " Calculation: {} " , query ) ;
2025-09-05 20:58:21 +12:00
debug! ( " Tree: {:?} " , tree ) ;
debug! ( " Result: {} " , res ) ;
Some ( Calculation { equation : query . to_string ( ) , result : res . to_string ( ) } )
2025-06-22 12:09:21 +12:00
}
// TODO: put into own crate with dependency astro-float = "0.9.2" so I can use more than f64
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-06-22 12:09:21 +12:00
enum Token {
2025-09-04 08:54:21 +12:00
Op ( Op ) ,
2025-09-05 16:11:11 +12:00
Atom ( Atom ) ,
2025-09-04 08:54:21 +12:00
/* Number(f64),
Func ( Func ) , * /
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-09-04 08:54:21 +12:00
enum Op {
2025-09-05 21:14:04 +12:00
Add ,
Subtract ,
Multiply ,
Divide ,
Exponent ,
LParen ,
RParen ,
2025-09-05 16:11:11 +12:00
Func ( Func ) , // A function is an Op that takes whatever the next thing is and binds it, either the next number or whatever is in parens
}
2025-09-05 16:45:57 +12:00
impl Op {
2025-09-05 21:50:22 +12:00
fn bp_infix ( & self ) -> Option < ( f64 , f64 ) > {
2025-09-05 19:35:18 +12:00
match self {
2025-09-05 21:50:22 +12:00
// Op::LParen => Some(0.0),
// Op::RParen => Some(0.0),
Op ::Add = > Some ( ( 1.0 , 1.1 ) ) ,
Op ::Subtract = > Some ( ( 1.0 , 1.1 ) ) ,
Op ::Multiply = > Some ( ( 2.0 , 2.1 ) ) ,
Op ::Divide = > Some ( ( 2.0 , 2.1 ) ) ,
Op ::Exponent = > Some ( ( 3.1 , 3.0 ) ) ,
_ = > None ,
// Op::Func(_) => 0.0, // TODO: decide if this is a good LBP
2025-09-05 19:35:18 +12:00
}
2025-09-05 16:45:57 +12:00
}
2025-09-05 21:50:22 +12:00
fn bp_prefix ( & self ) -> Option < f64 > {
2025-09-05 19:35:18 +12:00
match self {
2025-09-05 21:50:22 +12:00
Op ::Func ( _ ) = > Some ( 0.1 ) ,
Op ::Subtract = > Some ( 5.0 ) ,
Op ::Add = > Some ( 5.0 ) ,
_ = > None ,
2025-09-05 19:35:18 +12:00
}
2025-09-05 16:45:57 +12:00
}
2025-09-05 20:37:13 +12:00
fn apply_to ( & self , args : & Vec < Expr > ) -> f64 {
2025-09-05 21:50:22 +12:00
match args . len ( ) {
1 = > match self {
Op ::Subtract = > Some ( 0.0 - args [ 0 ] . eval ( ) ) ,
Op ::Add = > Some ( args [ 0 ] . eval ( ) . abs ( ) ) ,
Op ::Func ( f ) = > match f {
Func ::Sine = > Some ( args [ 0 ] . eval ( ) . sin ( ) ) ,
Func ::Cosine = > Some ( args [ 0 ] . eval ( ) . cos ( ) ) ,
Func ::Tangent = > Some ( args [ 0 ] . eval ( ) . tan ( ) ) ,
Func ::ArcSine = > Some ( args [ 0 ] . eval ( ) . asin ( ) ) ,
Func ::ArcCosine = > Some ( args [ 0 ] . eval ( ) . acos ( ) ) ,
Func ::ArcTangent = > Some ( args [ 0 ] . eval ( ) . atan ( ) ) ,
Func ::Log2 = > Some ( args [ 0 ] . eval ( ) . log2 ( ) ) ,
Func ::Log10 = > Some ( args [ 0 ] . eval ( ) . log10 ( ) ) ,
Func ::Square = > Some ( args [ 0 ] . eval ( ) . powf ( 2.0 ) ) ,
Func ::SquareRoot = > Some ( args [ 0 ] . eval ( ) . sqrt ( ) ) ,
2025-09-05 21:56:46 +12:00
Func ::LogN = > None ,
2025-09-05 21:50:22 +12:00
_ = > todo! ( " {:?} " , self )
}
_ = > None ,
}
2 = > match self {
Op ::LParen = > Some ( args [ 0 ] . eval ( ) ) ,
Op ::RParen = > Some ( args [ 0 ] . eval ( ) ) ,
Op ::Add = > Some ( args [ 0 ] . eval ( ) + args [ 1 ] . eval ( ) ) ,
Op ::Subtract = > Some ( args [ 0 ] . eval ( ) - args [ 1 ] . eval ( ) ) ,
Op ::Multiply = > Some ( args [ 0 ] . eval ( ) * args [ 1 ] . eval ( ) ) ,
Op ::Divide = > Some ( args [ 0 ] . eval ( ) / args [ 1 ] . eval ( ) ) ,
Op ::Exponent = > Some ( args [ 0 ] . eval ( ) . powf ( args [ 1 ] . eval ( ) ) ) ,
2025-09-05 21:56:46 +12:00
Op ::Func ( Func ::LogN ) = > Some ( args [ 0 ] . eval ( ) . log ( args [ 1 ] . eval ( ) ) ) ,
2025-09-05 21:50:22 +12:00
_ = > None ,
}
_ = > None ,
} . unwrap_or_else ( | | {
error! ( " ERROR when evaluating maths expression, got invalid number of args ({}) for {:?} " , args . len ( ) , self ) ;
// None
f64 ::NAN
} )
2025-09-05 20:37:13 +12:00
}
2025-09-05 16:45:57 +12:00
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-09-04 08:54:21 +12:00
enum Atom {
2025-09-05 19:35:18 +12:00
Number ( f64 ) , // TODO: use the high precision floats library instead
2025-09-05 16:11:11 +12:00
Const ( Const ) ,
2025-06-22 12:09:21 +12:00
}
2025-09-05 19:39:48 +12:00
impl Atom {
fn get_val ( & self ) -> f64 {
match self {
Atom ::Number ( val ) = > * val ,
Atom ::Const ( c ) = > match c {
Const ::Pi = > 3.141592653589793238462643383279 ,
Const ::E = > 2.718281828459045235360287471352 ,
}
}
}
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-06-22 12:09:21 +12:00
enum Func {
Sine ,
Cosine ,
Tangent ,
// sin-1, cos-1, tan-1
ArcSine ,
ArcCosine ,
ArcTangent ,
Log2 ,
Log10 ,
LogN ,
Square ,
SquareRoot ,
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-09-05 16:11:11 +12:00
enum Const {
Pi ,
E ,
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-09-05 16:45:57 +12:00
enum ParseErr {
2025-06-22 12:09:21 +12:00
Eof ,
Invalid ,
}
// this can probably be swapped out with a lexer generator like Logos if needed
struct Lexer < ' a > {
data : & ' a str ,
data_ptr : & ' a str ,
idx : usize ,
2025-09-05 19:35:18 +12:00
next_tok : Result < Token , ParseErr > ,
2025-06-22 12:09:21 +12:00
}
// TODO: refactor with iterator that returns Option(Token) where one token option is Eof (or a enum of Token(Token) and Eof, or just Option(Option(Token)))
impl Lexer < '_ > {
2025-09-05 19:35:18 +12:00
fn new ( data : & str ) -> Lexer < '_ > {
let mut n : Lexer = Lexer { data , data_ptr : data , idx : 0 , next_tok : Err ( ParseErr ::Eof ) } ;
n . next ( ) ;
debug! ( " New finished! " ) ;
n
}
2025-06-22 12:09:21 +12:00
2025-09-05 19:35:18 +12:00
fn _next ( & mut self ) -> Result < Token , ParseErr > {
2025-06-22 12:09:21 +12:00
match self . data . chars ( ) . nth ( self . idx ) {
Some ( val ) = > {
debug! ( " lexing char '{}' at idx {} " , val , self . idx ) ;
// debug!("current char '{}'", self.data.chars().nth(0).unwrap());
self . idx + = 1 ;
// TODO: make more efficient
self . data_ptr = & self . data [ self . idx .. ] ;
match val {
2025-09-05 21:14:04 +12:00
'+' = > Ok ( Token ::Op ( Op ::Add ) ) ,
'-' = > Ok ( Token ::Op ( Op ::Subtract ) ) ,
'× ' | '*' = > Ok ( Token ::Op ( Op ::Multiply ) ) ,
'÷' | '/' = > Ok ( Token ::Op ( Op ::Divide ) ) ,
'^' = > Ok ( Token ::Op ( Op ::Exponent ) ) ,
'(' = > Ok ( Token ::Op ( Op ::LParen ) ) ,
')' = > Ok ( Token ::Op ( Op ::RParen ) ) ,
2025-09-05 19:35:18 +12:00
_ if val . is_whitespace ( ) = > self . _next ( ) ,
2025-09-05 16:11:11 +12:00
// TODO: maybe parse '-' as part of number so I can do '1 + -1' and similar
2025-06-22 12:09:21 +12:00
_ if val . is_digit ( 10 ) = > {
let start = self . idx - 1 ;
self . data_ptr . chars ( ) . take_while ( | c | c . is_digit ( 10 ) ) . for_each ( | _ | self . idx + = 1 ) ; //.next().unwrap_or(' ').is_digit(10) {self.idx += 1;}
match self . data [ start .. self . idx ] . parse ( ) {
2025-09-05 16:11:11 +12:00
Ok ( val ) = > Ok ( Token ::Atom ( Atom ::Number ( val ) ) ) ,
2025-09-05 16:45:57 +12:00
Err ( e ) = > Err ( ParseErr ::Invalid ) ,
2025-06-22 12:09:21 +12:00
}
} ,
_ = > {
2025-09-05 19:35:18 +12:00
let mut l : usize ;
l = matches ( & self . data [ self . idx - 1 .. ] , " sin " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::Sine ) ) ) ;
}
l = matches ( & self . data [ self . idx - 1 .. ] , " cos " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::Cosine ) ) ) ;
}
l = matches ( & self . data [ self . idx - 1 .. ] , " tan " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::Tangent ) ) ) ;
}
2025-09-05 20:37:13 +12:00
l = matches ( & self . data [ self . idx - 1 .. ] , " arcsin " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::ArcSine ) ) ) ;
}
l = matches ( & self . data [ self . idx - 1 .. ] , " arccos " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::ArcCosine ) ) ) ;
}
l = matches ( & self . data [ self . idx - 1 .. ] , " arctan " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::ArcTangent ) ) ) ;
}
l = matches ( & self . data [ self . idx - 1 .. ] , " sqrt " ) ;
if l ! = 0 {
self . idx + = l ;
return Ok ( Token ::Op ( Op ::Func ( Func ::SquareRoot ) ) ) ;
}
2025-06-22 12:09:21 +12:00
debug! ( " got invalid char '{}' " , val ) ;
2025-09-05 16:45:57 +12:00
Err ( ParseErr ::Invalid )
2025-06-22 12:09:21 +12:00
}
}
}
2025-09-05 16:45:57 +12:00
None = > Err ( ParseErr ::Eof ) ,
2025-06-22 12:09:21 +12:00
}
}
2025-09-05 19:35:18 +12:00
fn next ( & mut self ) -> Result < Token , ParseErr > {
let val = self . next_tok ;
self . next_tok = self . _next ( ) ;
val
}
fn peek ( & mut self ) -> Result < Token , ParseErr > {
self . next_tok
}
2025-06-22 12:09:21 +12:00
// TODO: replace with iterator so I can do parser.parse(lexer.iter()) and parse does lex_iter.next() & such
fn lex_all ( & mut self ) -> Option < Vec < Token > > {
let mut tokens : Vec < Token > = vec! [ ] ;
loop {
match self . next ( ) {
2025-09-05 16:45:57 +12:00
Err ( ParseErr ::Eof ) = > return Some ( tokens ) ,
Err ( ParseErr ::Invalid ) = > return None ,
2025-06-22 12:09:21 +12:00
Ok ( tok ) = > tokens . push ( tok ) ,
}
// debug!("tokens: {:?}", tokens);
}
}
}
2025-09-05 19:35:18 +12:00
fn matches ( s : & str , check : & str ) -> usize {
// debug!("s: \"{}\", check: \"{}\"c_len: {}, s_len: {}, s[c_len]: {:?}, s[c_len + 1]: {:?}", s, check, check.chars().count(), s.chars().count(), s.chars().nth(check.chars().count()), s.chars().nth(check.chars().count() + 1));
match ( s . chars ( ) . count ( ) , check . chars ( ) . count ( ) ) {
( s_len , c_len ) if s_len < c_len = > 0 ,
( s_len , c_len ) if s_len = = c_len & & s = = check = > c_len - 1 ,
( s_len , c_len ) if s_len > c_len & & s . starts_with ( check ) & & s . chars ( ) . nth ( c_len ) . unwrap ( ) . is_whitespace ( ) = > c_len ,
( _ , _ ) = > 0 ,
}
}
2025-06-22 12:09:21 +12:00
struct Parser < ' a > {
lex : Lexer < ' a > ,
}
impl Parser < '_ > {
fn new ( lex : Lexer ) -> Parser { Parser { lex } }
2025-09-04 08:54:21 +12:00
fn parse ( & mut self ) -> Option < Expr > {
self . parse_expr ( 0.0 ) . ok ( )
}
fn parse_expr ( & mut self , min_bp : f64 ) -> Result < Expr , ParseErr > {
2025-09-05 16:45:57 +12:00
/* while let Ok(val) = self.lex.next() {debug!("token: {:?}", val)}
2025-09-05 16:11:11 +12:00
match self . lex . next ( ) . err ( ) {
2025-09-04 08:54:21 +12:00
_ = > return Err ( ParseErr ::Invalid ) ,
2025-09-05 16:45:57 +12:00
} * /
let mut lhs : Expr = match self . lex . next ( ) {
Ok ( val ) = > match val {
Token ::Atom ( val ) = > Ok ( Expr ::Atom ( val ) ) ,
Token ::Op ( op ) = > match op {
2025-09-05 21:50:22 +12:00
Op ::LParen = > self . parse_expr ( 0.0 ) ,
// Op::Func(f) => Ok(Expr::Node(Op::Func(f), vec![self.parse_expr(op.get_lbp())?])),
_ = > match op . bp_prefix ( ) {
Some ( bp ) = > Ok ( Expr ::Node ( op , vec! [ self . parse_expr ( bp ) ? ] ) ) ,
None = > { debug! ( " Got unexpected {:?} as prefix " , op ) ; Err ( ParseErr ::Invalid ) }
}
2025-09-05 16:45:57 +12:00
} ,
} ,
Err ( err ) = > Err ( err ) ,
} . map_err ( | err | { debug! ( " Unexpected error at start of expr: {:?} " , err ) ; err } ) ? ;
2025-09-05 19:35:18 +12:00
debug! ( " lhs of expression is {:?} " , lhs ) ;
2025-09-05 16:45:57 +12:00
loop {
2025-09-05 19:35:18 +12:00
let op : Op = match self . lex . peek ( ) {
2025-09-05 16:45:57 +12:00
Err ( ParseErr ::Eof ) = > break ,
2025-09-05 19:35:18 +12:00
Err ( e ) = > { debug! ( " In expr got err {:?} " , e ) ; Err ( e ) } ,
2025-09-05 16:45:57 +12:00
Ok ( tok ) = > match tok {
Token ::Op ( op ) = > match op {
2025-09-05 21:14:04 +12:00
Op ::RParen = > break ,
_ = > Ok ( op ) ,
2025-09-05 16:45:57 +12:00
}
2025-09-05 19:35:18 +12:00
v = > { debug! ( " Got unexpected token {:?} " , v ) ; Err ( ParseErr ::Invalid ) } ,
2025-09-05 16:45:57 +12:00
}
2025-09-05 19:35:18 +12:00
} . map_err ( | err | { debug! ( " Unexpected error inside expr at {:?} " , err ) ; err } ) ? ;
2025-09-05 21:50:22 +12:00
if let Some ( ( lbp , rbp ) ) = op . bp_infix ( ) {
if ( lbp < min_bp ) { break ; }
self . lex . next ( ) ;
let rhs : Expr = self . parse_expr ( rbp ) ? ;
lhs = Expr ::Node ( op , vec! [ lhs , rhs ] ) ;
} else {
debug! ( " Got unexpected non-infix operator in expression: {:?} " , op ) ;
return Err ( ParseErr ::Invalid ) ;
}
2025-09-04 08:54:21 +12:00
}
2025-09-05 16:45:57 +12:00
Ok ( lhs )
2025-09-04 08:54:21 +12:00
}
2025-06-22 12:09:21 +12:00
}
2025-09-04 08:54:21 +12:00
#[ derive(Debug) ]
enum Expr {
2025-09-05 16:45:57 +12:00
Atom ( Atom ) ,
2025-09-04 08:54:21 +12:00
Node ( Op , Vec < Expr > ) ,
2025-06-22 12:09:21 +12:00
}
2025-09-05 20:37:13 +12:00
impl Expr {
fn eval ( & self ) -> f64 {
let res = match ( self ) {
Expr ::Atom ( at ) = > at . get_val ( ) ,
Expr ::Node ( op , exprs ) = > op . apply_to ( exprs ) ,
} ;
// debug!("{:?} evaluated to {}", self, res);
res
}
}