diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/expr.rs | 135 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/op.rs | 93 | ||||
| -rw-r--r-- | src/parser.rs | 258 |
4 files changed, 263 insertions, 229 deletions
diff --git a/src/expr.rs b/src/expr.rs deleted file mode 100644 index bb6bb35..0000000 --- a/src/expr.rs +++ /dev/null @@ -1,135 +0,0 @@ -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<Expr>, - }, - BinOp { - op: BinOp, - x: Box<Expr>, - y: Box<Expr>, - }, -} - -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 { - enum Ex<'a> { - Expr(&'a Expr), - Un(UnOp), - Bin(BinOp), - } - - let mut exprs = Vec::new(); - let mut nums = Vec::new(); - - exprs.push(Ex::Expr(self)); - while let Some(e) = exprs.pop() { - match e { - Ex::Expr(Expr::Num(n)) => nums.push(*n), - Ex::Expr(Expr::UnOp { op, x }) => { - exprs.push(Ex::Un(*op)); - exprs.push(Ex::Expr(x)); - } - Ex::Expr(Expr::BinOp { op, x, y }) => { - exprs.push(Ex::Bin(*op)); - exprs.push(Ex::Expr(y)); - exprs.push(Ex::Expr(x)); - } - Ex::Un(op) => { - let n = nums.pop().unwrap(); - nums.push(op.eval(n)); - } - Ex::Bin(op) => { - let n2 = nums.pop().unwrap(); - let n1 = nums.pop().unwrap(); - nums.push(op.eval(n1, n2)); - } - } - } - nums[0] - } -} diff --git a/src/main.rs b/src/main.rs index 9af3e48..9c1e107 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ mod eval; -mod expr; mod lexer; +mod op; mod parser; use gdk4::{Key, ModifierType}; @@ -39,8 +39,8 @@ fn main() -> ExitCode { { window.destroy(); } - if let Some(n) = parser::parse(&input.text()) { - output.set_text(&format!("{}", n.eval())); + if let Some(bytecode) = parser::parse(&input.text()) { + output.set_text(&format!("{}", op::eval(&bytecode))); } }), ); diff --git a/src/op.rs b/src/op.rs new file mode 100644 index 0000000..6a2d00f --- /dev/null +++ b/src/op.rs @@ -0,0 +1,93 @@ +use crate::eval::Num; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnOp { + Neg, + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, + Sqrt, + Log, + Log10, + Log2, + Floor, + Ceil, + Round, + Abs, +} + +impl UnOp { + 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(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinOp { + Pow, + Mul, + Div, + IntDiv, + Mod, + Add, + Sub, +} + +impl BinOp { + 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), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Op { + Num(Num), + Un(UnOp), + Bin(BinOp), +} + +pub fn eval(ops: &[Op]) -> Num { + let mut stack = Vec::new(); + for op in ops { + match op { + Op::Num(n) => stack.push(*n), + Op::Un(op) => { + let n = stack.pop().unwrap(); + stack.push(op.eval(n)); + } + Op::Bin(op) => { + let n2 = stack.pop().unwrap(); + let n1 = stack.pop().unwrap(); + stack.push(op.eval(n1, n2)); + } + } + } + stack[0] +} diff --git a/src/parser.rs b/src/parser.rs index 6eeb9d8..4cd75be 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,18 +1,20 @@ use crate::eval::Num; -use crate::expr::{BinOp, Expr, UnOp}; use crate::lexer::Lexer; +use crate::op::{BinOp, Op, UnOp}; use std::str::FromStr; -enum Op { +enum Pending { Paren, Un(UnOp), - Bin(BinOp, Expr), + Bin(BinOp), } struct Parser<'a> { r: Lexer<'a>, first_token: Option<&'a str>, - stack: Vec<Op>, + // stack stores the pending operations that can't be flushed yet. + stack: Vec<Pending>, + result: Vec<Op>, } impl<'a> Parser<'a> { @@ -43,89 +45,93 @@ impl<'a> Parser<'a> { true } - fn parse_num(&mut self) -> Option<Num> { + fn parse_num(&mut self) -> Option<()> { let t = self.next()?; if t.contains('.') || t.contains('e') || t.contains('E') { let Ok(f) = f64::from_str(t) else { return None; }; - return Some(Num::Float(f)); + self.result.push(Op::Num(Num::Float(f))); + return Some(()); } let Ok(i) = i128::from_str(t) else { return None; }; - Some(Num::Int(i)) + self.result.push(Op::Num(Num::Int(i))); + Some(()) } - fn parse_const(&mut self) -> Option<Num> { + fn parse_const(&mut self) -> Option<()> { if self.symbol("e") { - return Some(Num::Float(std::f64::consts::E)); + self.result.push(Op::Num(Num::Float(std::f64::consts::E))); + return Some(()); } if self.symbol("pi") { - return Some(Num::Float(std::f64::consts::PI)); + self.result.push(Op::Num(Num::Float(std::f64::consts::PI))); + return Some(()); } self.parse_num() } fn parse_fun(&mut self) -> bool { if self.symbol("-") { - self.stack.push(Op::Un(UnOp::Neg)); + self.stack.push(Pending::Un(UnOp::Neg)); return true; } if self.symbol("sin") { - self.stack.push(Op::Un(UnOp::Sin)); + self.stack.push(Pending::Un(UnOp::Sin)); return true; } if self.symbol("cos") { - self.stack.push(Op::Un(UnOp::Cos)); + self.stack.push(Pending::Un(UnOp::Cos)); return true; } if self.symbol("tan") { - self.stack.push(Op::Un(UnOp::Tan)); + self.stack.push(Pending::Un(UnOp::Tan)); return true; } if self.symbol("asin") || self.symbol("arcsin") { - self.stack.push(Op::Un(UnOp::Asin)); + self.stack.push(Pending::Un(UnOp::Asin)); return true; } if self.symbol("acos") || self.symbol("arccos") { - self.stack.push(Op::Un(UnOp::Acos)); + self.stack.push(Pending::Un(UnOp::Acos)); return true; } if self.symbol("atan") || self.symbol("arctan") { - self.stack.push(Op::Un(UnOp::Atan)); + self.stack.push(Pending::Un(UnOp::Atan)); return true; } if self.symbol("sqrt") { - self.stack.push(Op::Un(UnOp::Sqrt)); + self.stack.push(Pending::Un(UnOp::Sqrt)); return true; } if self.symbol("log") || self.symbol("ln") { - self.stack.push(Op::Un(UnOp::Log)); + self.stack.push(Pending::Un(UnOp::Log)); return true; } if self.symbol("log10") { - self.stack.push(Op::Un(UnOp::Log10)); + self.stack.push(Pending::Un(UnOp::Log10)); return true; } if self.symbol("log2") { - self.stack.push(Op::Un(UnOp::Log2)); + self.stack.push(Pending::Un(UnOp::Log2)); return true; } if self.symbol("floor") { - self.stack.push(Op::Un(UnOp::Floor)); + self.stack.push(Pending::Un(UnOp::Floor)); return true; } if self.symbol("ceil") || self.symbol("ceiling") { - self.stack.push(Op::Un(UnOp::Ceil)); + self.stack.push(Pending::Un(UnOp::Ceil)); return true; } if self.symbol("round") { - self.stack.push(Op::Un(UnOp::Round)); + self.stack.push(Pending::Un(UnOp::Round)); return true; } if self.symbol("abs") { - self.stack.push(Op::Un(UnOp::Abs)); + self.stack.push(Pending::Un(UnOp::Abs)); return true; } false @@ -145,134 +151,135 @@ fn prec(op: BinOp) -> i8 { } impl<'a> Parser<'a> { - fn eval(&mut self, e: Expr, target_prec: i8) -> Expr { - let mut e = e; + fn flush(&mut self, target_prec: i8) { loop { match self.stack.last() { - None | Some(Op::Paren) => break, - Some(Op::Bin(op, _)) if prec(*op) < target_prec => break, + None | Some(Pending::Paren) => break, + Some(Pending::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!(), + Pending::Un(op) => self.result.push(Op::Un(op)), + Pending::Bin(op) => self.result.push(Op::Bin(op)), + Pending::Paren => unreachable!(), } } - e } - fn parse_op(&mut self, e: Expr) { + fn parse_op(&mut self) { if self.symbol("^") { - let e = self.eval(e, 3); - self.stack.push(Op::Bin(BinOp::Pow, e)); + self.flush(3); + self.stack.push(Pending::Bin(BinOp::Pow)); return; } if self.symbol("*") { - let e = self.eval(e, 1); - self.stack.push(Op::Bin(BinOp::Mul, e)); + self.flush(1); + self.stack.push(Pending::Bin(BinOp::Mul)); return; } if self.symbol("/") { - let e = self.eval(e, 1); - self.stack.push(Op::Bin(BinOp::Div, e)); + self.flush(1); + self.stack.push(Pending::Bin(BinOp::Div)); return; } if self.symbol("//") { - let e = self.eval(e, 1); - self.stack.push(Op::Bin(BinOp::IntDiv, e)); + self.flush(1); + self.stack.push(Pending::Bin(BinOp::IntDiv)); return; } if self.symbol("%") { - let e = self.eval(e, 1); - self.stack.push(Op::Bin(BinOp::Mod, e)); + self.flush(1); + self.stack.push(Pending::Bin(BinOp::Mod)); return; } if self.symbol("+") { - let e = self.eval(e, 0); - self.stack.push(Op::Bin(BinOp::Add, e)); + self.flush(0); + self.stack.push(Pending::Bin(BinOp::Add)); return; } if self.symbol("-") { - let e = self.eval(e, 0); - self.stack.push(Op::Bin(BinOp::Sub, e)); + self.flush(0); + self.stack.push(Pending::Bin(BinOp::Sub)); return; } - let e = self.eval(e, 1); - self.stack.push(Op::Bin(BinOp::Mul, e)); + self.flush(1); + self.stack.push(Pending::Bin(BinOp::Mul)); } - fn parse(&mut self) -> Option<Expr> { - let mut e = loop { + fn parse(&mut self) -> Option<()> { + loop { if self.symbol("(") { - self.stack.push(Op::Paren); + self.stack.push(Pending::Paren); continue; } if self.parse_fun() { continue; } - let mut e = Expr::Num(self.parse_const()?); + self.parse_const()?; while self.symbol(")") { loop { match self.stack.pop()? { - Op::Un(op) => e = op.expr(e), - Op::Bin(op, e1) => e = op.expr(e1, e), - Op::Paren => break, + Pending::Un(op) => self.result.push(Op::Un(op)), + Pending::Bin(op) => self.result.push(Op::Bin(op)), + Pending::Paren => break, } } } if self.peek().is_none() { - break e; + break; } - self.parse_op(e); - }; + self.parse_op(); + } 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(Pending::Paren) => return None, + Some(Pending::Un(op)) => self.result.push(Op::Un(op)), + Some(Pending::Bin(op)) => self.result.push(Op::Bin(op)), + None => return Some(()), } } } } -pub fn parse(expr: &str) -> Option<Expr> { +// parse turns an expression into bytecode. +pub fn parse(expr: &str) -> Option<Vec<Op>> { let mut p = Parser { r: Lexer { buf: expr }, first_token: None, stack: Vec::new(), + result: Vec::new(), }; - p.parse() + p.parse()?; + Some(p.result) } #[cfg(test)] mod tests { use super::*; - fn int(i: i128) -> Expr { - Expr::Num(Num::Int(i)) + fn int(i: i128) -> Op { + Op::Num(Num::Int(i)) } - fn float(f: f64) -> Expr { - Expr::Num(Num::Float(f)) + fn float(f: f64) -> Op { + Op::Num(Num::Float(f)) } #[test] fn parse_int() { - assert_eq!(parse("500"), Some(int(500))); + assert_eq!(parse("500"), Some(vec![int(500)])); } #[test] fn parse_float() { - assert_eq!(parse("1e2"), Some(float(100.))); + assert_eq!(parse("1e2"), Some(vec![float(100.)])); } #[test] fn parse_fun() { assert_eq!( parse("sin pi"), - Some(UnOp::Sin.expr(float(std::f64::consts::PI))) + Some(vec![float(std::f64::consts::PI), Op::Un(UnOp::Sin)]) ); } @@ -280,7 +287,7 @@ mod tests { fn parse_nested_fun() { assert_eq!( parse("log log 100"), - Some(UnOp::Log.expr(UnOp::Log.expr(int(100)))) + Some(vec![int(100), Op::Un(UnOp::Log), Op::Un(UnOp::Log)]) ); } @@ -288,7 +295,13 @@ mod tests { fn parse_power() { assert_eq!( parse("2^1^2"), - Some(BinOp::Pow.expr(int(2), BinOp::Pow.expr(int(1), int(2)))) + Some(vec![ + int(2), + int(1), + int(2), + Op::Bin(BinOp::Pow), + Op::Bin(BinOp::Pow) + ]) ); } @@ -296,7 +309,7 @@ mod tests { fn parse_fun_power() { assert_eq!( parse("log 2^2"), - Some(BinOp::Pow.expr(UnOp::Log.expr(int(2)), int(2))) + Some(vec![int(2), Op::Un(UnOp::Log), int(2), Op::Bin(BinOp::Pow)]) ); } @@ -304,7 +317,7 @@ mod tests { fn parse_power_fun() { assert_eq!( parse("2^log 2"), - Some(BinOp::Pow.expr(int(2), UnOp::Log.expr(int(2)))) + Some(vec![int(2), int(2), Op::Un(UnOp::Log), Op::Bin(BinOp::Pow)]) ); } @@ -312,7 +325,13 @@ mod tests { fn parse_pow_mul() { assert_eq!( parse("2^2*2"), - Some(BinOp::Mul.expr(BinOp::Pow.expr(int(2), int(2)), int(2))) + Some(vec![ + int(2), + int(2), + Op::Bin(BinOp::Pow), + int(2), + Op::Bin(BinOp::Mul) + ]) ); } @@ -320,7 +339,13 @@ mod tests { fn parse_mul_pow() { assert_eq!( parse("2*2^2"), - Some(BinOp::Mul.expr(int(2), BinOp::Pow.expr(int(2), int(2)))) + Some(vec![ + int(2), + int(2), + int(2), + Op::Bin(BinOp::Pow), + Op::Bin(BinOp::Mul) + ]) ); } @@ -328,7 +353,13 @@ mod tests { fn parse_mul_div() { assert_eq!( parse("2*2/2"), - Some(BinOp::Div.expr(BinOp::Mul.expr(int(2), int(2)), int(2))) + Some(vec![ + int(2), + int(2), + Op::Bin(BinOp::Mul), + int(2), + Op::Bin(BinOp::Div) + ]) ); } @@ -336,7 +367,13 @@ mod tests { fn parse_add_mul() { assert_eq!( parse("2+2*2"), - Some(BinOp::Add.expr(int(2), BinOp::Mul.expr(int(2), int(2)))) + Some(vec![ + int(2), + int(2), + int(2), + Op::Bin(BinOp::Mul), + Op::Bin(BinOp::Add) + ]) ); } @@ -344,7 +381,13 @@ mod tests { fn parse_mul_add() { assert_eq!( parse("2*2+2"), - Some(BinOp::Add.expr(BinOp::Mul.expr(int(2), int(2)), int(2))) + Some(vec![ + int(2), + int(2), + Op::Bin(BinOp::Mul), + int(2), + Op::Bin(BinOp::Add) + ]) ); } @@ -352,7 +395,7 @@ mod tests { fn parse_fun_add() { assert_eq!( parse("log 2+2"), - Some(BinOp::Add.expr(UnOp::Log.expr(int(2)), int(2))), + Some(vec![int(2), Op::Un(UnOp::Log), int(2), Op::Bin(BinOp::Add)]) ); } @@ -360,7 +403,13 @@ mod tests { fn parse_parens() { assert_eq!( parse("(1+2)*3"), - Some(BinOp::Mul.expr(BinOp::Add.expr(int(1), int(2)), int(3))) + Some(vec![ + int(1), + int(2), + Op::Bin(BinOp::Add), + int(3), + Op::Bin(BinOp::Mul) + ]) ); } @@ -368,7 +417,11 @@ mod tests { fn parse_implicit_multiplication() { assert_eq!( parse("2pi"), - Some(BinOp::Mul.expr(int(2), float(std::f64::consts::PI))), + Some(vec![ + int(2), + float(std::f64::consts::PI), + Op::Bin(BinOp::Mul) + ]) ); } @@ -376,7 +429,14 @@ mod tests { 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))) + Some(vec![ + int(2), + Op::Un(UnOp::Neg), + int(2), + Op::Bin(BinOp::Mul), + int(2), + Op::Bin(BinOp::Sub) + ]) ); } @@ -384,7 +444,12 @@ mod tests { fn parse_fun_implicit_multiplication() { assert_eq!( parse("sin 2pi"), - Some(BinOp::Mul.expr(UnOp::Sin.expr(int(2)), float(std::f64::consts::PI))), + Some(vec![ + int(2), + Op::Un(UnOp::Sin), + float(std::f64::consts::PI), + Op::Bin(BinOp::Mul) + ]) ); } @@ -392,7 +457,13 @@ mod tests { fn parse_unary_negate() { assert_eq!( parse("1---2"), - Some(BinOp::Sub.expr(int(1), UnOp::Neg.expr(UnOp::Neg.expr(int(2))))) + Some(vec![ + int(1), + int(2), + Op::Un(UnOp::Neg), + Op::Un(UnOp::Neg), + Op::Bin(BinOp::Sub) + ]) ); } @@ -400,17 +471,22 @@ mod tests { fn parse_negate_fun() { assert_eq!( parse("log-log 2"), - Some(UnOp::Log.expr(UnOp::Neg.expr(UnOp::Log.expr(int(2))))) + Some(vec![ + int(2), + Op::Un(UnOp::Log), + Op::Un(UnOp::Neg), + Op::Un(UnOp::Log) + ]) ); } #[test] fn parse_unmatched_parens() { - assert_eq!(parse("0))))"), None,); + assert_eq!(parse("0))))"), None); } #[test] fn parse_unmatched_op() { - assert_eq!(parse("2^2+"), None,); + assert_eq!(parse("2^2+"), None); } } |
