aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/expr.rs135
-rw-r--r--src/main.rs6
-rw-r--r--src/op.rs93
-rw-r--r--src/parser.rs258
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);
}
}