From 2c38fb391b598a1089febfce9ba18ded5e3e70d9 Mon Sep 17 00:00:00 2001 From: Snorre Ettrup Altschul Date: Mon, 24 Feb 2025 02:37:50 +0100 Subject: [PATCH] cleaned up some code --- Cargo.lock | 154 ++++++-------------- Cargo.toml | 2 +- flake.nix | 2 + src/cli-repl.rs | 210 +++++++++++++++++++++++++-- src/lib/environment.rs | 28 ++-- src/lib/node/add.rs | 29 ++-- src/lib/node/call.rs | 77 ++++++---- src/lib/node/closure.rs | 67 +++++++++ src/lib/node/constant.rs | 43 +++--- src/lib/node/divide.rs | 24 ++-- src/lib/node/exponent.rs | 22 ++- src/lib/node/function.rs | 32 ++--- src/lib/node/mod.rs | 32 ++++- src/lib/node/multiply.rs | 29 ++-- src/lib/node/set.rs | 2 +- src/lib/node/subtract.rs | 17 ++- src/lib/parser/mod.rs | 304 +++++++++++++++++++++++++-------------- 17 files changed, 723 insertions(+), 351 deletions(-) create mode 100644 src/lib/node/closure.rs diff --git a/Cargo.lock b/Cargo.lock index fe55b28..b49547a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,18 +8,6 @@ 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" @@ -41,6 +29,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "bindgen" version = "0.69.5" @@ -50,7 +44,7 @@ dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools", "lazy_static", "lazycell", "log", @@ -375,25 +369,26 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gmp-mpfr-sys" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0205cd82059bc63b63cf516d714352a30c44f2c74da9961dfda2617ae6b5918" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "hashbrown" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ - "ahash 0.3.8", + "ahash", "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" @@ -409,15 +404,6 @@ 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" @@ -494,51 +480,6 @@ 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" @@ -592,8 +533,8 @@ dependencies = [ "clay-layout", "enum_dispatch", "font-kit", - "malachite", "raylib", + "rug", "termion", "test-case", ] @@ -762,6 +703,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rug" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3" +dependencies = [ + "az", + "gmp-mpfr-sys", + "libc", + "libm", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -790,12 +743,6 @@ 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" @@ -830,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" dependencies = [ "arrayvec", - "hashbrown 0.7.2", + "hashbrown", "mopa", "smallvec", "tynm", @@ -855,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" dependencies = [ "crossbeam-queue", - "hashbrown 0.7.2", + "hashbrown", "hibitset", "log", "shred", @@ -982,12 +929,6 @@ 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" @@ -1056,6 +997,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1205,23 +1155,3 @@ 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 8882d59..29a1e52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,8 @@ 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"] } +rug = "1.27.0" termion = "4.0.3" test-case = "3.3.1" diff --git a/flake.nix b/flake.nix index 8795512..96a773d 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,8 @@ libclang libllvm nerd-fonts.mononoki + + m4 ]; LD_LIBRARY_PATH = diff --git a/src/cli-repl.rs b/src/cli-repl.rs index f00d04e..4841567 100644 --- a/src/cli-repl.rs +++ b/src/cli-repl.rs @@ -1,14 +1,16 @@ +use std::f32::consts; use std::f64; use std::io::{self, StdoutLock, Write, stdout}; use std::rc::Rc; use libopenbirch::environment::Environment; +use libopenbirch::node::call::Call; +use libopenbirch::node::closure::Closure; use libopenbirch::node::constant::Constant; use libopenbirch::node::empty::Empty; +use libopenbirch::node::set::Set; use libopenbirch::node::{Node, NodeEnum}; use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError}; -use malachite::base::num::basic::traits::Zero; -use malachite::rational::Rational; #[cfg(feature = "async")] use termion::AsyncReader; use termion::color; @@ -23,6 +25,9 @@ pub struct Input { stdout: RawTerminal>, buffer: String, current_char: usize, + + history: Vec, + history_idx: Option, } impl Input { pub fn new() -> Self { @@ -35,6 +40,9 @@ impl Input { stdout, buffer: "".into(), current_char: 0, + + history: vec![], + history_idx: None, } } @@ -68,9 +76,11 @@ impl Input { return Err(io::Error::from(io::ErrorKind::Interrupted)); } + self.history.insert(0, self.buffer.clone()); let r = self.buffer.clone(); self.buffer.clear(); self.current_char = 0; + self.history_idx = None; print!("\n\r"); return Ok(Some(r)); } @@ -107,6 +117,45 @@ impl Input { print!("{}", termion::cursor::Right(1)); let _ = self.stdout.flush(); } + Key::Up => { + match self.history_idx { + Some(i) => self.history_idx = Some((i + 1) % self.history.len()), + None => { + if self.history.len() > 0 { + self.history_idx = Some(0) + } else { + break; + } + } + } + + self.buffer = + self.history.get(self.history_idx.unwrap()).unwrap().clone(); + self.current_char = self.buffer.len(); + Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); + } + Key::Down => { + match self.history_idx { + Some(i) => { + let i = if i == 0 { self.history.len() } else { i }; + self.history_idx = Some((i - 1) % self.history.len()) + } + None => { + if self.history.len() > 0 { + self.history_idx = Some(self.history.len() - 1); + } else { + break; + } + } + } + + self.buffer = + self.history.get(self.history_idx.unwrap()).unwrap().clone(); + self.current_char = self.buffer.len(); + Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); + } + Key::Home => self.current_char = 0, + Key::End => self.current_char = self.buffer.len(), Key::Char(char) => { self.buffer.insert(self.current_char, char); self.current_char += 1; @@ -169,23 +218,168 @@ fn unset(args: &Vec>, env: &mut Environment) -> Result env.undefine(*id); } } else { - return Err(format!("Cannot undefine the expression {arg}")); + return Err(format!( + "Expected strings, but one of the arguments was a {}", + arg.type_str() + )); } } Ok(Empty::new(format!("Undefined {} symbols", args.len()))) } +fn get_float_precision( + args: &Vec>, + env: &mut Environment, +) -> Result, String> { + match args.len() { + 0 => Ok(Rc::new( + Constant::new_from_float(env.get_float_precision() as f64, &env).into(), + )), + 1 => { + let arg = args.first().unwrap(); + if let NodeEnum::Constant(constant) = arg.as_ref() { + let prec = constant.get_value().prec_64() as f64; + + Ok(Rc::new(Constant::new_from_float(prec, &env).into())) + } else { + Err(format!( + "Expected argument of type Constant, but got {}", + arg.type_str() + )) + } + } + _ => Err(format!("Expected 0 or 1 arguments, got {}", args.len()))?, + } +} + +fn set_float_precision( + args: &Vec>, + env: &mut Environment, +) -> Result, String> { + if args.len() != 1 { + Err(format!("Expected 1 arguments, got {}", args.len()))? + } + + let arg = args.first().unwrap(); + + let precision: u64 = if let NodeEnum::Constant(value) = arg.as_ref() { + value.get_value().to_u32_saturating().unwrap().into() + } else { + Err(format!( + "Expected argument of type Constant, but got {}", + arg.type_str() + ))? + }; + + env.set_float_precision(precision); + + Ok(Empty::new(format!("Set float precision to {precision}"))) +} + +fn map(args: &Vec>, env: &mut Environment) -> Result, String> { + if args.len() != 2 { + Err(format!("Expected 2 argument but got {}", args.len()))? + } + + let arg = args.first().unwrap(); + let func = match arg.as_ref() { + NodeEnum::Function(_) | NodeEnum::Closure(_) => arg, + _ => Err(format!( + "Argument 1 expected a Function but got {}", + arg.type_str() + ))?, + }; + + let arg = args.get(1).unwrap().as_ref(); + let set = if let NodeEnum::Set(set) = arg { + set.get_values() + } else { + return Err(format!( + "Argument 1 expected a Set but got {}", + arg.type_str() + ))?; + }; + + let mut out = Vec::with_capacity(set.len()); + + for expr in set.iter() { + out.push(Call::new(func.clone(), vec![expr.clone()]).evaluate(env)?); + } + + Ok(Set::new(out)) +} + +fn get(args: &Vec>, env: &mut Environment) -> Result, String> { + if args.len() != 2 { + Err(format!("Expected 2 argument but got {}", args.len()))? + } + + let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() { + set.get_values() + } else { + return Err(format!( + "Argument 1 expected Set but got {}", + args.first().unwrap().type_str() + )); + }; + + let idx = if let NodeEnum::Constant(c) = args.get(1).unwrap().as_ref() { + if let Some(u) = c.get_value().to_u32_saturating() { + u as usize + } else { + return Err( + "Error occured while trying to convert second argument to integer".to_owned(), + ); + } + } else { + return Err("Argument 1 is expected to be a Set".to_owned()); + }; + + if let Some(v) = set.get(idx) { + return Ok(v.clone()); + } + + return Err(format!( + "Index was out of bounds for Set of length {}", + set.len() + )); +} + +fn length(args: &Vec>, env: &mut Environment) -> Result, String> { + if args.len() != 1 { + Err(format!("Expected 1 argument but got {}", args.len()))? + } + + let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() { + set.get_values() + } else { + return Err("Argument 1 is expected to be a Set".to_owned()); + }; + + Ok(Rc::new( + Constant::new_from_float(set.len() as f32, &env).into(), + )) +} + fn main() -> Result<(), io::Error> { let mut input = Input::new(); let mut env = Environment::new(); + env.set_float_precision(128); + env.define_native_function("print", print); env.define_native_function("unset", unset); + env.define_native_function("get_float_precision", get_float_precision); + env.define_native_function("set_float_precision", set_float_precision); + env.define_native_function("map", map); + env.define_native_function("get", get); + env.define_native_function("length", length); + env.define( "pi", - Rc::new(Constant::new(f64::consts::PI.try_into().unwrap()).into()), + Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()), ); while let Some(source) = input.get()? { @@ -196,9 +390,7 @@ fn main() -> Result<(), io::Error> { if tokens_result.is_err() { match tokens_result.err().unwrap() { - LexerError::UnexpectedChar(i, exp) | LexerError::NumberParse(i, exp) => { - print_err(i, 1, exp) - } + LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp), } continue; } @@ -213,7 +405,9 @@ fn main() -> Result<(), io::Error> { ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp), ParserError::UnexpectedToken(i, len, exp) | ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp), - ParserError::UnexpectedNode(i, exp) => print_err(i, 1, exp), + ParserError::UnexpectedNode(i, exp) | ParserError::NumberParse(i, exp) => { + print_err(i, 1, exp) + } } continue; } diff --git a/src/lib/environment.rs b/src/lib/environment.rs index ab67c24..4b42a30 100644 --- a/src/lib/environment.rs +++ b/src/lib/environment.rs @@ -23,7 +23,9 @@ pub struct Environment { stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc)>, hidden: Vec, - disable_calls: bool, + enable_closures: bool, + + float_precision: u64, // stack: Vec>, symbol_to_id: HashMap, @@ -42,7 +44,9 @@ impl Environment { stack: vec![], stack_shadows: vec![], hidden: vec![], - disable_calls: false, + enable_closures: false, + + float_precision: 64, symbol_to_id: HashMap::new(), id_to_symbol: HashMap::new(), @@ -66,16 +70,24 @@ impl Environment { } } - pub fn disable_calls(&mut self) { - self.disable_calls = true; + pub fn get_float_precision(&self) -> u64 { + self.float_precision } - pub fn enable_calls(&mut self) { - self.disable_calls = false; + pub fn set_float_precision(&mut self, precision: u64) { + self.float_precision = precision; } - pub fn calls_disabled(&self) -> bool { - self.disable_calls + pub fn disable_closures(&mut self) { + self.enable_closures = true; + } + + pub fn enable_closures(&mut self) { + self.enable_closures = false; + } + + pub fn closures_enabled(&self) -> bool { + self.enable_closures } pub fn push_stack(&mut self) -> Result<(), String> { diff --git a/src/lib/node/add.rs b/src/lib/node/add.rs index d6aa134..9349010 100644 --- a/src/lib/node/add.rs +++ b/src/lib/node/add.rs @@ -1,8 +1,8 @@ use std::rc::Rc; -use malachite::{base::num::basic::traits::Zero, rational::Rational}; +use rug::Float; -use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; +use super::{Environment, Node, NodeEnum, Precedence, constant::Constant, set::Set}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Add { @@ -17,20 +17,33 @@ impl Node for Add { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Zero rule - (NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), - (_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), - + (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right), + (_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => Ok(evaluated_left), // Constant + Constant = Constant - (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { - Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into())) - } + (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new( + Constant::new(Float::with_val_64( + env.get_float_precision(), + a.get_value() + b.get_value(), + )) + .into(), + )), // Move constants after symbols in add (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => { Ok(Rc::new(Add::new(evaluated_right, evaluated_left).into())) } + // Concatenate sets + (NodeEnum::Set(s1), NodeEnum::Set(s2)) => { + let values = { + let mut v = s1.get_values().clone(); + v.append(&mut s2.get_values().clone()); + v + }; + Ok(Set::new(values)) + } + _ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())), } } diff --git a/src/lib/node/call.rs b/src/lib/node/call.rs index b70ef3e..668d9bf 100644 --- a/src/lib/node/call.rs +++ b/src/lib/node/call.rs @@ -1,47 +1,57 @@ use std::rc::Rc; -use crate::{environment::Environment, node::function::FunctionType}; +use crate::{ + environment::Environment, + node::{closure::Closure, function::FunctionType}, +}; -use super::{Node, NodeEnum, Precedence}; +use super::{Node, NodeEnum, Precedence, closure}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Call { function: Rc, - arguments: Rc, + arguments: Vec>, } impl Node for Call { fn evaluate(&self, env: &mut Environment) -> Result, String> { - if env.calls_disabled() { + if env.closures_enabled() { return Ok(Rc::new(self.clone().into())); } // Evaluate callee and error if its not a function let evaluated = self.function.evaluate(env)?; - let func = if let NodeEnum::Function(func) = evaluated.as_ref() { - func - } else { - // return Err(format!( - // "Cannot call {} as a function", - // evaluated.as_string(Some(env)) - // )); - return Ok(Rc::new(self.clone().into())); + // let func = if let NodeEnum::Function(func) = evaluated.as_ref() { + // func + // } else { + // return Ok(Rc::new(self.clone().into())); + // }; + + env.push_stack()?; + + let func = match evaluated.as_ref() { + NodeEnum::Function(func) => func, + NodeEnum::Closure(closure) => { + for (k, v) in closure.get_captured_variables() { + env.insert(*k, v.clone()); + } + closure.get_function() + } + _ => { + return Ok(Rc::new(self.clone().into())); + } }; - let arguments = if let NodeEnum::Set(set) = self.arguments.as_ref() { + let arguments = { let mut arguments = vec![]; - for value in set.get_values().iter() { + for value in self.arguments.iter() { arguments.push(value.evaluate(env)?); } arguments - } else { - panic!( - "This shoulnd not be possible, but call constructed with arguments of type that is not set" - ); }; // Call function body with arguments - match func.get_body() { + let ret = match func.get_body() { // Pass arguments to native function FunctionType::Native(_name, native_function) => native_function(&arguments, env), FunctionType::UserFunction(body, fargs) => { @@ -52,21 +62,36 @@ impl Node for Call { arguments.len() )); } - env.push_stack()?; // Define variables - fargs.iter().zip(arguments).for_each(|(symbol, value)| { + fargs.iter().zip(&arguments).for_each(|(symbol, value)| { env.insert(symbol.get_value(), value.clone()); }); let ev = body.evaluate(env)?; - env.pop_stack()?; + // Return evaluated return value for function - Ok(ev) + Ok(match ev.as_ref() { + NodeEnum::Function(hof) => { + let captures = fargs.iter().map(|a| a.get_value()).zip(arguments).collect(); + Closure::new(hof.clone(), captures) + } + _ => ev, + }) } - } + }; + + env.pop_stack()?; + ret } fn as_string(&self, env: Option<&Environment>) -> String { - let arguments = self.arguments.as_string(env); + let arguments = format!( + "{}", + self.arguments + .iter() + .map(|x| x.as_string(env)) + .reduce(|a, b| a + ", " + &b) + .unwrap_or("".to_owned()) + ); format!("{}({})", self.function.as_string(env), arguments) } @@ -76,7 +101,7 @@ impl Node for Call { } impl Call { - pub fn new(function: Rc, arguments: Rc) -> Rc { + pub fn new(function: Rc, arguments: Vec>) -> Rc { Rc::new( Self { function, diff --git a/src/lib/node/closure.rs b/src/lib/node/closure.rs new file mode 100644 index 0000000..cb8b389 --- /dev/null +++ b/src/lib/node/closure.rs @@ -0,0 +1,67 @@ +use std::rc::Rc; + +use crate::environment::{Environment, EnvironmentInternalSymbolKey}; + +use super::{Node, NodeEnum, Precedence, function::Function}; + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub struct Closure { + function: Function, + captured_variables: Vec<(EnvironmentInternalSymbolKey, Rc)>, +} + +impl Node for Closure { + fn evaluate(&self, env: &mut Environment) -> Result, String> { + Ok(Rc::new(self.clone().into())) + } + + fn as_string(&self, env: Option<&Environment>) -> String { + if let Some(env) = env { + let captures = self + .captured_variables + .iter() + .map(|(k, v)| { + format!( + "{}: {}", + env.id_to_str(k).expect("Failed to get variable name"), + v.as_string(Some(env)) + ) + }) + .reduce(|a, b| format!("{}, {}", a, b)) + .unwrap(); + format!( + "( ({captures}) => {})", + self.function.as_string(Some(env)) + ) + } else { + format!("", self.function.as_string(env)) + } + } + + fn precedence(&self) -> Precedence { + Precedence::Call + } +} + +impl Closure { + pub fn new( + function: Function, + captured_variables: Vec<(EnvironmentInternalSymbolKey, Rc)>, + ) -> Rc { + Rc::new( + Self { + function, + captured_variables, + } + .into(), + ) + } + + pub fn get_captured_variables(&self) -> &Vec<(EnvironmentInternalSymbolKey, Rc)> { + &self.captured_variables + } + + pub fn get_function(&self) -> &Function { + &self.function + } +} diff --git a/src/lib/node/constant.rs b/src/lib/node/constant.rs index 0bbc633..b007825 100644 --- a/src/lib/node/constant.rs +++ b/src/lib/node/constant.rs @@ -1,13 +1,10 @@ use std::rc::Rc; -use malachite::{ - base::{num::basic::traits::Zero, rounding_modes::RoundingMode}, - rational::{Rational, conversion::primitive_float_from_rational::FloatConversionError}, -}; +use rug::Float; use super::{Environment, Node, Precedence}; -pub type ConstantValue = malachite::rational::Rational; +pub type ConstantValue = Float; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Constant { @@ -26,29 +23,15 @@ impl Node for Constant { } fn as_string(&self, _env: Option<&Environment>) -> String { - if self.value == Rational::ZERO { - return "0".to_owned(); - } - - if self.value.approx_log() < 50. { - let x: Result = self.value.clone().try_into(); - - match x { - Ok(v) => { - return v.to_string(); - } - Err(_) => {} - } - } - - if let Some((man, exp, _)) = self - .value - .sci_mantissa_and_exponent_round_ref::(RoundingMode::Nearest) - { - format!("{}*2^{}", man, exp) - } else { - self.value.to_string() + if self.value.is_zero() { + "0".to_string(); } + + self.value + .to_string_radix(10, Some(8)) + .trim_end_matches('0') + .trim_end_matches('.') + .to_string() } fn precedence(&self) -> Precedence { @@ -61,6 +44,12 @@ impl Constant { Self { value } } + pub fn new_from_float(value: impl Into, env: &Environment) -> Self { + Self { + value: Float::with_val_64(env.get_float_precision(), value.into()), + } + } + pub fn get_value(&self) -> &ConstantValue { &self.value } diff --git a/src/lib/node/divide.rs b/src/lib/node/divide.rs index 1b5cdc5..5fb23a3 100644 --- a/src/lib/node/divide.rs +++ b/src/lib/node/divide.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use malachite::{base::num::basic::traits::Zero, rational::Rational}; +use rug::Float; use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set}; @@ -17,20 +17,28 @@ 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() == &Rational::ZERO => { + (_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => { Err("Division by Zero".into()) } - (_, _) if evaluated_left == evaluated_right => Ok(Rc::new(Constant::new(Rational::ZERO).into())), + (_, _) if evaluated_left == evaluated_right => { + Ok(Rc::new(Constant::new_from_float(0.0, &env).into())) + } // Zero rule - (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())) + (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => { + Ok(evaluated_left) } + // Constant / Constant = Constant + (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new( + Constant::new(Float::with_val_64( + env.get_float_precision(), + a.get_value() / b.get_value(), + )) + .into(), + )), + // Divide a set with a constant (NodeEnum::Set(s), NodeEnum::Constant(c)) => { let old_values = s.get_values(); diff --git a/src/lib/node/exponent.rs b/src/lib/node/exponent.rs index 108de7e..254bf03 100644 --- a/src/lib/node/exponent.rs +++ b/src/lib/node/exponent.rs @@ -1,10 +1,6 @@ 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 rug::{Float, ops::Pow}; use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; @@ -20,15 +16,13 @@ impl Node for Exponent { 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())) - } + (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new( + Constant::new(Float::with_val_64( + env.get_float_precision(), + a.get_value().pow(b.get_value()), + )) + .into(), + )), _ => Ok(Self::new(evaluated_left, evaluated_right)), } diff --git a/src/lib/node/function.rs b/src/lib/node/function.rs index 08e16c3..e763371 100644 --- a/src/lib/node/function.rs +++ b/src/lib/node/function.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::environment::Environment; -use super::{Node, NodeEnum, Precedence, symbol::Symbol}; +use super::{closure::Closure, symbol::Symbol, Node, NodeEnum, Precedence}; pub type NativeFunctionType = fn(&Vec>, env: &mut Environment) -> Result, String>; @@ -20,22 +20,22 @@ pub struct Function { impl Node for Function { fn evaluate(&self, env: &mut Environment) -> Result, String> { - // Ok(Rc::new(self.clone().into())) + Ok(Rc::new(self.clone().into())) - Ok(Rc::new( - Self { - function: match &self.function { - FunctionType::Native(_, _) => self.function.clone(), - FunctionType::UserFunction(node_enum, symbols) => { - env.disable_calls(); - let evaluated = node_enum.evaluate(env)?; - env.enable_calls(); - FunctionType::UserFunction(evaluated, symbols.clone()) - } - }, - } - .into(), - )) + // Ok(Rc::new( + // Self { + // function: match &self.function { + // FunctionType::Native(_, _) => self.function.clone(), + // FunctionType::UserFunction(node_enum, symbols) => { + // env.disable_calls(); + // let evaluated = node_enum.evaluate(env)?; + // env.enable_calls(); + // FunctionType::UserFunction(evaluated, symbols.clone()) + // } + // }, + // } + // .into(), + // )) } fn as_string(&self, env: Option<&Environment>) -> String { diff --git a/src/lib/node/mod.rs b/src/lib/node/mod.rs index b1a9baf..cf41b6f 100644 --- a/src/lib/node/mod.rs +++ b/src/lib/node/mod.rs @@ -17,6 +17,7 @@ use set::Set; use subtract::Subtract; use symbol::Symbol; use string_node::StringNode; +use closure::Closure; use crate::environment::Environment; @@ -37,6 +38,7 @@ pub mod set; pub mod subtract; pub mod symbol; pub mod string_node; +pub mod closure; #[enum_dispatch] #[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)] @@ -56,7 +58,7 @@ pub enum NodeEnum { Assign, Empty, Function, - // Closure // IMPLEMENT THIS SO CURRYING WORKS + Closure, // IMPLEMENT THIS SO CURRYING WORKS Call, Bool, @@ -155,3 +157,31 @@ impl PartialOrd for NodeEnum { } } } + +impl NodeEnum { + pub fn type_str(&self) -> String { + match self { + NodeEnum::Constant(_) => "Constant", + NodeEnum::StringNode(_) => "String", + NodeEnum::Add(_) => "Add", + NodeEnum::Subtract(_) => "Subtract", + NodeEnum::Multiply(_) => "Multiply", + NodeEnum::Divide(_) => "Divide", + NodeEnum::Exponent(_) => "Exponent", + NodeEnum::Symbol(_) => "Symbol", + NodeEnum::Assign(_) => "Assign", + NodeEnum::Empty(_) => "Empty", + NodeEnum::Function(_) => "Function", + NodeEnum::Closure(_) => "Closure", + NodeEnum::Call(_) => "Call", + NodeEnum::Bool(_) => "Bool", + NodeEnum::IfElse(_) => "If", + NodeEnum::Set(_) => "Set", + NodeEnum::Equals(_) => "Equals", + NodeEnum::Greater(_) => "Greater", + NodeEnum::GreaterEquals(_) => "Greater Equals", + NodeEnum::Less(_) => "Less", + NodeEnum::LessEquals(_) => "Less Equals" + }.to_owned() + } +} diff --git a/src/lib/node/multiply.rs b/src/lib/node/multiply.rs index 8890b96..d850d53 100644 --- a/src/lib/node/multiply.rs +++ b/src/lib/node/multiply.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use malachite::{base::num::basic::traits::{One, Zero}, rational::Rational}; +use rug::Float; use crate::environment::Environment; @@ -23,14 +23,14 @@ 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() == &Rational::ZERO => + if zero.get_value() == &0.0 => { - Ok(Rc::new(Constant::new(Rational::ZERO).into())) + Ok(Rc::new(Constant::new_from_float(0.0, &env).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), + (NodeEnum::Constant(one), _) if one.get_value() == &0.0 => Ok(evaluated_right), + (_, NodeEnum::Constant(one)) if one.get_value() == &0.0 => Ok(evaluated_left), // Multiply into parenthesis (add) (_, NodeEnum::Add(add)) => Ok(Add::new_rc( @@ -63,9 +63,12 @@ impl Node for Multiply { div.get_right(), )), - // Constant + Constant = Constant + // Constant * Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { - Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into())) + Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64( + env.get_float_precision(), + a.get_value() * b.get_value(), + ))))) } // Move constant infront of symbols @@ -146,14 +149,22 @@ impl Multiply { ) -> Result, String> { match (multiply.left.as_ref(), multiply.right.as_ref()) { (NodeEnum::Constant(c2), _) => { - let new_const = Constant::new(constant.get_value() * c2.get_value()).into(); + let new_const = Constant::new(Float::with_val_64( + env.get_float_precision(), + constant.get_value() * c2.get_value(), + )) + .into(); Ok(Rc::new( Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), )) } (_, NodeEnum::Constant(c2)) => { - let new_const = Constant::new(constant.get_value() * c2.get_value()).into(); + let new_const = Constant::new(Float::with_val_64( + env.get_float_precision(), + constant.get_value() * c2.get_value(), + )) + .into(); Ok(Rc::new( Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), )) diff --git a/src/lib/node/set.rs b/src/lib/node/set.rs index b279e24..6ba1fee 100644 --- a/src/lib/node/set.rs +++ b/src/lib/node/set.rs @@ -20,7 +20,7 @@ impl Node for Set { fn as_string(&self, env: Option<&Environment>) -> String { format!( - "({})", + "[{}]", self.values .iter() .map(|x| x.as_string(env)) diff --git a/src/lib/node/subtract.rs b/src/lib/node/subtract.rs index 7007e38..3eee08b 100644 --- a/src/lib/node/subtract.rs +++ b/src/lib/node/subtract.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use malachite::{base::num::basic::traits::Zero, rational::Rational}; +use rug::Float; use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; @@ -17,18 +17,21 @@ impl Node for Subtract { match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Zero rule - (NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), - (_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), + (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right), + (_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => Ok(evaluated_left), - // Constant + Constant = Constant + // Constant - Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => { - Ok(Rc::new(Constant::new(a.get_value() - b.get_value()).into())) + Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64( + env.get_float_precision(), + a.get_value() - b.get_value(), + ))))) } - // Symbol + Constant we just return the same + // Symbol - Constant we just return the same (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 + // 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(), )), diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index 74f048b..aeac793 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -1,9 +1,26 @@ -use std::{iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter}; +use std::{env, iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter}; + +use rug::Float; use crate::{ environment::Environment, node::{ - 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, string_node::StringNode, 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, + string_node::StringNode, + subtract::Subtract, + symbol::Symbol, }, }; @@ -13,7 +30,7 @@ pub struct Token(usize, TokenType); #[derive(Debug, PartialEq, Clone)] pub enum TokenType { // Space, - Number(ConstantValue), + Number(String), Identifier(String), String(String), @@ -34,6 +51,9 @@ pub enum TokenType { RParen, LParen, + + RSquare, + LSquare, Comma, If, @@ -68,6 +88,9 @@ impl TokenType { TokenType::RParen => 1, TokenType::LParen => 1, + + TokenType::RSquare => 1, + TokenType::LSquare => 1, TokenType::Comma => 1, TokenType::If => 2, @@ -88,7 +111,6 @@ pub struct Lexer<'a> { #[derive(Debug)] pub enum LexerError { UnexpectedChar(usize, String), - NumberParse(usize, String), } impl<'a> Lexer<'a> { @@ -125,7 +147,7 @@ impl<'a> Lexer<'a> { loop { let d = self.source.peek(); match d { - Some('0'..='9') | Some('e') | Some('E') => { + Some('0'..='9') => { digit.push(*d.unwrap()); self.source.next(); i += 1; @@ -148,41 +170,41 @@ impl<'a> Lexer<'a> { } } } - let number = - if let Some(v) = ConstantValue::from_sci_string_simplest(digit.as_str()) { - v - } else { - return Err(LexerError::NumberParse( - i, - format!("Failed to convert {digit} to a number"), - )); - }; + // if let Some(v) = { + // v + // } else { + // return Err(LexerError::NumberParse( + // i, + // format!("Failed to convert {digit} to a number"), + // )); + // }; - tokens.push(Token(i, TokenType::Number(number))); + tokens.push(Token(i, TokenType::Number(digit))); } '"' => { let mut buffer = "".to_owned(); loop { - let next = self.source.peek(); + let next = self.source.peek(); - match next { - Some('"') => { - tokens.push(Token(i, TokenType::String(buffer.clone()))); - self.source.next(); - break; - } - Some(_) => { - buffer.push(self.source.next().unwrap()); - } - None => { - return Err(LexerError::UnexpectedChar( - i, - "Unexpected End of file".to_owned(), - )); + match next { + Some('"') => { + tokens.push(Token(i, TokenType::String(buffer.clone()))); + self.source.next(); + break; + } + Some(_) => { + buffer.push(self.source.next().unwrap()); + } + None => { + return Err(LexerError::UnexpectedChar( + i, + "Unexpected End of file".to_owned(), + )); + } } } - }}, + } // LeftArrow (->) '-' if self.source.peek() == Some(&'>') => { @@ -221,6 +243,9 @@ impl<'a> Lexer<'a> { '(' => tokens.push(Token(i, TokenType::LParen)), ')' => tokens.push(Token(i, TokenType::RParen)), + '[' => tokens.push(Token(i, TokenType::LSquare)), + ']' => tokens.push(Token(i, TokenType::RSquare)), + _ if c.is_alphabetic() || c == '_' => { tokens.push(self.lex_identifier(&mut i, c)?); } @@ -273,6 +298,7 @@ pub enum ParserError { UnexpectedToken(usize, usize, String), Unimplemented(usize, usize, String), UnexpectedNode(usize, String), + NumberParse(usize, String), } /// Recursive descent parser @@ -464,34 +490,95 @@ impl<'a> Parser<'a> { } fn call(&mut self) -> Result, ParserError> { - let mut expr = self.function(); + // Left hand side + let mut expr = self.function()?; + // Calls are right-associative, so we evaluate right-to-left loop { let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() { (*i, x.clone()) } else { - return expr; + return Ok(expr); }; + // If the next token is a parenthesis then we construct a call if t == TokenType::LParen { - let potential_parameters = self.primary()?; - let parameters = if let NodeEnum::Set(set) = potential_parameters.as_ref() { - potential_parameters - } else { - // return Err(ParserError::UnexpectedNode( - // i, - // format!("Expected a Set here, but got a {potential_parameters:?}"), - // )); - Set::new(vec![potential_parameters]) - }; + self.consume(); - expr = Ok(Call::new(expr?, parameters)); + // Calls can have 0 arguments, so check and return early + if self.matchType(TokenType::RParen) { + expr = Call::new(expr, vec![]); + } else { + // Parse expressions until a patching Right-Parenthesis is found + let mut parameters = vec![self.equality()?]; + + while self.matchType(TokenType::Comma) { + parameters.push(self.equality()?); + } + + if !self.matchType(TokenType::RParen) { + return Err(ParserError::UnexpectedToken( + i, + t.len(), + "Unclosed right parenthesis".to_owned(), + )); + } + + // If the next token is a ColonEquals (assignment) then + // the user wants function assignment sugar + // + // Ie f(x) := x*5 => f := x -> x*5 + if self.matchType(TokenType::ColonEquals) { + if let NodeEnum::Symbol(_) = expr.as_ref() { + } else { + let Token(i, token) = self.previous.as_ref().unwrap(); + return Err(ParserError::UnexpectedToken( + *i, + token.len(), + format!( + "Expected an Identifier here but found a {}", + expr.type_str() + ), + )); + }; + + // Parse body + let body = self.equality()?; + + // Convert vector of expressions to vector of symbols + let mut arguments = Vec::with_capacity(parameters.len()); + for param in parameters.into_iter() { + if let NodeEnum::Symbol(symbol) = + Rc::::try_unwrap(param).unwrap() + { + arguments.push(symbol); + } else { + return Err(ParserError::UnexpectedToken( + i, + t.len(), + format!("One or more argument is not a Symbol",), + )); + } + } + + // Early exit with new desugared expression + return Ok(Rc::new( + Assign::new( + expr, + Function::new(FunctionType::UserFunction(body, arguments)), + ) + .into(), + )); + } else { + expr = Call::new(expr, parameters); + } + } } else { break; } } - expr + Ok(expr) } fn function(&mut self) -> Result, ParserError> { @@ -588,6 +675,48 @@ impl<'a> Parser<'a> { return Ok(IfElse::new(condition, expressions, else_branch)); } + self.set() + } + + fn set(&mut self) -> Result, ParserError> { + if self.matchType(TokenType::LSquare) { + // Empty set + if self.matchType(TokenType::RSquare) { + return Ok(Set::new(vec![])); + } + + let mut values = vec![self.equality()?]; + while { + if let Some(Token(_, TokenType::RSquare)) = self.tokens.peek() { + self.consume(); + false + } else { + true + } + } { + let (i, token) = if let Some(Token(i, x)) = self.tokens.peek() { + (i, x) + } else { + return Err(ParserError::UnexpectedEndOfTokens( + "Expected comma here".into(), + )); + }; + + if *token == TokenType::Comma { + self.consume(); + } else { + return Err(ParserError::UnexpectedToken( + *i, + token.len(), + format!("Expected comma here, but got {token:?}"), + )); + } + + values.push(self.equality()?); + } + + return Ok(Set::new(values)); + } self.primary() } @@ -601,7 +730,17 @@ impl<'a> Parser<'a> { }; match token { - TokenType::Number(value) => Ok(Rc::new(Constant::new(value).into())), + TokenType::Number(value) => { + let value = if let Ok(incomplete) = Float::parse(&value) { + Float::with_val_64(self.environment.get_float_precision(), incomplete) + } else { + return Err(ParserError::NumberParse( + i, + format!("Failed to convert `{value}` to a number"), + )); + }; + Ok(Rc::new(Constant::new(value).into())) + } TokenType::Identifier(string) => Ok(Rc::new( Symbol::new_from_str(string, self.environment).into(), )), @@ -611,70 +750,25 @@ impl<'a> Parser<'a> { TokenType::String(s) => Ok(StringNode::new(s)), 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) - } else { - return Err(ParserError::UnexpectedEndOfTokens( - "Unclosed right parenthesis".into(), - )); - }; - match t { - TokenType::RParen => { - self.consume(); - Ok(expr) + if !self.matchType(TokenType::RParen) { + if let Some(Token(i, t)) = self.tokens.peek() { + return Err(ParserError::UnexpectedToken( + *i, + t.len(), + format!("Expected right parenthesis here, but got {t:?}"), + )); + } else { + return Err(ParserError::UnexpectedToken( + i, + 1, + "Unclosed right parenthesis".to_owned(), + )); } - TokenType::Comma => { - let mut values = vec![expr]; - while { - if let Some(Token(_, TokenType::RParen)) = self.tokens.peek() { - self.consume(); - false - } else { - true - } - } { - let (i, token) = if let Some(Token(i, x)) = self.tokens.peek() { - (i, x) - } else { - return Err(ParserError::UnexpectedEndOfTokens( - "Expected comma here".into(), - )); - }; - - if *token == TokenType::Comma { - self.consume(); - } else { - return Err(ParserError::UnexpectedToken( - *i, - token.len(), - format!("Expected comma here, but got {token:?}"), - )); - } - - values.push(self.equality()?); - } - - Ok(Set::new(values)) - } - _ => Err(ParserError::Unimplemented( - *i, - t.len(), - format!("Expected either a comma or a right parenthesis here. Got {t:?}"), - )), } - // if t != TokenType::RParen { - // Err(ParserError::UnexpectedToken(i, t.len(), format!(""))) - // } else { - // Ok(expr) - // } + Ok(expr) } _ => Err(ParserError::UnexpectedToken( i,