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-04 08:54:21 +12:00
debug! ( " Parse tree: {:?} " , parser . parse ( ) ) ;
2025-06-22 12:09:21 +12:00
// debug!("final token was: {:?}", lexer.next());
// debug!("Tokens: {:?}", lexer.lex_all());
None
}
// 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 16:11:11 +12:00
BinOp ( BinOp ) ,
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 {
fn get_lbp ( & self ) -> f64 {
2025-09-05 19:35:18 +12:00
match self {
Op ::BinOp ( op ) = > match op {
BinOp ::LParen = > 0.0 ,
BinOp ::RParen = > 0.0 ,
BinOp ::Add = > 1.0 ,
BinOp ::Subtract = > 1.0 ,
BinOp ::Multiply = > 2.0 ,
BinOp ::Divide = > 2.0 ,
BinOp ::Exponent = > 3.0 ,
} ,
Op ::Func ( _ ) = > 2.9 , // TODO: decide if this is a good LBP
}
2025-09-05 16:45:57 +12:00
}
fn get_rbp ( & self ) -> f64 {
2025-09-05 19:35:18 +12:00
match self {
Op ::BinOp ( op ) = > match op {
BinOp ::LParen = > 0.0 ,
BinOp ::RParen = > 0.0 ,
BinOp ::Add = > 1.1 ,
BinOp ::Subtract = > 1.1 ,
BinOp ::Multiply = > 2.1 ,
BinOp ::Divide = > 2.1 ,
BinOp ::Exponent = > 3.1 ,
} ,
Op ::Func ( _ ) = > 4.0 , // TODO: decide if this is a good RBP
}
2025-09-05 16:45:57 +12:00
}
}
2025-09-05 19:35:18 +12:00
#[ derive(Debug, Copy, Clone) ]
2025-09-05 16:11:11 +12:00
enum BinOp {
2025-06-22 12:09:21 +12:00
Add ,
Subtract ,
Multiply ,
Divide ,
Exponent ,
LParen ,
RParen ,
2025-09-04 08:54:21 +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: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 16:11:11 +12:00
'+' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::Add ) ) ) ,
'-' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::Subtract ) ) ) ,
'× ' | '*' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::Multiply ) ) ) ,
'÷' | '/' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::Divide ) ) ) ,
'^' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::Exponent ) ) ) ,
'(' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::LParen ) ) ) ,
')' = > Ok ( Token ::Op ( Op ::BinOp ( BinOp ::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-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 {
Op ::BinOp ( BinOp ::LParen ) = > self . parse_expr ( op . get_lbp ( ) ) ,
2025-09-05 19:35:18 +12:00
Op ::Func ( f ) = > Ok ( Expr ::Node ( Op ::Func ( f ) , vec! [ self . parse_expr ( op . get_lbp ( ) ) ? ] ) ) ,
2025-09-05 16:45:57 +12:00
_ = > Err ( ParseErr ::Invalid ) ,
} ,
} ,
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 {
Op ::BinOp ( op ) = > match op {
BinOp ::RParen = > break ,
_ = > Ok ( Op ::BinOp ( op ) ) ,
} ,
2025-09-05 19:35:18 +12:00
Op ::Func ( f ) = > {
lhs = Expr ::Node ( Op ::Func ( f ) , vec! [ self . parse_expr ( Op ::Func ( f ) . get_lbp ( ) ) ? ] ) ;
continue ;
} ,
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 16:45:57 +12:00
if ( op . get_lbp ( ) < min_bp ) { break ; }
2025-09-05 19:35:18 +12:00
self . lex . next ( ) ;
2025-09-05 16:45:57 +12:00
let rhs : Expr = self . parse_expr ( op . get_rbp ( ) ) ? ;
lhs = Expr ::Node ( op , vec! [ lhs , rhs ] ) ;
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
}