From 5e88902a161674605c6835c7be96d412d82e7dd4 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Thu, 30 May 2024 20:00:08 -0700 Subject: Write tests for parser. --- src/eval.rs | 139 ++++++++++----------- src/expr.rs | 106 ++++++++++++++++ src/main.rs | 3 +- src/parser.rs | 390 +++++++++++++++++++++++++++++++++++++--------------------- 4 files changed, 427 insertions(+), 211 deletions(-) create mode 100644 src/expr.rs diff --git a/src/eval.rs b/src/eval.rs index dce9d68..a72f271 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,4 +1,5 @@ use std::fmt::{Display, Formatter}; +use Num::{Float, Int}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Num { @@ -6,25 +7,17 @@ pub enum Num { Float(f64), } -pub fn int(i: i128) -> Num { - Num::Int(i) -} - -pub fn float(f: f64) -> Num { - Num::Float(f) -} - impl Num { fn as_float(self) -> f64 { match self { - Num::Int(i) => i as f64, - Num::Float(f) => f, + Int(i) => i as f64, + Float(f) => f, } } fn powi(self, p: i128) -> Num { if p == 0 { - return int(1); + return Int(1); } if p == 1 { return self; @@ -37,106 +30,106 @@ impl Num { pub fn pow(self, other: Num) -> Num { match other { - Num::Int(i) => { + Int(i) => { if i >= 0 { return self.powi(i); } if let Ok(i_32) = i32::try_from(i) { - return float(self.as_float().powi(i_32)); + return Float(self.as_float().powi(i_32)); } - float(1.).div(self).powi(-i) + Float(1.).div(self).powi(-i) } - Num::Float(f) => float(self.as_float().powf(f)), + Float(f) => Float(self.as_float().powf(f)), } } pub fn mul(self, other: Num) -> Num { - if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let (Int(i1), Int(i2)) = (self, other) { if let Some(p) = i1.checked_mul(i2) { - return int(p); + return Int(p); } } - float(self.as_float() * other.as_float()) + Float(self.as_float() * other.as_float()) } pub fn div(self, other: Num) -> Num { - float(self.as_float() / other.as_float()) + Float(self.as_float() / other.as_float()) } pub fn int_div(self, other: Num) -> Num { - if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let (Int(i1), Int(i2)) = (self, other) { if let Some(q) = i1.checked_div(i2) { - return int(q); + return Int(q); } } - int((self.as_float() / other.as_float()) as i128) + Int((self.as_float() / other.as_float()) as i128) } pub fn modulo(self, other: Num) -> Num { - if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let (Int(i1), Int(i2)) = (self, other) { if let Some(r) = i1.checked_rem(i2) { if i2 > 0 && r < 0 || i2 < 0 && r > 0 { - return int(r + i2); + return Int(r + i2); } - return int(r); + return Int(r); } } let n1 = self.as_float(); let n2 = other.as_float(); let r = n1 % n2; if n2 > 0. && r < 0. || n2 < 0. && r > 0. { - return float(r + n2); + return Float(r + n2); } - float(r) + Float(r) } pub fn add(self, other: Num) -> Num { - if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let (Int(i1), Int(i2)) = (self, other) { if let Some(s) = i1.checked_add(i2) { - return int(s); + return Int(s); } } - float(self.as_float() + other.as_float()) + Float(self.as_float() + other.as_float()) } pub fn sub(self, other: Num) -> Num { - if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let (Int(i1), Int(i2)) = (self, other) { if let Some(d) = i1.checked_sub(i2) { - return int(d); + return Int(d); } } - float(self.as_float() - other.as_float()) + Float(self.as_float() - other.as_float()) } pub fn sin(self) -> Num { - float(self.as_float().sin()) + Float(self.as_float().sin()) } pub fn cos(self) -> Num { - float(self.as_float().cos()) + Float(self.as_float().cos()) } pub fn tan(self) -> Num { - float(self.as_float().tan()) + Float(self.as_float().tan()) } pub fn asin(self) -> Num { - float(self.as_float().asin()) + Float(self.as_float().asin()) } pub fn acos(self) -> Num { - float(self.as_float().acos()) + Float(self.as_float().acos()) } pub fn atan(self) -> Num { - float(self.as_float().atan()) + Float(self.as_float().atan()) } pub fn sqrt(self) -> Num { match self { - Num::Int(i) if i >= 0 => { + Int(i) if i >= 0 => { if i <= 1 { - return int(i); + return Int(i); } let mut x0 = i / 2; let mut x1 = (x0 + i / x0) / 2; @@ -145,51 +138,51 @@ impl Num { x1 = (x0 + i / x0) / 2; } if x0 * x0 == i { - return int(x0); + return Int(x0); } } _ => (), } - float(self.as_float().sqrt()) + Float(self.as_float().sqrt()) } pub fn log(self) -> Num { - float(self.as_float().ln()) + Float(self.as_float().ln()) } pub fn log10(self) -> Num { - float(self.as_float().log10()) + Float(self.as_float().log10()) } pub fn log2(self) -> Num { - float(self.as_float().log2()) + Float(self.as_float().log2()) } pub fn floor(self) -> Num { match self { - Num::Int(i) => int(i), - Num::Float(f) => int(f.floor() as i128), + Int(i) => Int(i), + Float(f) => Int(f.floor() as i128), } } pub fn ceil(self) -> Num { match self { - Num::Int(i) => int(i), - Num::Float(f) => int(f.ceil() as i128), + Int(i) => Int(i), + Float(f) => Int(f.ceil() as i128), } } pub fn round(self) -> Num { match self { - Num::Int(i) => int(i), - Num::Float(f) => int(f.round_ties_even() as i128), + Int(i) => Int(i), + Float(f) => Int(f.round_ties_even() as i128), } } pub fn abs(self) -> Num { match self { - Num::Int(i) => int(i.abs()), - Num::Float(f) => float(f.abs()), + Int(i) => Int(i.abs()), + Float(f) => Float(f.abs()), } } } @@ -197,8 +190,8 @@ impl Num { impl Display for Num { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { match self { - Num::Int(n) => write!(f, "{n}"), - Num::Float(n) => { + Int(n) => write!(f, "{n}"), + Float(n) => { if !(-1e30..=1e30).contains(n) { return write!(f, "{n:.7e}"); } @@ -218,76 +211,76 @@ mod tests { #[test] fn pow_positive_int() { - assert_eq!(int(2).pow(int(16)), int(65536)); + assert_eq!(Int(2).pow(Int(16)), Int(65536)); } #[test] fn pow_negative_int() { - assert_eq!(int(2).pow(int(-3)), float(0.125)); + assert_eq!(Int(2).pow(Int(-3)), Float(0.125)); } #[test] fn pow_float() { - assert_eq!(int(4).pow(float(0.5)), float(2.)); + assert_eq!(Int(4).pow(Float(0.5)), Float(2.)); } #[test] fn pow_overflow() { - assert_eq!(int(2).pow(int(1 << 126)), float(f64::INFINITY)); + assert_eq!(Int(2).pow(Int(1 << 126)), Float(f64::INFINITY)); } #[test] fn pow_underflow() { - assert_eq!(int(2).pow(int(-(1 << 126))), float(0.)); + assert_eq!(Int(2).pow(Int(-(1 << 126))), Float(0.)); } #[test] fn modulo_pos() { - assert_eq!(int(5).modulo(int(3)), int(2)); + assert_eq!(Int(5).modulo(Int(3)), Int(2)); } #[test] fn modulo_pos_neg() { - assert_eq!(int(5).modulo(int(-3)), int(-1)); + assert_eq!(Int(5).modulo(Int(-3)), Int(-1)); } #[test] fn modulo_neg_pos() { - assert_eq!(int(-5).modulo(int(3)), int(1)); + assert_eq!(Int(-5).modulo(Int(3)), Int(1)); } #[test] fn modulo_neg() { - assert_eq!(int(-5).modulo(int(-3)), int(-2)); + assert_eq!(Int(-5).modulo(Int(-3)), Int(-2)); } #[test] fn modulo_float_pos() { - assert_eq!(float(5.).modulo(int(3)), float(2.)); + assert_eq!(Float(5.).modulo(Int(3)), Float(2.)); } #[test] fn modulo_float_pos_neg() { - assert_eq!(float(5.).modulo(int(-3)), float(-1.)); + assert_eq!(Float(5.).modulo(Int(-3)), Float(-1.)); } #[test] fn modulo_float_neg_pos() { - assert_eq!(float(-5.).modulo(int(3)), float(1.)); + assert_eq!(Float(-5.).modulo(Int(3)), Float(1.)); } #[test] fn modulo_float_neg() { - assert_eq!(float(-5.).modulo(int(-3)), float(-2.)); + assert_eq!(Float(-5.).modulo(Int(-3)), Float(-2.)); } #[test] fn sqrt() { for n in 0..65536 { assert_eq!( - int(n * n).sqrt(), - int(n), - "int({}).sqrt() is not equal to {}", + Int(n * n).sqrt(), + Int(n), + "Int({}).sqrt() is not equal to {}", n * n, n ); @@ -296,6 +289,6 @@ mod tests { #[test] fn sqrt_big() { - assert_eq!(int(1 << 126).sqrt(), int(1 << 63)); + assert_eq!(Int(1 << 126).sqrt(), Int(1 << 63)); } } diff --git a/src/expr.rs b/src/expr.rs new file mode 100644 index 0000000..1022de4 --- /dev/null +++ b/src/expr.rs @@ -0,0 +1,106 @@ +use crate::eval::Num; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinOp { + Pow, + Mul, + Div, + IntDiv, + Mod, + Add, + Sub, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnOp { + Neg, + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, + Sqrt, + Log, + Log10, + Log2, + Floor, + Ceil, + Round, + Abs, +} + +#[derive(Debug, PartialEq)] +pub enum Expr { + Num(Num), + UnOp { + op: UnOp, + x: Box, + }, + BinOp { + op: BinOp, + x: Box, + y: Box, + }, +} + +impl BinOp { + pub fn expr(self, x: Expr, y: Expr) -> Expr { + Expr::BinOp { + op: self, + x: Box::new(x), + y: Box::new(y), + } + } + + fn eval(self, x: Num, y: Num) -> Num { + match self { + BinOp::Pow => x.pow(y), + BinOp::Mul => x.mul(y), + BinOp::Div => x.div(y), + BinOp::IntDiv => x.int_div(y), + BinOp::Mod => x.modulo(y), + BinOp::Add => x.add(y), + BinOp::Sub => x.sub(y), + } + } +} + +impl UnOp { + pub fn expr(self, x: Expr) -> Expr { + Expr::UnOp { + op: self, + x: Box::new(x), + } + } + + fn eval(self, x: Num) -> Num { + match self { + UnOp::Neg => Num::Int(0).sub(x), + UnOp::Sin => x.sin(), + UnOp::Cos => x.cos(), + UnOp::Tan => x.tan(), + UnOp::Asin => x.asin(), + UnOp::Acos => x.acos(), + UnOp::Atan => x.atan(), + UnOp::Sqrt => x.sqrt(), + UnOp::Log => x.log(), + UnOp::Log10 => x.log10(), + UnOp::Log2 => x.log2(), + UnOp::Floor => x.floor(), + UnOp::Ceil => x.ceil(), + UnOp::Round => x.round(), + UnOp::Abs => x.abs(), + } + } +} + +impl Expr { + pub fn eval(&self) -> Num { + match self { + Expr::Num(n) => *n, + Expr::UnOp { op, x } => op.eval(x.eval()), + Expr::BinOp { op, x, y } => op.eval(x.eval(), y.eval()), + } + } +} diff --git a/src/main.rs b/src/main.rs index 8976e17..9af3e48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod eval; +mod expr; mod lexer; mod parser; @@ -39,7 +40,7 @@ fn main() -> ExitCode { window.destroy(); } if let Some(n) = parser::parse(&input.text()) { - output.set_text(&format!("{n}")); + output.set_text(&format!("{}", n.eval())); } }), ); diff --git a/src/parser.rs b/src/parser.rs index 5e02037..6eeb9d8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,14 +1,12 @@ -use crate::eval; use crate::eval::Num; +use crate::expr::{BinOp, Expr, UnOp}; use crate::lexer::Lexer; use std::str::FromStr; enum Op { Paren, - Op { - prec: i8, - eval: Box Num>, - }, + Un(UnOp), + Bin(BinOp, Expr), } struct Parser<'a> { @@ -51,213 +49,161 @@ impl<'a> Parser<'a> { let Ok(f) = f64::from_str(t) else { return None; }; - return Some(eval::float(f)); + return Some(Num::Float(f)); } let Ok(i) = i128::from_str(t) else { return None; }; - Some(eval::int(i)) + Some(Num::Int(i)) } fn parse_const(&mut self) -> Option { if self.symbol("e") { - return Some(eval::float(std::f64::consts::E)); + return Some(Num::Float(std::f64::consts::E)); } if self.symbol("pi") { - return Some(eval::float(std::f64::consts::PI)); + return Some(Num::Float(std::f64::consts::PI)); } self.parse_num() } fn parse_fun(&mut self) -> bool { if self.symbol("-") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(|n| eval::int(0).sub(n)), - }); + self.stack.push(Op::Un(UnOp::Neg)); return true; } if self.symbol("sin") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::sin), - }); + self.stack.push(Op::Un(UnOp::Sin)); return true; } if self.symbol("cos") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::cos), - }); + self.stack.push(Op::Un(UnOp::Cos)); return true; } if self.symbol("tan") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::tan), - }); + self.stack.push(Op::Un(UnOp::Tan)); return true; } if self.symbol("asin") || self.symbol("arcsin") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::asin), - }); + self.stack.push(Op::Un(UnOp::Asin)); return true; } if self.symbol("acos") || self.symbol("arccos") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::acos), - }); + self.stack.push(Op::Un(UnOp::Acos)); return true; } if self.symbol("atan") || self.symbol("arctan") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::atan), - }); + self.stack.push(Op::Un(UnOp::Atan)); return true; } if self.symbol("sqrt") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::sqrt), - }); + self.stack.push(Op::Un(UnOp::Sqrt)); return true; } if self.symbol("log") || self.symbol("ln") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::log), - }); + self.stack.push(Op::Un(UnOp::Log)); return true; } if self.symbol("log10") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::log10), - }); + self.stack.push(Op::Un(UnOp::Log10)); return true; } if self.symbol("log2") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::log2), - }); + self.stack.push(Op::Un(UnOp::Log2)); return true; } if self.symbol("floor") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::floor), - }); + self.stack.push(Op::Un(UnOp::Floor)); return true; } if self.symbol("ceil") || self.symbol("ceiling") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::ceil), - }); + self.stack.push(Op::Un(UnOp::Ceil)); return true; } if self.symbol("round") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::round), - }); + self.stack.push(Op::Un(UnOp::Round)); return true; } if self.symbol("abs") { - self.stack.push(Op::Op { - prec: 3, - eval: Box::new(Num::abs), - }); + self.stack.push(Op::Un(UnOp::Abs)); return true; } false } +} + +fn prec(op: BinOp) -> i8 { + match op { + BinOp::Pow => 2, + BinOp::Mul => 1, + BinOp::Div => 1, + BinOp::IntDiv => 1, + BinOp::Mod => 1, + BinOp::Add => 0, + BinOp::Sub => 0, + } +} - fn eval(&mut self, n: Num, target_prec: i8) -> Num { - let mut n = n; - while let Some(Op::Op { prec, .. }) = self.stack.last() { - if *prec < target_prec { - break; +impl<'a> Parser<'a> { + fn eval(&mut self, e: Expr, target_prec: i8) -> Expr { + let mut e = e; + loop { + match self.stack.last() { + None | Some(Op::Paren) => break, + Some(Op::Bin(op, _)) if prec(*op) < target_prec => break, + _ => (), + } + match self.stack.pop().unwrap() { + Op::Un(op) => e = op.expr(e), + Op::Bin(op, e1) => e = op.expr(e1, e), + Op::Paren => unreachable!(), } - let Some(Op::Op { eval, .. }) = self.stack.pop() else { - unreachable!(); - }; - n = eval(n); } - n + e } - fn parse_op(&mut self, n: Num) { + fn parse_op(&mut self, e: Expr) { if self.symbol("^") { - let n1 = self.eval(n, 3); - self.stack.push(Op::Op { - prec: 2, - eval: Box::new(move |n2| n1.pow(n2)), - }); + let e = self.eval(e, 3); + self.stack.push(Op::Bin(BinOp::Pow, e)); return; } if self.symbol("*") { - let n1 = self.eval(n, 1); - self.stack.push(Op::Op { - prec: 1, - eval: Box::new(move |n2| n1.mul(n2)), - }); + let e = self.eval(e, 1); + self.stack.push(Op::Bin(BinOp::Mul, e)); return; } if self.symbol("/") { - let n1 = self.eval(n, 1); - self.stack.push(Op::Op { - prec: 1, - eval: Box::new(move |n2| n1.div(n2)), - }); + let e = self.eval(e, 1); + self.stack.push(Op::Bin(BinOp::Div, e)); return; } if self.symbol("//") { - let n1 = self.eval(n, 1); - self.stack.push(Op::Op { - prec: 1, - eval: Box::new(move |n2| n1.int_div(n2)), - }); + let e = self.eval(e, 1); + self.stack.push(Op::Bin(BinOp::IntDiv, e)); return; } if self.symbol("%") { - let n1 = self.eval(n, 1); - self.stack.push(Op::Op { - prec: 1, - eval: Box::new(move |n2| n1.modulo(n2)), - }); + let e = self.eval(e, 1); + self.stack.push(Op::Bin(BinOp::Mod, e)); return; } if self.symbol("+") { - let n1 = self.eval(n, 0); - self.stack.push(Op::Op { - prec: 0, - eval: Box::new(move |n2| n1.add(n2)), - }); + let e = self.eval(e, 0); + self.stack.push(Op::Bin(BinOp::Add, e)); return; } if self.symbol("-") { - let n1 = self.eval(n, 0); - self.stack.push(Op::Op { - prec: 0, - eval: Box::new(move |n2| n1.sub(n2)), - }); + let e = self.eval(e, 0); + self.stack.push(Op::Bin(BinOp::Sub, e)); return; } - let n1 = self.eval(n, 1); - self.stack.push(Op::Op { - prec: 1, - eval: Box::new(move |n2| n1.mul(n2)), - }); + let e = self.eval(e, 1); + self.stack.push(Op::Bin(BinOp::Mul, e)); } - fn parse(&mut self) -> Option { - let mut n = loop { + fn parse(&mut self) -> Option { + let mut e = loop { if self.symbol("(") { self.stack.push(Op::Paren); continue; @@ -265,32 +211,33 @@ impl<'a> Parser<'a> { if self.parse_fun() { continue; } - let mut n = self.parse_const()?; + let mut e = Expr::Num(self.parse_const()?); while self.symbol(")") { loop { - let op = self.stack.pop()?; - let Op::Op { eval, .. } = op else { - break; - }; - n = eval(n); + match self.stack.pop()? { + Op::Un(op) => e = op.expr(e), + Op::Bin(op, e1) => e = op.expr(e1, e), + Op::Paren => break, + } } } if self.peek().is_none() { - break n; + break e; } - self.parse_op(n); + self.parse_op(e); }; - while let Some(op) = self.stack.pop() { - let Op::Op { eval, .. } = op else { - return None; - }; - n = eval(n); + loop { + match self.stack.pop() { + Some(Op::Paren) => return None, + Some(Op::Un(op)) => e = op.expr(e), + Some(Op::Bin(op, e1)) => e = op.expr(e1, e), + None => return Some(e), + } } - Some(n) } } -pub fn parse(expr: &str) -> Option { +pub fn parse(expr: &str) -> Option { let mut p = Parser { r: Lexer { buf: expr }, first_token: None, @@ -298,3 +245,172 @@ pub fn parse(expr: &str) -> Option { }; p.parse() } + +#[cfg(test)] +mod tests { + use super::*; + + fn int(i: i128) -> Expr { + Expr::Num(Num::Int(i)) + } + + fn float(f: f64) -> Expr { + Expr::Num(Num::Float(f)) + } + + #[test] + fn parse_int() { + assert_eq!(parse("500"), Some(int(500))); + } + + #[test] + fn parse_float() { + assert_eq!(parse("1e2"), Some(float(100.))); + } + + #[test] + fn parse_fun() { + assert_eq!( + parse("sin pi"), + Some(UnOp::Sin.expr(float(std::f64::consts::PI))) + ); + } + + #[test] + fn parse_nested_fun() { + assert_eq!( + parse("log log 100"), + Some(UnOp::Log.expr(UnOp::Log.expr(int(100)))) + ); + } + + #[test] + fn parse_power() { + assert_eq!( + parse("2^1^2"), + Some(BinOp::Pow.expr(int(2), BinOp::Pow.expr(int(1), int(2)))) + ); + } + + #[test] + fn parse_fun_power() { + assert_eq!( + parse("log 2^2"), + Some(BinOp::Pow.expr(UnOp::Log.expr(int(2)), int(2))) + ); + } + + #[test] + fn parse_power_fun() { + assert_eq!( + parse("2^log 2"), + Some(BinOp::Pow.expr(int(2), UnOp::Log.expr(int(2)))) + ); + } + + #[test] + fn parse_pow_mul() { + assert_eq!( + parse("2^2*2"), + Some(BinOp::Mul.expr(BinOp::Pow.expr(int(2), int(2)), int(2))) + ); + } + + #[test] + fn parse_mul_pow() { + assert_eq!( + parse("2*2^2"), + Some(BinOp::Mul.expr(int(2), BinOp::Pow.expr(int(2), int(2)))) + ); + } + + #[test] + fn parse_mul_div() { + assert_eq!( + parse("2*2/2"), + Some(BinOp::Div.expr(BinOp::Mul.expr(int(2), int(2)), int(2))) + ); + } + + #[test] + fn parse_add_mul() { + assert_eq!( + parse("2+2*2"), + Some(BinOp::Add.expr(int(2), BinOp::Mul.expr(int(2), int(2)))) + ); + } + + #[test] + fn parse_mul_add() { + assert_eq!( + parse("2*2+2"), + Some(BinOp::Add.expr(BinOp::Mul.expr(int(2), int(2)), int(2))) + ); + } + + #[test] + fn parse_fun_add() { + assert_eq!( + parse("log 2+2"), + Some(BinOp::Add.expr(UnOp::Log.expr(int(2)), int(2))), + ); + } + + #[test] + fn parse_parens() { + assert_eq!( + parse("(1+2)*3"), + Some(BinOp::Mul.expr(BinOp::Add.expr(int(1), int(2)), int(3))) + ); + } + + #[test] + fn parse_implicit_multiplication() { + assert_eq!( + parse("2pi"), + Some(BinOp::Mul.expr(int(2), float(std::f64::consts::PI))), + ); + } + + #[test] + fn parse_implicit_multiplication_neg() { + assert_eq!( + parse("-2 2 -2"), + Some(BinOp::Sub.expr(BinOp::Mul.expr(UnOp::Neg.expr(int(2)), int(2)), int(2))) + ); + } + + #[test] + fn parse_fun_implicit_multiplication() { + assert_eq!( + parse("sin 2pi"), + Some(BinOp::Mul.expr(UnOp::Sin.expr(int(2)), float(std::f64::consts::PI))), + ); + } + + #[test] + fn parse_unary_negate() { + assert_eq!( + parse("1---2"), + Some(BinOp::Sub.expr(int(1), UnOp::Neg.expr(UnOp::Neg.expr(int(2))))) + ); + } + + #[test] + fn parse_negate_fun() { + assert_eq!( + parse("log-log 2"), + Some(UnOp::Log.expr(UnOp::Neg.expr(UnOp::Log.expr(int(2))))) + ); + } + + #[test] + fn parse_unmatched_parens() { + assert_eq!(parse("0))))"), None,); + } + + #[test] + fn parse_unmatched_op() { + assert_eq!(parse("2^2+"), None,); + } +} -- cgit v1.3.1