From fa8aae88b446cffa121c54164ef6d76c78238543 Mon Sep 17 00:00:00 2001 From: Snorre Ettrup Altschul Date: Fri, 21 Feb 2025 20:42:56 +0100 Subject: [PATCH] did shit --- Cargo.lock | 122 ++++++++++++++++- Cargo.toml | 1 + src/cli-repl.rs | 23 +++- src/lib/environment.rs | 105 +++++++++++---- src/lib/node/add.rs | 18 +-- src/lib/node/call.rs | 23 ++-- src/lib/node/comparison.rs | 237 +++++++++++++++++++++++++++++++++ src/lib/node/constant.rs | 6 +- src/lib/node/divide.rs | 25 ++-- src/lib/node/empty.rs | 9 +- src/lib/node/equals.rs | 22 +-- src/lib/node/exponent.rs | 68 ++++++++++ src/lib/node/function.rs | 44 +++--- src/lib/node/if_else.rs | 6 +- src/lib/node/mod.rs | 100 +++++++++++--- src/lib/node/multiply.rs | 56 ++++++-- src/lib/node/set.rs | 2 +- src/lib/node/subtract.rs | 33 ++--- src/lib/node/symbol.rs | 12 +- src/lib/parser/mod.rs | 123 ++++++++++++++--- src/lib/tests/mod.rs | 266 +++++++++++++++++++------------------ 21 files changed, 991 insertions(+), 310 deletions(-) create mode 100644 src/lib/node/comparison.rs create mode 100644 src/lib/node/exponent.rs diff --git a/Cargo.lock b/Cargo.lock index c252c9b..fe55b28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -38,7 +50,7 @@ dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -369,10 +381,19 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ - "ahash", + "ahash 0.3.8", "autocfg", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", +] + [[package]] name = "hibitset" version = "0.6.4" @@ -388,6 +409,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -425,6 +455,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + [[package]] name = "libredox" version = "0.1.3" @@ -458,6 +494,51 @@ version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "malachite" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa282d5c3914eba1e50fd81012e04cfd5a79a08c29ece0fa20ef73afcbd7804" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef157484a1d7246fbff1a4fd2c148b83ac7324161963af61001ea43dc9086076" +dependencies = [ + "hashbrown 0.14.5", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-nz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de4451b2e0e0ec6e4f39ee311b72154d291b185f5450474ace5663e37d7f8360" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa9bd8eccda7ff0959d48e00e03ee3626817414442725b2cc1f1b2f215ceee9" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -511,6 +592,7 @@ dependencies = [ "clay-layout", "enum_dispatch", "font-kit", + "malachite", "raylib", "termion", "test-case", @@ -708,6 +790,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + [[package]] name = "same-file" version = "1.0.6" @@ -742,7 +830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" dependencies = [ "arrayvec", - "hashbrown", + "hashbrown 0.7.2", "mopa", "smallvec", "tynm", @@ -767,7 +855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" dependencies = [ "crossbeam-queue", - "hashbrown", + "hashbrown 0.7.2", "hibitset", "log", "shred", @@ -894,6 +982,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -1111,3 +1205,23 @@ dependencies = [ "once_cell", "pkg-config", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] diff --git a/Cargo.toml b/Cargo.toml index c894d61..8882d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" clay-layout = { path = "../clay-ui-rust" } enum_dispatch = "0.3.13" font-kit = "0.14.2" +malachite = "0.5.0" raylib = { version = "5.0.2", features = ["wayland"] } termion = "4.0.3" test-case = "3.3.1" diff --git a/src/cli-repl.rs b/src/cli-repl.rs index 0c7d1d0..82ace64 100644 --- a/src/cli-repl.rs +++ b/src/cli-repl.rs @@ -1,8 +1,13 @@ use std::io::{self, StdoutLock, Write, stdout}; +use std::rc::Rc; use libopenbirch::environment::Environment; -use libopenbirch::node::Node; +use libopenbirch::node::constant::Constant; +use libopenbirch::node::empty::Empty; +use libopenbirch::node::{Node, NodeEnum}; use libopenbirch::parser::{Lexer, Parser, ParserError}; +use malachite::base::num::basic::traits::Zero; +use malachite::rational::Rational; #[cfg(feature = "async")] use termion::AsyncReader; use termion::color; @@ -148,12 +153,24 @@ fn print_err(i: usize, len: usize, exp: String) { ); } +fn print(args: &Vec>, env: &mut Environment) -> Result, String> { + for expr in args { + println!("\r{}", expr.as_string(Some(env))); + } + + Ok(Empty::new("")) +} + fn main() -> Result<(), io::Error> { let mut input = Input::new(); let mut env = Environment::new(); + env.define_native_function("print", print); + while let Some(source) = input.get()? { + input.disable_raw()?; + let mut lexer = Lexer::new(&source); let tokens_result = lexer.lex(); @@ -164,9 +181,6 @@ fn main() -> Result<(), io::Error> { continue; } - #[cfg(debug_assertions)] - input.disable_raw()?; - let tokens = tokens_result.unwrap(); let mut parser = Parser::new(tokens, &mut env); @@ -193,7 +207,6 @@ fn main() -> Result<(), io::Error> { } } - #[cfg(debug_assertions)] input.enable_raw()?; print!("{}", color::Fg(color::Reset)); diff --git a/src/lib/environment.rs b/src/lib/environment.rs index 7066308..3785ce1 100644 --- a/src/lib/environment.rs +++ b/src/lib/environment.rs @@ -1,16 +1,28 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::HashMap, rc::Rc, thread::current}; -use crate::node::NodeEnum; +use crate::node::{ + NodeEnum, + function::{self, Function, FunctionType, NativeFunctionType}, +}; pub type EnvironmentInternalSymbolKey = u16; // pub type Environment = HashMap>; +struct Scope { + pub stack_len: usize, + pub shadow_len: usize, +} + pub struct Environment { map: HashMap>, stack_depth: usize, max_stack_depth: usize, - stack: Vec>, + stack_sizes: Vec, + stack: Vec, + stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc)>, + + // stack: Vec>, symbol_to_id: HashMap, id_to_symbol: HashMap, unique_keys: EnvironmentInternalSymbolKey, @@ -22,7 +34,10 @@ impl Environment { map: HashMap::new(), stack_depth: 0, max_stack_depth: 5000, - stack: vec![vec![]], + + stack_sizes: vec![], + stack: vec![], + stack_shadows: vec![], symbol_to_id: HashMap::new(), id_to_symbol: HashMap::new(), @@ -31,61 +46,101 @@ impl Environment { } pub fn push_stack(&mut self) -> Result<(), String> { - debug_assert!(self.stack_depth == self.stack.len() - 1); - if self.stack_depth == self.max_stack_depth { self.unwind_stack(); return Err("Max Stack depth exceeded".to_owned()); } self.stack_depth += 1; - self.stack.push(vec![]); + self.stack_sizes.push(Scope { + stack_len: 0, + shadow_len: 0, + }); - debug_assert!(self.stack_depth == self.stack.len() - 1); + // debug_assert!(self.stack_depth == self.stack.len() - 1); Ok(()) } pub fn pop_stack(&mut self) -> Result<(), String> { - debug_assert!( - self.stack_depth == self.stack.len() - 1, - "Expected stack len: {}, stack_depth: {}", - self.stack.len(), - self.stack_depth - ); - if self.stack_depth == 0 { return Err("Trying to pop empty stack".to_owned()); } self.stack_depth -= 1; - - for item in self.stack.pop().unwrap() { - self.map.remove(&item); + let scope = self.stack_sizes.pop().unwrap(); + for _ in 0..scope.stack_len { + self.map.remove(&self.stack.pop().unwrap()); + } + for _ in 0..scope.shadow_len { + let (id, value) = self.stack_shadows.pop().unwrap(); + self.map.insert(id, value); } - - debug_assert!(self.stack_depth == self.stack.len() - 1); Ok(()) } fn unwind_stack(&mut self) { - for stack in &self.stack { - for item in stack { - self.map.remove(&item); + for scope in &self.stack_sizes { + for _ in 0..scope.stack_len { + self.map.remove(&self.stack.pop().unwrap()); } } - self.stack = vec![vec![]]; + self.stack_sizes = vec![]; + self.stack_shadows = vec![]; + self.stack_depth = 0; + } + + pub fn define(&mut self, name: String, value: Rc) { + let id = if let Some(value) = self.str_to_id(&name) { + *value + } else { + self.get_new_id() + }; + + self.map.insert(id, value); + self.insert_id_to_str(id, name); + } + + pub fn define_native_function(&mut self, name: &'static str, func: NativeFunctionType) { + let f = Function::new(FunctionType::Native(name, func)); + + self.define(name.to_owned(), f); } pub fn get(&self, key: &EnvironmentInternalSymbolKey) -> Option<&Rc> { self.map.get(key) } + fn get_current_scope(&mut self) -> &[u16] { + let len = self.stack.len(); + let current_scope_size = self.stack_sizes.last().unwrap().stack_len; + let start = len - current_scope_size; + self.stack.get(start..len).unwrap() + } + pub fn insert(&mut self, key: EnvironmentInternalSymbolKey, value: Rc) { + if self.stack_depth != 0 { + let mut shadow = false; + if let Some(existing) = self.map.get(&key) { + if !self.get_current_scope().contains(&key) { + // We need to shadow this variable + self.stack_shadows + .push((key, self.map.insert(key, value.clone()).unwrap())); + self.stack.push(key); + shadow = true; + } + } + let scope = unsafe { self.stack_sizes.get_unchecked_mut(self.stack_depth - 1) }; + scope.stack_len += 1; + self.stack.push(key); + if shadow { + scope.shadow_len += 1; + return; + } + } self.map.insert(key, value); - unsafe { self.stack.get_unchecked_mut(self.stack_depth).push(key) } } pub fn str_to_id(&self, value: &String) -> Option<&EnvironmentInternalSymbolKey> { diff --git a/src/lib/node/add.rs b/src/lib/node/add.rs index 0b86675..d6aa134 100644 --- a/src/lib/node/add.rs +++ b/src/lib/node/add.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use malachite::{base::num::basic::traits::Zero, rational::Rational}; + use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; #[derive(Clone, Debug, PartialEq, PartialOrd)] @@ -15,20 +17,16 @@ impl Node for Add { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Zero rule - (NodeEnum::Constant(zero), _) if zero.get_value() == 0. => Ok(evaluated_right), - (_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => Ok(evaluated_left), + (NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), + (_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), + // Constant + Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into())) } - // Symbol + Constant we just return the same - (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => { - Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())) - } - - // Constant + Symbol we switch them around so the constant is last + // Move constants after symbols in add (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => { Ok(Rc::new(Add::new(evaluated_right, evaluated_left).into())) } @@ -61,6 +59,10 @@ impl Add { Self { left, right } } + pub fn new_rc(left: Rc, right: Rc) -> Rc { + Rc::new(Self { left, right }.into()) + } + pub fn get_left(&self) -> Rc { self.left.clone() } diff --git a/src/lib/node/call.rs b/src/lib/node/call.rs index f6eea5e..bb93042 100644 --- a/src/lib/node/call.rs +++ b/src/lib/node/call.rs @@ -35,27 +35,24 @@ impl Node for Call { ); }; - // Check if argument counts match - let fargs = func.get_arguments(); - if fargs.len() != arguments.len() { - return Err(format!( - "Error calling function. Expected {} arguments, but got {}", - func.get_arguments().len(), - arguments.len() - )); - } - // Call function body with arguments match func.get_body() { // Pass arguments to native function - FunctionType::Native(_name, native_function) => native_function(&arguments), - FunctionType::UserFunction(node_enum) => { + FunctionType::Native(_name, native_function) => native_function(&arguments, env), + FunctionType::UserFunction(body, fargs) => { + if fargs.len() != arguments.len() { + return Err(format!( + "Error calling function. Expected {} arguments, but got {}", + fargs.len(), + arguments.len() + )); + } env.push_stack()?; // Define variables fargs.iter().zip(arguments).for_each(|(symbol, value)| { env.insert(symbol.get_value(), value.clone()); }); - let ev = node_enum.evaluate(env)?; + let ev = body.evaluate(env)?; env.pop_stack()?; // Return evaluated return value for function Ok(ev) diff --git a/src/lib/node/comparison.rs b/src/lib/node/comparison.rs new file mode 100644 index 0000000..6464999 --- /dev/null +++ b/src/lib/node/comparison.rs @@ -0,0 +1,237 @@ +use std::rc::Rc; + +use crate::environment::Environment; + +use super::{Node, NodeEnum, Precedence, if_else::Bool}; + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Greater { + left: Rc, + right: Vec>, +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Less { + left: Rc, + right: Vec>, +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct GreaterEquals { + left: Rc, + right: Vec>, +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct LessEquals { + left: Rc, + right: Vec>, +} + +impl Node for Greater { + fn evaluate(&self, env: &mut Environment) -> Result, String> { + let mut expr = self.left.evaluate(env)?; + for e in &self.right { + let e = e.evaluate(env)?; + let evaluation = expr.partial_cmp(&e); + match evaluation { + Some(cmp) if cmp != std::cmp::Ordering::Greater => { + return Ok(Rc::new(Bool::False.into())); + } + None => { + return self.abort(env); + } + _ => {} + } + expr = e; + } + return Ok(Rc::new(Bool::True.into())); + } + + fn as_string(&self, env: Option<&Environment>) -> String { + format!( + "{} > {}", + self.left.as_string(env), + self.right + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + " > " + &b) + .unwrap() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Compare + } +} + +impl Node for Less { + fn evaluate(&self, env: &mut Environment) -> Result, String> { + let mut expr = self.left.evaluate(env)?; + for e in &self.right { + let e = e.evaluate(env)?; + let evaluation = expr.partial_cmp(&e); + match evaluation { + Some(cmp) if cmp != std::cmp::Ordering::Less => { + return Ok(Rc::new(Bool::False.into())); + } + None => { + return self.abort(env); + } + _ => {} + } + expr = e; + } + return Ok(Rc::new(Bool::True.into())); + } + + fn as_string(&self, env: Option<&Environment>) -> String { + format!( + "{} < {}", + self.left.as_string(env), + self.right + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + " < " + &b) + .unwrap() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Compare + } +} + +impl Node for GreaterEquals { + fn evaluate(&self, env: &mut Environment) -> Result, String> { + let mut expr = self.left.evaluate(env)?; + for e in &self.right { + let e = e.evaluate(env)?; + let evaluation = expr.partial_cmp(&e); + match evaluation { + Some(cmp) + if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Greater => + { + return Ok(Rc::new(Bool::False.into())); + } + None => { + return self.abort(env); + } + _ => {} + } + expr = e; + } + return Ok(Rc::new(Bool::True.into())); + } + + fn as_string(&self, env: Option<&Environment>) -> String { + format!( + "{} >= {}", + self.left.as_string(env), + self.right + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + " >= " + &b) + .unwrap() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Compare + } +} + +impl Node for LessEquals { + fn evaluate(&self, env: &mut Environment) -> Result, String> { + let mut expr = self.left.evaluate(env)?; + for e in &self.right { + let e = e.evaluate(env)?; + let evaluation = expr.partial_cmp(&e); + match evaluation { + Some(cmp) + if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Less => + { + return Ok(Rc::new(Bool::False.into())); + } + None => { + return self.abort(env); + } + _ => {} + } + expr = e; + } + return Ok(Rc::new(Bool::True.into())); + } + + fn as_string(&self, env: Option<&Environment>) -> String { + format!( + "{} <= {}", + self.left.as_string(env), + self.right + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + " <= " + &b) + .unwrap() + ) + } + + fn precedence(&self) -> Precedence { + Precedence::Compare + } +} + +impl Greater { + pub fn new(left: Rc, right: Vec>) -> Rc { + Rc::new(Self { left, right }.into()) + } + + fn abort(&self, env: &mut Environment) -> Result, String> { + let mut arguments = vec![]; + for value in self.right.iter() { + arguments.push(value.evaluate(env)?); + } + Ok(Self::new(self.left.evaluate(env)?, arguments)) + } +} + +impl Less { + pub fn new(left: Rc, right: Vec>) -> Rc { + Rc::new(Self { left, right }.into()) + } + + fn abort(&self, env: &mut Environment) -> Result, String> { + let mut arguments = vec![]; + for value in self.right.iter() { + arguments.push(value.evaluate(env)?); + } + Ok(Self::new(self.left.evaluate(env)?, arguments)) + } +} + +impl LessEquals { + pub fn new(left: Rc, right: Vec>) -> Rc { + Rc::new(Self { left, right }.into()) + } + + fn abort(&self, env: &mut Environment) -> Result, String> { + let mut arguments = vec![]; + for value in self.right.iter() { + arguments.push(value.evaluate(env)?); + } + Ok(Self::new(self.left.evaluate(env)?, arguments)) + } +} + +impl GreaterEquals { + pub fn new(left: Rc, right: Vec>) -> Rc { + Rc::new(Self { left, right }.into()) + } + + fn abort(&self, env: &mut Environment) -> Result, String> { + let mut arguments = vec![]; + for value in self.right.iter() { + arguments.push(value.evaluate(env)?); + } + Ok(Self::new(self.left.evaluate(env)?, arguments)) + } +} diff --git a/src/lib/node/constant.rs b/src/lib/node/constant.rs index 00d2573..547c36b 100644 --- a/src/lib/node/constant.rs +++ b/src/lib/node/constant.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use super::{Environment, Node, Precedence}; -pub type ConstantValue = f64; +pub type ConstantValue = malachite::rational::Rational; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Constant { @@ -34,8 +34,8 @@ impl Constant { Self { value } } - pub fn get_value(&self) -> ConstantValue { - self.value + pub fn get_value(&self) -> &ConstantValue { + &self.value } pub fn set_value(&mut self, value: ConstantValue) { diff --git a/src/lib/node/divide.rs b/src/lib/node/divide.rs index 1cd2ab5..1b5cdc5 100644 --- a/src/lib/node/divide.rs +++ b/src/lib/node/divide.rs @@ -1,6 +1,8 @@ use std::rc::Rc; -use super::{constant::Constant, set::Set, Node, NodeEnum, Precedence}; +use malachite::{base::num::basic::traits::Zero, rational::Rational}; + +use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Divide { @@ -15,27 +17,20 @@ impl Node for Divide { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Error if dividing by zero - (_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => { + (_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => { Err("Division by Zero".into()) } + (_, _) if evaluated_left == evaluated_right => Ok(Rc::new(Constant::new(Rational::ZERO).into())), + // Zero rule - (NodeEnum::Constant(zero), _) if zero.get_value() == 0. => { - Ok(evaluated_left) - } + (NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), // Constant + Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { Ok(Rc::new(Constant::new(a.get_value() / b.get_value()).into())) } - // Symbol + Constant we just return the same - (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => { - Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into())) - } - // Constant + Symbol we switch them around so the constant is last - (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => { - Ok(Rc::new(Divide::new(evaluated_right, evaluated_left).into())) - } + // Divide a set with a constant (NodeEnum::Set(s), NodeEnum::Constant(c)) => { let old_values = s.get_values(); @@ -90,6 +85,10 @@ impl Divide { Self { left, right } } + pub fn new_rc(left: Rc, right: Rc) -> Rc { + Rc::new(Self::new(left, right).into()) + } + pub fn get_left(&self) -> Rc { self.left.clone() } diff --git a/src/lib/node/empty.rs b/src/lib/node/empty.rs index 565ba9a..a037198 100644 --- a/src/lib/node/empty.rs +++ b/src/lib/node/empty.rs @@ -2,11 +2,18 @@ use std::{rc::Rc, sync::LazyLock}; use super::{Environment, Node, NodeEnum, Precedence}; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialOrd)] pub struct Empty { message: Option, } +impl PartialEq for Empty { + fn eq(&self, other: &Self) -> bool { + true + } +} +impl Eq for Empty {} + impl Node for Empty { fn evaluate(&self, _: &mut Environment) -> Result, String> { Ok(Empty::EMPTY.clone()) diff --git a/src/lib/node/equals.rs b/src/lib/node/equals.rs index e1194f8..f228689 100644 --- a/src/lib/node/equals.rs +++ b/src/lib/node/equals.rs @@ -5,25 +5,31 @@ use super::{Environment, Node, NodeEnum, Precedence, if_else::Bool}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Equals { left: Rc, - right: Rc, + right: Vec>, } impl Node for Equals { fn evaluate(&self, env: &mut Environment) -> Result, String> { let left = self.left.evaluate(env)?; - let right = self.right.evaluate(env)?; - match left == right { - true => Ok(Rc::new(Bool::True.into())), - false => Ok(Rc::new(Bool::False.into())), + for expr in &self.right { + if left != expr.evaluate(env)? { + return Ok(Rc::new(Bool::False.into())); + } } + + return Ok(Rc::new(Bool::True.into())); } fn as_string(&self, env: Option<&Environment>) -> String { format!( - "({}={})", + "{} = {}", self.left.as_string(env), - self.right.as_string(env) + self.right + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + " = " + &b) + .unwrap() ) } @@ -33,7 +39,7 @@ impl Node for Equals { } impl Equals { - pub fn new(left: Rc, right: Rc) -> Self { + pub fn new(left: Rc, right: Vec>) -> Self { Self { left, right } } } diff --git a/src/lib/node/exponent.rs b/src/lib/node/exponent.rs new file mode 100644 index 0000000..108de7e --- /dev/null +++ b/src/lib/node/exponent.rs @@ -0,0 +1,68 @@ +use std::rc::Rc; + +use malachite::{Natural, rational::Rational, rational::arithmetic::pow}; + +use malachite::base::num::arithmetic::traits::Pow; + +use malachite::rational::conversion::integer_from_rational; + +use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Exponent { + left: Rc, + right: Rc, +} + +impl Node for Exponent { + fn evaluate(&self, env: &mut super::Environment) -> Result, String> { + let evaluated_left = self.left.evaluate(env)?; + let evaluated_right = self.right.evaluate(env)?; + + match (evaluated_left.as_ref(), evaluated_right.as_ref()) { + (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { + let v = b.get_value(); + let exp: u64 = if let Ok(u) = v.try_into() { + u + } else { + return Err("Exponent was too large".into()); + }; + Ok(Rc::new(Constant::new(a.get_value().pow(exp)).into())) + } + + _ => Ok(Self::new(evaluated_left, evaluated_right)), + } + } + + fn as_string(&self, env: Option<&Environment>) -> String { + let left_string = if self.left.precedence() <= self.precedence() { + format!("({})", self.left.as_string(env)) + } else { + self.left.as_string(env) + }; + let right_string = if self.right.precedence() <= self.precedence() { + format!("({})", self.right.as_string(env)) + } else { + self.right.as_string(env) + }; + format!("{}^{}", left_string, right_string) + } + + fn precedence(&self) -> Precedence { + Precedence::Exponent + } +} + +impl Exponent { + pub fn new(left: Rc, right: Rc) -> Rc { + Rc::new(Self { left, right }.into()) + } + + pub fn get_left(&self) -> &Rc { + &self.left + } + + pub fn get_right(&self) -> &Rc { + &self.right + } +} diff --git a/src/lib/node/function.rs b/src/lib/node/function.rs index ecaa5e9..3f96121 100644 --- a/src/lib/node/function.rs +++ b/src/lib/node/function.rs @@ -4,21 +4,17 @@ use crate::environment::Environment; use super::{Node, NodeEnum, Precedence, symbol::Symbol}; +pub type NativeFunctionType = fn(&Vec>, env: &mut Environment) -> Result, String>; + #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum FunctionType { - Native( - &'static str, - fn(&Vec>) -> Result, String>, - ), - UserFunction(Rc), + Native(&'static str, NativeFunctionType), + UserFunction(Rc, Vec), } #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Function { function: FunctionType, - // TODO: Finish reasoning about whether or not this is - // the right way to implement functions - arguments: Vec, } impl Node for Function { @@ -27,16 +23,16 @@ impl Node for Function { } fn as_string(&self, env: Option<&Environment>) -> String { - let args = self - .arguments - .iter() - .map(|x| Node::as_string(x, env)) - .reduce(|acc, e| format!("{acc}, {e}")) - .unwrap_or("()".to_owned()); - match &self.function { - FunctionType::Native(name, _) => name.to_owned().to_owned(), - FunctionType::UserFunction(body) => format!("({args} -> {})", body.as_string(env)), + FunctionType::Native(name, _) => format!("(Native Function `{name}`)"), + FunctionType::UserFunction(body, args) => format!( + "({} -> {})", + args.iter() + .map(|x| x.as_string(env)) + .reduce(|acc, e| format!("{acc}, {e}")) + .unwrap_or("()".to_owned()), + body.as_string(env) + ), } } @@ -46,21 +42,11 @@ impl Node for Function { } impl Function { - pub fn new(t: FunctionType, args: Vec) -> Rc { - Rc::new( - Self { - function: t, - arguments: args, - } - .into(), - ) + pub fn new(t: FunctionType) -> Rc { + Rc::new(Self { function: t }.into()) } pub fn get_body(&self) -> &FunctionType { &self.function } - - pub fn get_arguments(&self) -> &Vec { - &self.arguments - } } diff --git a/src/lib/node/if_else.rs b/src/lib/node/if_else.rs index 20eab5d..0f6a3a1 100644 --- a/src/lib/node/if_else.rs +++ b/src/lib/node/if_else.rs @@ -64,12 +64,12 @@ impl Node for IfElse { for expr in to_evaluate { expr.evaluate(env)?; } - last.evaluate(env) + last.evaluate(env)? } else { - Ok(Empty::EMPTY.clone()) + Empty::EMPTY.clone() }; env.pop_stack()?; - ret + Ok(ret) } match condition { diff --git a/src/lib/node/mod.rs b/src/lib/node/mod.rs index 6db3aa4..4d32092 100644 --- a/src/lib/node/mod.rs +++ b/src/lib/node/mod.rs @@ -3,38 +3,42 @@ use std::{fmt::Display, rc::Rc}; use add::Add; use assign::Assign; use call::Call; +use comparison::{Greater, GreaterEquals, Less, LessEquals}; use constant::Constant; use divide::Divide; use empty::Empty; use enum_dispatch::enum_dispatch; +use equals::Equals; +use exponent::Exponent; use function::Function; use if_else::{Bool, IfElse}; use multiply::Multiply; +use set::Set; use subtract::Subtract; use symbol::Symbol; -use equals::Equals; -use set::Set; use crate::environment::Environment; pub mod add; +pub mod assign; +pub mod call; +pub mod comparison; pub mod constant; pub mod divide; +pub mod empty; +pub mod equals; +pub mod exponent; +pub mod function; +pub mod if_else; pub mod multiply; +pub mod node_ref; +pub mod set; pub mod subtract; pub mod symbol; -pub mod node_ref; -pub mod assign; -pub mod empty; -pub mod function; -pub mod call; -pub mod if_else; -pub mod equals; -pub mod set; #[enum_dispatch] #[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)] -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq)] pub enum NodeEnum { Constant, @@ -43,6 +47,7 @@ pub enum NodeEnum { Subtract, Multiply, Divide, + Exponent, Symbol, // NodeRef, // DEPRECATED, use Symbol @@ -57,17 +62,15 @@ pub enum NodeEnum { Set, Equals, - // Greater, - // GreaterEquals, - // Less, - // LessEquals - - // Logical operators - // In, - // Where, - // Not, - // Or, - // And + Greater, + GreaterEquals, + Less, + LessEquals, // Logical operators + // In, + // Where, + // Not, + // Or, + // And } #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -79,6 +82,7 @@ pub enum Precedence { Term, Factor, Unary, + Exponent, Call, Primary, } @@ -95,3 +99,55 @@ impl Display for NodeEnum { write!(f, "{}", Node::as_string(self, None)) } } + +impl PartialOrd for NodeEnum { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (NodeEnum::Constant(constant1), NodeEnum::Constant(constant2)) => { + constant1.partial_cmp(constant2) + } + + (NodeEnum::Add(add1), NodeEnum::Add(add2)) => add1.partial_cmp(add2), + (NodeEnum::Subtract(subtract1), NodeEnum::Subtract(subtract2)) => { + subtract1.partial_cmp(subtract2) + } + (NodeEnum::Multiply(multiply1), NodeEnum::Multiply(multiply2)) => { + multiply1.partial_cmp(multiply2) + } + (NodeEnum::Divide(divide1), NodeEnum::Divide(divide2)) => divide1.partial_cmp(divide2), + (NodeEnum::Exponent(exponent1), NodeEnum::Exponent(exponent2)) => { + exponent1.partial_cmp(exponent2) + } + + (NodeEnum::Symbol(symbol1), NodeEnum::Symbol(symbol2)) => symbol1.partial_cmp(symbol2), + (NodeEnum::Assign(assign1), NodeEnum::Assign(assign2)) => assign1.partial_cmp(assign2), + (NodeEnum::Empty(empty1), NodeEnum::Empty(empty2)) => empty1.partial_cmp(empty2), + (NodeEnum::Function(function1), NodeEnum::Function(function2)) => { + function1.partial_cmp(function2) + } + (NodeEnum::Call(call1), NodeEnum::Call(call2)) => call1.partial_cmp(call2), + + (NodeEnum::Bool(bool1), NodeEnum::Bool(bool2)) => bool1.partial_cmp(bool2), + (NodeEnum::IfElse(if_else1), NodeEnum::IfElse(if_else2)) => { + if_else1.partial_cmp(if_else2) + } + + (NodeEnum::Set(set1), NodeEnum::Set(set2)) => set1.partial_cmp(set2), + + (NodeEnum::Equals(equals1), NodeEnum::Equals(equals2)) => equals1.partial_cmp(equals2), + (NodeEnum::Greater(greater1), NodeEnum::Greater(greater2)) => { + greater1.partial_cmp(greater2) + } + ( + NodeEnum::GreaterEquals(greater_equals1), + NodeEnum::GreaterEquals(greater_equals2), + ) => greater_equals1.partial_cmp(greater_equals2), + (NodeEnum::Less(less1), NodeEnum::Less(less2)) => less1.partial_cmp(less2), + (NodeEnum::LessEquals(less_equals1), NodeEnum::LessEquals(less_equals2)) => { + less_equals1.partial_cmp(less_equals2) + } + + _ => None, + } + } +} diff --git a/src/lib/node/multiply.rs b/src/lib/node/multiply.rs index 564d4ff..8890b96 100644 --- a/src/lib/node/multiply.rs +++ b/src/lib/node/multiply.rs @@ -1,8 +1,13 @@ use std::rc::Rc; +use malachite::{base::num::basic::traits::{One, Zero}, rational::Rational}; + use crate::environment::Environment; -use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set}; +use super::{ + Node, NodeEnum, Precedence, add::Add, constant::Constant, divide::Divide, set::Set, + subtract::Subtract, +}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Multiply { @@ -18,26 +23,57 @@ impl Node for Multiply { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Zero rule (NodeEnum::Constant(zero), _) | (_, NodeEnum::Constant(zero)) - if zero.get_value() == 0. => + if zero.get_value() == &Rational::ZERO => { - Ok(Rc::new(Constant::new(0.).into())) + Ok(Rc::new(Constant::new(Rational::ZERO).into())) } + // Identity rule + (NodeEnum::Constant(one), _) if one.get_value() == &Rational::ONE => Ok(evaluated_right), + (_, NodeEnum::Constant(one)) if one.get_value() == &Rational::ONE => Ok(evaluated_left), + + // Multiply into parenthesis (add) + (_, NodeEnum::Add(add)) => Ok(Add::new_rc( + Self::new_rc(evaluated_left.clone(), add.get_left()).evaluate(env)?, + Self::new_rc(evaluated_left, add.get_right()).evaluate(env)?, + )), + (NodeEnum::Add(add), _) => Ok(Add::new_rc( + Self::new_rc(evaluated_right.clone(), add.get_left()).evaluate(env)?, + Self::new_rc(evaluated_right, add.get_right()).evaluate(env)?, + )), + + // Multiply into parenthesis (sub) + (_, NodeEnum::Subtract(sub)) => Ok(Subtract::new_rc( + Self::new_rc(evaluated_left.clone(), sub.get_left()).evaluate(env)?, + Self::new_rc(evaluated_left, sub.get_right()).evaluate(env)?, + )), + (NodeEnum::Subtract(sub), _) => Ok(Subtract::new_rc( + Self::new_rc(evaluated_right.clone(), sub.get_left()).evaluate(env)?, + Self::new_rc(evaluated_right, sub.get_right()).evaluate(env)?, + )), + + // Multiply fraction + (NodeEnum::Divide(div), _) => Ok(Divide::new_rc( + Self::new_rc(evaluated_right, div.get_left()).evaluate(env)?, + div.get_right(), + )), + + (_, NodeEnum::Divide(div)) => Ok(Divide::new_rc( + Self::new_rc(evaluated_left, div.get_left()).evaluate(env)?, + div.get_right(), + )), + // Constant + Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into())) } - // Symbol + Constant we just return the same - (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new( + // Move constant infront of symbols + (NodeEnum::Symbol(_), NodeEnum::Constant(_)) + | (NodeEnum::Exponent(_), NodeEnum::Constant(_)) => Ok(Rc::new( Multiply::new(evaluated_right, evaluated_left).into(), )), - // Constant + Symbol we switch them around so the constant is last - (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new( - Multiply::new(evaluated_left, evaluated_right).into(), - )), - // Multiply a set with a constant (NodeEnum::Set(s), NodeEnum::Constant(c)) | (NodeEnum::Constant(c), NodeEnum::Set(s)) => { diff --git a/src/lib/node/set.rs b/src/lib/node/set.rs index d37f4fb..b279e24 100644 --- a/src/lib/node/set.rs +++ b/src/lib/node/set.rs @@ -25,7 +25,7 @@ impl Node for Set { .iter() .map(|x| x.as_string(env)) .reduce(|a, b| a + ", " + &b) - .unwrap() + .unwrap_or("".to_owned()) ) } diff --git a/src/lib/node/subtract.rs b/src/lib/node/subtract.rs index 63c8dee..7007e38 100644 --- a/src/lib/node/subtract.rs +++ b/src/lib/node/subtract.rs @@ -1,5 +1,7 @@ use std::rc::Rc; +use malachite::{base::num::basic::traits::Zero, rational::Rational}; + use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; #[derive(Clone, Debug, PartialEq, PartialOrd)] @@ -15,24 +17,24 @@ impl Node for Subtract { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Zero rule - (NodeEnum::Constant(zero), _) if zero.get_value() == 0. => Ok(evaluated_right), - (_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => Ok(evaluated_left), + (NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), + (_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), // Constant + Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { Ok(Rc::new(Constant::new(a.get_value() - b.get_value()).into())) } // Symbol + Constant we just return the same - (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => { - Ok(Rc::new(Subtract::new(evaluated_left, evaluated_right).into())) - } + (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new( + Subtract::new(evaluated_left, evaluated_right).into(), + )), // Constant + Symbol we switch them around so the constant is last - (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::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new( + Subtract::new(evaluated_right, evaluated_left).into(), + )), + _ => Ok(Rc::new( + Subtract::new(evaluated_left, evaluated_right).into(), + )), } } @@ -57,10 +59,11 @@ impl Node for Subtract { impl Subtract { pub fn new(left: Rc, right: Rc) -> Self { - Self { - left, - right, - } + Self { left, right } + } + + pub fn new_rc(left: Rc, right: Rc) -> Rc { + Rc::new(Self { left, right }.into()) } pub fn get_left(&self) -> Rc { diff --git a/src/lib/node/symbol.rs b/src/lib/node/symbol.rs index 0b128f5..c39047e 100644 --- a/src/lib/node/symbol.rs +++ b/src/lib/node/symbol.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use super::{Environment, Node, NodeEnum, Precedence}; use crate::environment::EnvironmentInternalSymbolKey; -#[derive(Debug, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Clone, PartialEq)] pub struct Symbol { value: EnvironmentInternalSymbolKey, } @@ -26,10 +26,10 @@ impl Node for Symbol { } else { env.id_to_str(&self.value) .cloned() - .unwrap_or(format!("{{#SYMBOL {}}}", self.value)) + .unwrap_or(format!("{{#SYMBOL {:?}}}", self.value)) } } else { - format!("{{#SYMBOL {}}}", self.value) + format!("{{#SYMBOL {:?}}}", self.value) } } @@ -58,3 +58,9 @@ impl Symbol { self.value } } + +impl PartialOrd for Symbol { + fn partial_cmp(&self, other: &Self) -> Option { + None + } +} diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index 4133124..a5f6c36 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -3,7 +3,21 @@ use std::{iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter}; use crate::{ environment::Environment, node::{ - add::Add, assign::Assign, call::Call, constant::{Constant, ConstantValue}, divide::Divide, equals::Equals, function::{Function, FunctionType}, if_else::{Bool, ElseBranchEnum, IfElse}, multiply::Multiply, set::Set, subtract::Subtract, symbol::Symbol, NodeEnum + NodeEnum, + add::Add, + assign::Assign, + call::Call, + comparison::{Greater, GreaterEquals, Less, LessEquals}, + constant::{Constant, ConstantValue}, + divide::Divide, + equals::Equals, + exponent::Exponent, + function::{Function, FunctionType}, + if_else::{Bool, ElseBranchEnum, IfElse}, + multiply::Multiply, + set::Set, + subtract::Subtract, + symbol::Symbol, }, }; @@ -20,8 +34,13 @@ pub enum TokenType { Minus, Star, Slash, + Hat, Equals, + Greater, + Less, + GreaterEquals, + LessEquals, ColonEquals, LeftArrow, @@ -36,7 +55,7 @@ pub enum TokenType { End, True, - False + False, } impl TokenType { @@ -49,7 +68,13 @@ impl TokenType { TokenType::Minus => 1, TokenType::Star => 1, TokenType::Slash => 1, + TokenType::Hat => 1, + TokenType::Equals => 1, + TokenType::Greater => 1, + TokenType::Less => 1, + TokenType::GreaterEquals => 2, + TokenType::LessEquals => 2, TokenType::ColonEquals => 2, TokenType::LeftArrow => 2, @@ -108,26 +133,26 @@ impl<'a> Lexer<'a> { // Numbers with decimal points '0'..='9' | '.' => { let mut digit = String::from(c); + let mut has_decimal = c == '.'; loop { let d = self.source.peek(); - let mut has_decimal = c == '.'; match d { Some('0'..='9') => { digit.push(*d.unwrap()); self.source.next(); i += 1; } - #[allow(unused_assignments)] // For some reason it thinks has_decimal - // is never read Some('.') => { if has_decimal { return Err(LexerError::UnexpectedChar( - i, + i + 1, "Invalid digit with multiple decimal points".into(), )); } digit.push(*d.unwrap()); + self.source.next(); + i += 1; has_decimal = true; } _ => { @@ -147,12 +172,26 @@ impl<'a> Lexer<'a> { tokens.push(Token(i, TokenType::LeftArrow)); } + '<' if self.source.peek() == Some(&'=') => { + self.source.next(); + i += 1; + tokens.push(Token(i, TokenType::LessEquals)); + } + '>' if self.source.peek() == Some(&'=') => { + self.source.next(); + i += 1; + tokens.push(Token(i, TokenType::GreaterEquals)); + } + '<' => tokens.push(Token(i, TokenType::Less)), + '>' => tokens.push(Token(i, TokenType::Greater)), + '+' => tokens.push(Token(i, TokenType::Plus)), '-' => tokens.push(Token(i, TokenType::Minus)), '*' => tokens.push(Token(i, TokenType::Star)), '/' => tokens.push(Token(i, TokenType::Slash)), '=' => tokens.push(Token(i, TokenType::Equals)), ',' => tokens.push(Token(i, TokenType::Comma)), + '^' => tokens.push(Token(i, TokenType::Hat)), ':' if self.source.peek() == Some(&'=') => { self.source.next(); @@ -314,16 +353,56 @@ impl<'a> Parser<'a> { fn equality(&mut self) -> Result, ParserError> { // TODO: Implement equality - let expr = self.comparison(); + let expr = self.comparison()?; if self.matchType(TokenType::Equals) { - return Ok(Rc::new(Equals::new(expr?, self.equality()?).into())); + let mut expressions = vec![]; + loop { + expressions.push(self.comparison()?); + if !self.matchType(TokenType::Equals) { + break; + } + } + return Ok(Rc::new(Equals::new(expr, expressions).into())); } - expr + Ok(expr) } fn comparison(&mut self) -> Result, ParserError> { // TODO: Implement comparison - self.term() + let expr = self.term()?; + + let t = if let Some(Token(i, t)) = self.tokens.peek() { + t.clone() + } else { + return Ok(expr); + }; + + if match t { + TokenType::Greater => true, + TokenType::Less => true, + TokenType::GreaterEquals => true, + TokenType::LessEquals => true, + _ => false, + } { + self.consume(); + let mut expressions = vec![]; + loop { + expressions.push(self.term()?); + if !self.matchType(t.clone()) { + break; + } + } + + return match t { + TokenType::Greater => Ok(Greater::new(expr, expressions)), + TokenType::Less => Ok(Less::new(expr, expressions)), + TokenType::GreaterEquals => Ok(GreaterEquals::new(expr, expressions)), + TokenType::LessEquals => Ok(LessEquals::new(expr, expressions)), + _ => panic!(), + }; + } + + Ok(expr) } fn term(&mut self) -> Result, ParserError> { @@ -356,7 +435,14 @@ impl<'a> Parser<'a> { } fn exponent(&mut self) -> Result, ParserError> { - self.call() + let expr = self.call(); + + if self.matchType(TokenType::Hat) { + let right = self.unary()?; + return Ok(Exponent::new(expr?, right)); + } + + expr } fn call(&mut self) -> Result, ParserError> { @@ -403,10 +489,10 @@ impl<'a> Parser<'a> { let right = self.equality()?; match expr.clone().as_ref() { NodeEnum::Symbol(symbol) => { - return Ok(Function::new( - FunctionType::UserFunction(right), + return Ok(Function::new(FunctionType::UserFunction( + right, vec![symbol.clone()], - )); + ))); } NodeEnum::Set(set) => { let mut symbols = vec![]; @@ -425,7 +511,9 @@ impl<'a> Parser<'a> { } } - return Ok(Function::new(FunctionType::UserFunction(right), symbols)); + return Ok(Function::new( + FunctionType::UserFunction(right, symbols), + )); } _ => { return Err(ParserError::UnexpectedNode( @@ -505,6 +593,11 @@ impl<'a> Parser<'a> { TokenType::False => Ok(Rc::new(Bool::False.into())), TokenType::LParen => { + // Empty set + if self.matchType(TokenType::RParen) { + return Ok(Set::new(vec![])); + } + let expr = self.expression()?; let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() { (i, x) diff --git a/src/lib/tests/mod.rs b/src/lib/tests/mod.rs index 96e1570..4294909 100644 --- a/src/lib/tests/mod.rs +++ b/src/lib/tests/mod.rs @@ -1,133 +1,135 @@ -mod arithmetic { - use std::rc::Rc; +// TODO: Test case this: `x*0*5*y*z` - use crate::{environment, node::{ - add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum - }}; - use environment::Environment; - - use test_case::test_case; - - #[test_case(69.0, 420.0, 489.0 ; "when both are positive")] - #[test_case(-2.0, -4.0, -6.0 ; "when both are negative")] - #[test_case(0.0, 0.0, 0.0 ; "when both are zero")] - #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")] - // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN - fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) { - let mut env = Environment::new(); - - let a = Rc::new(Constant::new(a).into()); - let b = Rc::new(Constant::new(b).into()); - let d: NodeEnum = Add::new(a, b).into(); - - let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); - - assert!( - value == Constant::new(e).into(), - "Expected {} got {}", - e, - value - ) - } - - #[test_case(69.0, 420.0, -351.0 ; "when both are positive")] - #[test_case(-2.0, -4.0, 2.0 ; "when both are negative")] - #[test_case(0.0, 0.0, 0.0 ; "when both are zero")] - #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")] - // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN - fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) { - let mut env = Environment::new(); - - let a = Rc::new(Constant::new(0.0).into()); - let b = Rc::new(Constant::new(aa).into()); - let c = Rc::new(Constant::new(bb).into()); - let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into(); - - let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); - - assert!( - value == Constant::new(e).into(), - "Expected {} got {}", - e, - value - ) - } - - #[test_case(5.0, 10.0, 50.0 ; "when both are positive")] - #[test_case(-5.0, 10.0, -50.0 ; "when left is negative")] - #[test_case(5.0, -10.0, -50.0 ; "when right is negative")] - #[test_case(-5.0, -10.0, 50.0 ; "when both are negative")] - #[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")] - fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) { - let mut env = Environment::new(); - - let a = Rc::new(Constant::new(a).into()); - let b = Rc::new(Constant::new(b).into()); - let d: NodeEnum = Multiply::new(a, b).into(); - - let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); - - assert!( - value == Constant::new(e).into(), - "Expected {} got {}", - e, - value - ) - } - - #[test_case(5.0, 10.0, 0.5 ; "when both are positive")] - #[test_case(-5.0, 10.0, -0.5 ; "when left is negative")] - #[test_case(5.0, -10.0, -0.5 ; "when right is negative")] - #[test_case(-5.0, -10.0, 0.5 ; "when both are negative")] - fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) { - let mut env = Environment::new(); - - let a = Rc::new(Constant::new(a).into()); - let b = Rc::new(Constant::new(b).into()); - let d: NodeEnum = Divide::new(a, b).into(); - - let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); - - assert!( - value == Constant::new(e).into(), - "Expected {} got {}", - e, - value - ) - } -} - -mod functions { - #[test] - fn stringification() { - // let f = Function - } -} - -mod expected_errors { - use test_case::test_case; - - use crate::node::constant::ConstantValue; - - #[test_case(5.0, 0.0 ; "divide by zero")] - fn division(a: ConstantValue, b: ConstantValue) { - let _ = a+b; - } -} - -mod misc { - use test_case::test_case; - - use crate::node::constant::ConstantValue; - - #[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")] - fn convert_to_string( - a: impl Into, - op1: char, - b: impl Into, - op2: char, - c: impl Into, - e: &'static str, - ) { - } -} +// mod arithmetic { +// use std::rc::Rc; +// +// use crate::{environment, node::{ +// add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum +// }}; +// use environment::Environment; +// +// use test_case::test_case; +// +// #[test_case(69.0, 420.0, 489.0 ; "when both are positive")] +// #[test_case(-2.0, -4.0, -6.0 ; "when both are negative")] +// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")] +// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")] +// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN +// fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) { +// let mut env = Environment::new(); +// +// let a = Rc::new(Constant::new(a).into()); +// let b = Rc::new(Constant::new(b).into()); +// let d: NodeEnum = Add::new(a, b).into(); +// +// let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); +// +// assert!( +// value == Constant::new(e).into(), +// "Expected {} got {}", +// e, +// value +// ) +// } +// +// #[test_case(69.0, 420.0, -351.0 ; "when both are positive")] +// #[test_case(-2.0, -4.0, 2.0 ; "when both are negative")] +// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")] +// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")] +// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN +// fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) { +// let mut env = Environment::new(); +// +// let a = Rc::new(Constant::new(0.0).into()); +// let b = Rc::new(Constant::new(aa).into()); +// let c = Rc::new(Constant::new(bb).into()); +// let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into(); +// +// let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); +// +// assert!( +// value == Constant::new(e).into(), +// "Expected {} got {}", +// e, +// value +// ) +// } +// +// #[test_case(5.0, 10.0, 50.0 ; "when both are positive")] +// #[test_case(-5.0, 10.0, -50.0 ; "when left is negative")] +// #[test_case(5.0, -10.0, -50.0 ; "when right is negative")] +// #[test_case(-5.0, -10.0, 50.0 ; "when both are negative")] +// #[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")] +// fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) { +// let mut env = Environment::new(); +// +// let a = Rc::new(Constant::new(a).into()); +// let b = Rc::new(Constant::new(b).into()); +// let d: NodeEnum = Multiply::new(a, b).into(); +// +// let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); +// +// assert!( +// value == Constant::new(e).into(), +// "Expected {} got {}", +// e, +// value +// ) +// } +// +// #[test_case(5.0, 10.0, 0.5 ; "when both are positive")] +// #[test_case(-5.0, 10.0, -0.5 ; "when left is negative")] +// #[test_case(5.0, -10.0, -0.5 ; "when right is negative")] +// #[test_case(-5.0, -10.0, 0.5 ; "when both are negative")] +// fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) { +// let mut env = Environment::new(); +// +// let a = Rc::new(Constant::new(a).into()); +// let b = Rc::new(Constant::new(b).into()); +// let d: NodeEnum = Divide::new(a, b).into(); +// +// let value = Rc::::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap(); +// +// assert!( +// value == Constant::new(e).into(), +// "Expected {} got {}", +// e, +// value +// ) +// } +// } +// +// mod functions { +// #[test] +// fn stringification() { +// // let f = Function +// } +// } +// +// mod expected_errors { +// use test_case::test_case; +// +// use crate::node::constant::ConstantValue; +// +// #[test_case(5.0, 0.0 ; "divide by zero")] +// fn division(a: ConstantValue, b: ConstantValue) { +// let _ = a+b; +// } +// } +// +// mod misc { +// use test_case::test_case; +// +// use crate::node::constant::ConstantValue; +// +// #[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")] +// fn convert_to_string( +// a: impl Into, +// op1: char, +// b: impl Into, +// op2: char, +// c: impl Into, +// e: &'static str, +// ) { +// } +// }