From cd758a7fced390a91bcf089f2b62827a2670a2a1 Mon Sep 17 00:00:00 2001
From: Snorre <snorre@altschul.dk>
Date: Mon, 24 Feb 2025 17:13:40 +0100
Subject: [PATCH] git

---
 src/lib/node/constant.rs |  6 ++-
 src/lib/node/divide.rs   | 23 +++++++++++-
 src/lib/node/multiply.rs | 20 ++++++++--
 src/lib/node/subtract.rs | 62 +++++++++++++++++++++++++++++--
 src/lib/parser/mod.rs    | 80 ++++++++++++++++++++++++++++++++++------
 5 files changed, 168 insertions(+), 23 deletions(-)

diff --git a/src/lib/node/constant.rs b/src/lib/node/constant.rs
index 64f00a2..c9a79ed 100644
--- a/src/lib/node/constant.rs
+++ b/src/lib/node/constant.rs
@@ -26,7 +26,11 @@ impl Node for Constant {
         if self.value.is_zero() {
             return "0".to_string();
         }
-            
+
+        if self.value < 1. && self.value > 0.001 {
+            return self.value.to_f64().to_string();
+        }
+
         self.value
             .to_string_radix(10, Some(8))
             .trim_end_matches('0')
diff --git a/src/lib/node/divide.rs b/src/lib/node/divide.rs
index 5fb23a3..a8a3f0a 100644
--- a/src/lib/node/divide.rs
+++ b/src/lib/node/divide.rs
@@ -26,8 +26,27 @@ impl Node for Divide {
             }
 
             // Zero rule
-            (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => {
-                Ok(evaluated_left)
+            (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_left),
+
+            // Cancel out symbols and constants
+            (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
+                match (
+                    (m1.get_left().as_ref(), m1.get_right().as_ref()),
+                    (m2.get_left().as_ref(), m2.get_right().as_ref()),
+                ) {
+                    ((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2))
+                    | ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2))
+                    | ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2)))
+                    | ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => {
+                        if s1 == s2 {
+                            Divide::new(Rc::new(o1.clone()), Rc::new(o2.clone())).evaluate(env)
+                        } else {
+                            Ok(Divide::new_rc(evaluated_left, evaluated_right))
+                        }
+                    }
+
+                    _ => Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into())),
+                }
             }
 
             // Constant / Constant = Constant
diff --git a/src/lib/node/multiply.rs b/src/lib/node/multiply.rs
index 10cf7ce..b9b42f1 100644
--- a/src/lib/node/multiply.rs
+++ b/src/lib/node/multiply.rs
@@ -63,6 +63,18 @@ impl Node for Multiply {
                 div.get_right(),
             )),
 
+            // 0.5*n -> n/2
+            (NodeEnum::Constant(c), _) if c.get_value() == &0.5 => Divide::new(
+                evaluated_right,
+                Rc::new(Constant::new_from_float(2, &env).into()),
+            )
+            .evaluate(env),
+            (_, NodeEnum::Constant(c)) if c.get_value() == &0.5 => Divide::new(
+                evaluated_left,
+                Rc::new(Constant::new_from_float(2, &env).into()),
+            )
+            .evaluate(env),
+
             // Constant * Constant = Constant
             (NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
                 Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64(
@@ -106,10 +118,10 @@ impl Node for Multiply {
                 Ok(Set::new(values))
             }
 
-            (NodeEnum::Constant(c), NodeEnum::Multiply(m))
-            | (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => {
-                Self::collapse_nested_multiply(c, m, env)?.evaluate(env)
-            }
+            // (NodeEnum::Constant(c), NodeEnum::Multiply(m))
+            // | (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => {
+            //     Self::collapse_nested_multiply(c, m, env)?.evaluate(env)
+            // }
 
             // (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
             //     Self::move_constants_to_left(evaluated_left.clone(), evaluated_right.clone())
diff --git a/src/lib/node/subtract.rs b/src/lib/node/subtract.rs
index 3eee08b..d6bdbc1 100644
--- a/src/lib/node/subtract.rs
+++ b/src/lib/node/subtract.rs
@@ -2,7 +2,7 @@ use std::rc::Rc;
 
 use rug::Float;
 
-use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
+use super::{Environment, Node, NodeEnum, Precedence, constant::Constant, multiply::Multiply};
 
 #[derive(Clone, Debug, PartialEq, PartialOrd)]
 pub struct Subtract {
@@ -35,9 +35,63 @@ impl Node for Subtract {
             (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
                 Subtract::new(evaluated_right, evaluated_left).into(),
             )),
-            _ => Ok(Rc::new(
-                Subtract::new(evaluated_left, evaluated_right).into(),
-            )),
+
+            (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
+                match (
+                    (m1.get_left().as_ref(), m1.get_right().as_ref()),
+                    (m2.get_left().as_ref(), m2.get_right().as_ref()),
+                ) {
+                    ((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2))
+                    | ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2))
+                    | ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2)))
+                    | ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => {
+                        if s1 == s2 {
+                            Multiply::new_rc(
+                                Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone()))
+                                    .evaluate(env)?,
+                                Rc::new(s1.clone().into()),
+                            )
+                            .evaluate(env)
+                        } else {
+                            Ok(Rc::new(
+                                Subtract::new(evaluated_left, evaluated_right).into(),
+                            ))
+                        }
+                    }
+
+                    ((NodeEnum::Exponent(e1), o1), (NodeEnum::Exponent(e2), o2))
+                    | ((o1, NodeEnum::Exponent(e1)), (NodeEnum::Exponent(e2), o2))
+                    | ((NodeEnum::Exponent(e1), o1), (o2, NodeEnum::Exponent(e2)))
+                    | ((o1, NodeEnum::Exponent(e1)), (o2, NodeEnum::Exponent(e2))) => {
+                        if e1 == e2 {
+                            Multiply::new_rc(
+                                Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone()))
+                                    .evaluate(env)?,
+                                Rc::new(e1.clone().into()),
+                            )
+                            .evaluate(env)
+                        } else {
+                            Ok(Rc::new(
+                                Subtract::new(evaluated_left, evaluated_right).into(),
+                            ))
+                        }
+                    }
+
+                    _ => Ok(Rc::new(
+                        Subtract::new(evaluated_left, evaluated_right).into(),
+                    )),
+                }
+            }
+
+            _ => {
+                if evaluated_left == evaluated_right {
+                    Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
+                } else {
+                    Ok(Rc::new(
+                        Subtract::new(evaluated_left, evaluated_right).into(),
+                    ))
+                }
+            }
         }
     }
 
diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs
index 4ea4ab3..b9158e9 100644
--- a/src/lib/parser/mod.rs
+++ b/src/lib/parser/mod.rs
@@ -1,4 +1,9 @@
-use std::{iter::Peekable, rc::Rc, str::Chars, vec::IntoIter};
+use std::{
+    iter::Peekable,
+    rc::Rc,
+    str::{Chars, SplitTerminator},
+    vec::IntoIter,
+};
 
 use rug::Float;
 
@@ -63,6 +68,8 @@ pub enum TokenType {
 
     True,
     False,
+
+    Terminator,
 }
 
 impl TokenType {
@@ -100,6 +107,8 @@ impl TokenType {
 
             TokenType::True => 4,
             TokenType::False => 5,
+
+            Self::Terminator => 1,
         }
     }
 }
@@ -240,6 +249,8 @@ impl<'a> Lexer<'a> {
                     tokens.push(Token(i, TokenType::ColonEquals));
                 }
 
+                ';' => tokens.push(Token(i, TokenType::Terminator)),
+
                 '(' => tokens.push(Token(i, TokenType::LParen)),
                 ')' => tokens.push(Token(i, TokenType::RParen)),
 
@@ -293,6 +304,7 @@ impl<'a> Lexer<'a> {
     }
 }
 
+#[derive(Debug)]
 pub enum ParserError {
     UnexpectedEndOfTokens(String),
     UnexpectedToken(usize, usize, String),
@@ -325,8 +337,18 @@ impl<'a> Parser<'a> {
     pub fn parse(&mut self) -> Result<Vec<Rc<NodeEnum>>, ParserError> {
         let mut expressions = vec![];
 
-        while self.tokens.peek().is_some() {
+        if self.tokens.len() == 0 {
+            return Ok(expressions);
+        }
+
+        loop {
             expressions.push(self.expression()?);
+
+            if !self.is_at_end() {
+                self.match_or_err(TokenType::Terminator)?;
+            } else {
+                break;
+            }
         }
 
         Ok(expressions)
@@ -462,16 +484,31 @@ impl<'a> Parser<'a> {
     }
 
     fn factor(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
-        let expr = self.unary()?;
-        if let Some(Token(_, TokenType::Star)) = self.tokens.peek() {
-            self.consume();
-            Ok(Rc::new(Multiply::new(expr, self.comparison()?).into()))
-        } else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() {
-            self.consume();
-            Ok(Rc::new(Divide::new(expr, self.comparison()?).into()))
-        } else {
-            Ok(expr)
+        let mut expr = self.unary()?;
+        // if let Some(Token(_, TokenType::Star)) = self.tokens.peek() {
+        //     self.consume();
+        //     Ok(Rc::new(Multiply::new(expr, self.comparison()?).into()))
+        // } else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() {
+        //     self.consume();
+        //     Ok(Rc::new(Divide::new(expr, self.comparison()?).into()))
+        // } else {
+        //     Ok(expr)
+        // }
+
+        loop {
+            if self.match_type(TokenType::Star) {
+                let right = self.unary()?;
+                expr = Multiply::new_rc(expr, right);
+                continue;
+            } else if self.match_type(TokenType::Slash) {
+                let right = self.unary()?;
+                expr = Divide::new_rc(expr, right);
+                continue;
+            }
+            break;
         }
+
+        Ok(expr)
     }
 
     fn unary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
@@ -729,7 +766,7 @@ impl<'a> Parser<'a> {
             ));
         };
 
-        match token {
+        let expr = match token {
             TokenType::Number(value) => {
                 let value = if let Ok(incomplete) = Float::parse(&value) {
                     Float::with_val_64(self.environment.get_float_precision(), incomplete)
@@ -775,6 +812,25 @@ impl<'a> Parser<'a> {
                 token.len(),
                 format!("Unexpected token {token:?}"),
             )),
+        };
+
+        // Implicit multiplication
+        if !self.is_at_end() {
+            match self.tokens.peek().unwrap() {
+                Token(_, TokenType::Identifier(_)) | Token(_, TokenType::Number(_)) => {
+                    return Ok(Multiply::new_rc(expr?, self.primary()?));
+                }
+                Token(_, TokenType::LParen) if expr.is_ok() => {
+                    if let NodeEnum::Symbol(_) = expr.as_ref().unwrap().as_ref() {
+                        return expr;
+                    }
+
+                    return Ok(Multiply::new_rc(expr?, self.primary()?));
+                }
+                _ => {}
+            }
         }
+
+        expr
     }
 }