finish up lexing & do some parsing improvements (functions now require parens for multiple args :(, I'll see if I can change that later)

This commit is contained in:
Book-reader 2025-09-05 23:32:21 +12:00
parent cbf35e9746
commit 47477732ba

View file

@ -20,7 +20,7 @@ pub fn calculate(query: &str) -> Option<Calculation> {
}
// TODO: put into own crate with dependency astro-float = "0.9.2" so I can use more than f64
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Token {
Op(Op),
Atom(Atom),
@ -28,7 +28,7 @@ enum Token {
Func(Func),*/
}
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Op {
Add,
Subtract,
@ -57,7 +57,7 @@ impl Op {
fn bp_prefix(&self) -> Option<f64> {
match self {
Op::Func(_) => Some(0.1),
Op::Func(_) => Some(6.0),
Op::Subtract => Some(5.0),
Op::Add => Some(5.0),
_ => None,
@ -78,10 +78,12 @@ impl Op {
Func::ArcTangent => Some(args[0].eval().atan()),
Func::Log2 => Some(args[0].eval().log2()),
Func::Log10 => Some(args[0].eval().log10()),
Func::LogN => Some(args[0].eval().ln()),
Func::Square => Some(args[0].eval().powf(2.0)),
Func::SquareRoot => Some(args[0].eval().sqrt()),
Func::LogN => None,
_ => todo!("{:?}", self)
Func::Abs => Some(args[0].eval().abs()),
Func::Log => None,
// _ => todo!("{:?}", self)
}
_ => None,
}
@ -93,7 +95,7 @@ impl Op {
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())),
Op::Func(Func::LogN) => Some(args[0].eval().log(args[1].eval())),
Op::Func(Func::Log) => Some(args[0].eval().log(args[1].eval())),
_ => None,
}
_ => None,
@ -105,7 +107,7 @@ impl Op {
}
}
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Atom {
Number(f64), // TODO: use the high precision floats library instead
Const(Const),
@ -123,7 +125,7 @@ impl Atom {
}
}
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Func {
Sine,
Cosine,
@ -135,17 +137,39 @@ enum Func {
Log2,
Log10,
LogN,
Log,
Square,
SquareRoot,
Abs,
}
#[derive(Debug, Copy, Clone)]
impl Func {
fn names() -> &'static [(Func, &'static [&'static str])] {
&[
(Func::Sine, &["sin", "sine"]),
(Func::Cosine, &["cos", "cosine"]),
(Func::Tangent, &["tan", "tangent"]),
(Func::ArcSine, &["asin", "asine", "arcsin", "arcsine"]),
(Func::ArcCosine, &["acos", "acosine", "arccos", "arccosine"]),
(Func::ArcTangent, &["atan", "atangent", "arctan", "arctangent"]),
(Func::Log2, &["log2"]),
(Func::Log10, &["log10"]),
(Func::LogN, &["ln", "logn"]),
(Func::Log, &["log"]),
(Func::Square, &["square", "squared"]),
(Func::SquareRoot, &["sqrt", "squareroot", ""]),
(Func::Abs, &["abs", "absolute"]),
]
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum Const {
Pi,
E,
}
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum ParseErr {
Eof,
Invalid,
@ -189,7 +213,7 @@ impl Lexer<'_> {
_ if val.is_digit(10) => {
let mut len: usize = 0;
self.data_ptr.chars().take_while(|c| c.is_digit(10) || *c == '.').for_each(|_| len += 1);
self.next_by += len - 1;
self.next_by = len;
match self.data_ptr[..len].parse() {
Ok(val) => Ok(Token::Atom(Atom::Number(val))),
@ -197,46 +221,15 @@ impl Lexer<'_> {
}
},
_ => {
/* match (&self.data[self.idx - 1..]) {
s if s.starts_with(""
let len = self.data_ptr.chars().count();
for (f, names) in Func::names() {
for name in *names {
if self.data_ptr.starts_with(name) {
self.next_by = if self.data_ptr.len() > name.len() { name.chars().count() + 1} else { name.chars().count() };
return Ok(Token::Op(Op::Func(*f)));
}
}
}
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)));
}
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)));
}
*/
debug!("got invalid char '{}'", val);
Err(ParseErr::Invalid)
}
@ -304,7 +297,11 @@ impl Parser<'_> {
Ok(val) => match val {
Token::Atom(val) => Ok(Expr::Atom(val)),
Token::Op(op) => match op {
Op::LParen => self.parse_expr(0.0),
Op::LParen => {
let val = self.parse_expr(0.0);
assert_eq!(self.lex.next(), Ok(Token::Op(Op::RParen)));
val
},
// 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)?])),
@ -314,19 +311,24 @@ impl Parser<'_> {
},
Err(err) => Err(err),
}.map_err(|err| { debug!("Unexpected error at start of expr: {:?}", err); err })?;
debug!("lhs of expression is {:?}", lhs);
debug!("lhs of expression is {:?}, min_bp is {}", lhs, min_bp);
loop {
debug!("loop start");
let op: Op = match self.lex.peek() {
Err(ParseErr::Eof) => break,
Err(e) => { debug!("In expr got err {:?}", e); Err(e) },
Ok(tok) => match tok {
Token::Op(op) => match op {
Op::RParen => break,
Op::RParen => {
debug!("got RParen");
break;
},
_ => Ok(op),
}
v => { debug!("Got unexpected token {:?}", v); Err(ParseErr::Invalid) },
}
}.map_err(|err| { debug!("Unexpected error inside expr at {:?}", err); err })?;
debug!("op is {:?}", op);
if let Some((lbp, rbp)) = op.bp_infix() {
if (lbp < min_bp) { break; }
self.lex.next();
@ -337,6 +339,7 @@ impl Parser<'_> {
return Err(ParseErr::Invalid);
}
}
debug!("Returning expr {:?}", lhs);
Ok(lhs)
}
}