cleaned up some code

This commit is contained in:
Snorre Ettrup Altschul 2025-02-24 02:37:50 +01:00
parent 04af1e291c
commit 2c38fb391b
17 changed files with 723 additions and 351 deletions

154
Cargo.lock generated
View file

@ -8,18 +8,6 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" 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]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -41,6 +29,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]] [[package]]
name = "bindgen" name = "bindgen"
version = "0.69.5" version = "0.69.5"
@ -50,7 +44,7 @@ dependencies = [
"bitflags 2.8.0", "bitflags 2.8.0",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"itertools 0.12.1", "itertools",
"lazy_static", "lazy_static",
"lazycell", "lazycell",
"log", "log",
@ -375,25 +369,26 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.7.2" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
dependencies = [ dependencies = [
"ahash 0.3.8", "ahash",
"autocfg", "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]] [[package]]
name = "hibitset" name = "hibitset"
version = "0.6.4" version = "0.6.4"
@ -409,15 +404,6 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.1" version = "0.12.1"
@ -494,51 +480,6 @@ version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 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]] [[package]]
name = "maybe-uninit" name = "maybe-uninit"
version = "2.0.0" version = "2.0.0"
@ -592,8 +533,8 @@ dependencies = [
"clay-layout", "clay-layout",
"enum_dispatch", "enum_dispatch",
"font-kit", "font-kit",
"malachite",
"raylib", "raylib",
"rug",
"termion", "termion",
"test-case", "test-case",
] ]
@ -762,6 +703,18 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 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]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -790,12 +743,6 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -830,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"hashbrown 0.7.2", "hashbrown",
"mopa", "mopa",
"smallvec", "smallvec",
"tynm", "tynm",
@ -855,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240"
dependencies = [ dependencies = [
"crossbeam-queue", "crossbeam-queue",
"hashbrown 0.7.2", "hashbrown",
"hibitset", "hibitset",
"log", "log",
"shred", "shred",
@ -982,12 +929,6 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@ -1056,6 +997,15 @@ dependencies = [
"windows-targets 0.48.5", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@ -1205,23 +1155,3 @@ dependencies = [
"once_cell", "once_cell",
"pkg-config", "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",
]

View file

@ -8,8 +8,8 @@ edition = "2024"
clay-layout = { path = "../clay-ui-rust" } clay-layout = { path = "../clay-ui-rust" }
enum_dispatch = "0.3.13" enum_dispatch = "0.3.13"
font-kit = "0.14.2" font-kit = "0.14.2"
malachite = "0.5.0"
raylib = { version = "5.0.2", features = ["wayland"] } raylib = { version = "5.0.2", features = ["wayland"] }
rug = "1.27.0"
termion = "4.0.3" termion = "4.0.3"
test-case = "3.3.1" test-case = "3.3.1"

View file

@ -49,6 +49,8 @@
libclang libclang
libllvm libllvm
nerd-fonts.mononoki nerd-fonts.mononoki
m4
]; ];
LD_LIBRARY_PATH = LD_LIBRARY_PATH =

View file

@ -1,14 +1,16 @@
use std::f32::consts;
use std::f64; use std::f64;
use std::io::{self, StdoutLock, Write, stdout}; use std::io::{self, StdoutLock, Write, stdout};
use std::rc::Rc; use std::rc::Rc;
use libopenbirch::environment::Environment; use libopenbirch::environment::Environment;
use libopenbirch::node::call::Call;
use libopenbirch::node::closure::Closure;
use libopenbirch::node::constant::Constant; use libopenbirch::node::constant::Constant;
use libopenbirch::node::empty::Empty; use libopenbirch::node::empty::Empty;
use libopenbirch::node::set::Set;
use libopenbirch::node::{Node, NodeEnum}; use libopenbirch::node::{Node, NodeEnum};
use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError}; use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError};
use malachite::base::num::basic::traits::Zero;
use malachite::rational::Rational;
#[cfg(feature = "async")] #[cfg(feature = "async")]
use termion::AsyncReader; use termion::AsyncReader;
use termion::color; use termion::color;
@ -23,6 +25,9 @@ pub struct Input {
stdout: RawTerminal<StdoutLock<'static>>, stdout: RawTerminal<StdoutLock<'static>>,
buffer: String, buffer: String,
current_char: usize, current_char: usize,
history: Vec<String>,
history_idx: Option<usize>,
} }
impl Input { impl Input {
pub fn new() -> Self { pub fn new() -> Self {
@ -35,6 +40,9 @@ impl Input {
stdout, stdout,
buffer: "".into(), buffer: "".into(),
current_char: 0, current_char: 0,
history: vec![],
history_idx: None,
} }
} }
@ -68,9 +76,11 @@ impl Input {
return Err(io::Error::from(io::ErrorKind::Interrupted)); return Err(io::Error::from(io::ErrorKind::Interrupted));
} }
self.history.insert(0, self.buffer.clone());
let r = self.buffer.clone(); let r = self.buffer.clone();
self.buffer.clear(); self.buffer.clear();
self.current_char = 0; self.current_char = 0;
self.history_idx = None;
print!("\n\r"); print!("\n\r");
return Ok(Some(r)); return Ok(Some(r));
} }
@ -107,6 +117,45 @@ impl Input {
print!("{}", termion::cursor::Right(1)); print!("{}", termion::cursor::Right(1));
let _ = self.stdout.flush(); 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) => { Key::Char(char) => {
self.buffer.insert(self.current_char, char); self.buffer.insert(self.current_char, char);
self.current_char += 1; self.current_char += 1;
@ -169,23 +218,168 @@ fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>
env.undefine(*id); env.undefine(*id);
} }
} else { } 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()))) Ok(Empty::new(format!("Undefined {} symbols", args.len())))
} }
fn get_float_precision(
args: &Vec<Rc<NodeEnum>>,
env: &mut Environment,
) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>,
env: &mut Environment,
) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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> { fn main() -> Result<(), io::Error> {
let mut input = Input::new(); let mut input = Input::new();
let mut env = Environment::new(); let mut env = Environment::new();
env.set_float_precision(128);
env.define_native_function("print", print); env.define_native_function("print", print);
env.define_native_function("unset", unset); 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( env.define(
"pi", "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()? { while let Some(source) = input.get()? {
@ -196,9 +390,7 @@ fn main() -> Result<(), io::Error> {
if tokens_result.is_err() { if tokens_result.is_err() {
match tokens_result.err().unwrap() { match tokens_result.err().unwrap() {
LexerError::UnexpectedChar(i, exp) | LexerError::NumberParse(i, exp) => { LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
print_err(i, 1, exp)
}
} }
continue; continue;
} }
@ -213,7 +405,9 @@ fn main() -> Result<(), io::Error> {
ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp), ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp),
ParserError::UnexpectedToken(i, len, exp) ParserError::UnexpectedToken(i, len, exp)
| ParserError::Unimplemented(i, len, exp) => print_err(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; continue;
} }

View file

@ -23,7 +23,9 @@ pub struct Environment {
stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>, stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
hidden: Vec<EnvironmentInternalSymbolKey>, hidden: Vec<EnvironmentInternalSymbolKey>,
disable_calls: bool, enable_closures: bool,
float_precision: u64,
// stack: Vec<Vec<EnvironmentInternalSymbolKey>>, // stack: Vec<Vec<EnvironmentInternalSymbolKey>>,
symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>, symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>,
@ -42,7 +44,9 @@ impl Environment {
stack: vec![], stack: vec![],
stack_shadows: vec![], stack_shadows: vec![],
hidden: vec![], hidden: vec![],
disable_calls: false, enable_closures: false,
float_precision: 64,
symbol_to_id: HashMap::new(), symbol_to_id: HashMap::new(),
id_to_symbol: HashMap::new(), id_to_symbol: HashMap::new(),
@ -66,16 +70,24 @@ impl Environment {
} }
} }
pub fn disable_calls(&mut self) { pub fn get_float_precision(&self) -> u64 {
self.disable_calls = true; self.float_precision
} }
pub fn enable_calls(&mut self) { pub fn set_float_precision(&mut self, precision: u64) {
self.disable_calls = false; self.float_precision = precision;
} }
pub fn calls_disabled(&self) -> bool { pub fn disable_closures(&mut self) {
self.disable_calls 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> { pub fn push_stack(&mut self) -> Result<(), String> {

View file

@ -1,8 +1,8 @@
use std::rc::Rc; 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)] #[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Add { pub struct Add {
@ -17,20 +17,33 @@ impl Node for Add {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) { match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule // Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => 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_left),
// Constant + Constant = Constant // Constant + Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => { (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new(
Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into())) Constant::new(Float::with_val_64(
} env.get_float_precision(),
a.get_value() + b.get_value(),
))
.into(),
)),
// Move constants after symbols in add // Move constants after symbols in add
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => { (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
Ok(Rc::new(Add::new(evaluated_right, evaluated_left).into())) 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())), _ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())),
} }
} }

View file

@ -1,47 +1,57 @@
use std::rc::Rc; 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)] #[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Call { pub struct Call {
function: Rc<NodeEnum>, function: Rc<NodeEnum>,
arguments: Rc<NodeEnum>, arguments: Vec<Rc<NodeEnum>>,
} }
impl Node for Call { impl Node for Call {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> { fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
if env.calls_disabled() { if env.closures_enabled() {
return Ok(Rc::new(self.clone().into())); return Ok(Rc::new(self.clone().into()));
} }
// Evaluate callee and error if its not a function // Evaluate callee and error if its not a function
let evaluated = self.function.evaluate(env)?; let evaluated = self.function.evaluate(env)?;
let func = if let NodeEnum::Function(func) = evaluated.as_ref() { // let func = if let NodeEnum::Function(func) = evaluated.as_ref() {
func // func
} else { // } else {
// return Err(format!( // return Ok(Rc::new(self.clone().into()));
// "Cannot call {} as a function", // };
// evaluated.as_string(Some(env))
// )); env.push_stack()?;
return Ok(Rc::new(self.clone().into()));
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![]; let mut arguments = vec![];
for value in set.get_values().iter() { for value in self.arguments.iter() {
arguments.push(value.evaluate(env)?); arguments.push(value.evaluate(env)?);
} }
arguments arguments
} else {
panic!(
"This shoulnd not be possible, but call constructed with arguments of type that is not set"
);
}; };
// Call function body with arguments // Call function body with arguments
match func.get_body() { let ret = match func.get_body() {
// Pass arguments to native function // Pass arguments to native function
FunctionType::Native(_name, native_function) => native_function(&arguments, env), FunctionType::Native(_name, native_function) => native_function(&arguments, env),
FunctionType::UserFunction(body, fargs) => { FunctionType::UserFunction(body, fargs) => {
@ -52,21 +62,36 @@ impl Node for Call {
arguments.len() arguments.len()
)); ));
} }
env.push_stack()?;
// Define variables // 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()); env.insert(symbol.get_value(), value.clone());
}); });
let ev = body.evaluate(env)?; let ev = body.evaluate(env)?;
env.pop_stack()?;
// Return evaluated return value for function // 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 { 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) format!("{}({})", self.function.as_string(env), arguments)
} }
@ -76,7 +101,7 @@ impl Node for Call {
} }
impl Call { impl Call {
pub fn new(function: Rc<NodeEnum>, arguments: Rc<NodeEnum>) -> Rc<NodeEnum> { pub fn new(function: Rc<NodeEnum>, arguments: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
Rc::new( Rc::new(
Self { Self {
function, function,

67
src/lib/node/closure.rs Normal file
View file

@ -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<NodeEnum>)>,
}
impl Node for Closure {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, 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!("<Closure {}>", self.function.as_string(env))
}
}
fn precedence(&self) -> Precedence {
Precedence::Call
}
}
impl Closure {
pub fn new(
function: Function,
captured_variables: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
) -> Rc<NodeEnum> {
Rc::new(
Self {
function,
captured_variables,
}
.into(),
)
}
pub fn get_captured_variables(&self) -> &Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)> {
&self.captured_variables
}
pub fn get_function(&self) -> &Function {
&self.function
}
}

View file

@ -1,13 +1,10 @@
use std::rc::Rc; use std::rc::Rc;
use malachite::{ use rug::Float;
base::{num::basic::traits::Zero, rounding_modes::RoundingMode},
rational::{Rational, conversion::primitive_float_from_rational::FloatConversionError},
};
use super::{Environment, Node, Precedence}; use super::{Environment, Node, Precedence};
pub type ConstantValue = malachite::rational::Rational; pub type ConstantValue = Float;
#[derive(Clone, Debug, PartialEq, PartialOrd)] #[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Constant { pub struct Constant {
@ -26,29 +23,15 @@ impl Node for Constant {
} }
fn as_string(&self, _env: Option<&Environment>) -> String { fn as_string(&self, _env: Option<&Environment>) -> String {
if self.value == Rational::ZERO { if self.value.is_zero() {
return "0".to_owned(); "0".to_string();
}
if self.value.approx_log() < 50. {
let x: Result<f64, FloatConversionError> = 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::<f64>(RoundingMode::Nearest)
{
format!("{}*2^{}", man, exp)
} else {
self.value.to_string()
} }
self.value
.to_string_radix(10, Some(8))
.trim_end_matches('0')
.trim_end_matches('.')
.to_string()
} }
fn precedence(&self) -> Precedence { fn precedence(&self) -> Precedence {
@ -61,6 +44,12 @@ impl Constant {
Self { value } Self { value }
} }
pub fn new_from_float(value: impl Into<f64>, env: &Environment) -> Self {
Self {
value: Float::with_val_64(env.get_float_precision(), value.into()),
}
}
pub fn get_value(&self) -> &ConstantValue { pub fn get_value(&self) -> &ConstantValue {
&self.value &self.value
} }

View file

@ -1,6 +1,6 @@
use std::rc::Rc; 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}; 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()) { match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Error if dividing by zero // 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()) 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 // Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left), (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()))
} }
// 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 // Divide a set with a constant
(NodeEnum::Set(s), NodeEnum::Constant(c)) => { (NodeEnum::Set(s), NodeEnum::Constant(c)) => {
let old_values = s.get_values(); let old_values = s.get_values();

View file

@ -1,10 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use malachite::{Natural, rational::Rational, rational::arithmetic::pow}; use rug::{Float, ops::Pow};
use malachite::base::num::arithmetic::traits::Pow;
use malachite::rational::conversion::integer_from_rational;
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant}; use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
@ -20,15 +16,13 @@ impl Node for Exponent {
let evaluated_right = self.right.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?;
match (evaluated_left.as_ref(), evaluated_right.as_ref()) { match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => { (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new(
let v = b.get_value(); Constant::new(Float::with_val_64(
let exp: u64 = if let Ok(u) = v.try_into() { env.get_float_precision(),
u a.get_value().pow(b.get_value()),
} else { ))
return Err("Exponent was too large".into()); .into(),
}; )),
Ok(Rc::new(Constant::new(a.get_value().pow(exp)).into()))
}
_ => Ok(Self::new(evaluated_left, evaluated_right)), _ => Ok(Self::new(evaluated_left, evaluated_right)),
} }

View file

@ -2,7 +2,7 @@ use std::rc::Rc;
use crate::environment::Environment; use crate::environment::Environment;
use super::{Node, NodeEnum, Precedence, symbol::Symbol}; use super::{closure::Closure, symbol::Symbol, Node, NodeEnum, Precedence};
pub type NativeFunctionType = pub type NativeFunctionType =
fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>; fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>;
@ -20,22 +20,22 @@ pub struct Function {
impl Node for Function { impl Node for Function {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> { fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
// Ok(Rc::new(self.clone().into())) Ok(Rc::new(self.clone().into()))
Ok(Rc::new( // Ok(Rc::new(
Self { // Self {
function: match &self.function { // function: match &self.function {
FunctionType::Native(_, _) => self.function.clone(), // FunctionType::Native(_, _) => self.function.clone(),
FunctionType::UserFunction(node_enum, symbols) => { // FunctionType::UserFunction(node_enum, symbols) => {
env.disable_calls(); // env.disable_calls();
let evaluated = node_enum.evaluate(env)?; // let evaluated = node_enum.evaluate(env)?;
env.enable_calls(); // env.enable_calls();
FunctionType::UserFunction(evaluated, symbols.clone()) // FunctionType::UserFunction(evaluated, symbols.clone())
} // }
}, // },
} // }
.into(), // .into(),
)) // ))
} }
fn as_string(&self, env: Option<&Environment>) -> String { fn as_string(&self, env: Option<&Environment>) -> String {

View file

@ -17,6 +17,7 @@ use set::Set;
use subtract::Subtract; use subtract::Subtract;
use symbol::Symbol; use symbol::Symbol;
use string_node::StringNode; use string_node::StringNode;
use closure::Closure;
use crate::environment::Environment; use crate::environment::Environment;
@ -37,6 +38,7 @@ pub mod set;
pub mod subtract; pub mod subtract;
pub mod symbol; pub mod symbol;
pub mod string_node; pub mod string_node;
pub mod closure;
#[enum_dispatch] #[enum_dispatch]
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)] #[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
@ -56,7 +58,7 @@ pub enum NodeEnum {
Assign, Assign,
Empty, Empty,
Function, Function,
// Closure // IMPLEMENT THIS SO CURRYING WORKS Closure, // IMPLEMENT THIS SO CURRYING WORKS
Call, Call,
Bool, 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()
}
}

View file

@ -1,6 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use malachite::{base::num::basic::traits::{One, Zero}, rational::Rational}; use rug::Float;
use crate::environment::Environment; use crate::environment::Environment;
@ -23,14 +23,14 @@ impl Node for Multiply {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) { match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule // Zero rule
(NodeEnum::Constant(zero), _) | (_, NodeEnum::Constant(zero)) (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 // Identity rule
(NodeEnum::Constant(one), _) if one.get_value() == &Rational::ONE => Ok(evaluated_right), (NodeEnum::Constant(one), _) if one.get_value() == &0.0 => 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_left),
// Multiply into parenthesis (add) // Multiply into parenthesis (add)
(_, NodeEnum::Add(add)) => Ok(Add::new_rc( (_, NodeEnum::Add(add)) => Ok(Add::new_rc(
@ -63,9 +63,12 @@ impl Node for Multiply {
div.get_right(), div.get_right(),
)), )),
// Constant + Constant = Constant // Constant * Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => { (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 // Move constant infront of symbols
@ -146,14 +149,22 @@ impl Multiply {
) -> Result<Rc<NodeEnum>, String> { ) -> Result<Rc<NodeEnum>, String> {
match (multiply.left.as_ref(), multiply.right.as_ref()) { match (multiply.left.as_ref(), multiply.right.as_ref()) {
(NodeEnum::Constant(c2), _) => { (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( Ok(Rc::new(
Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), Multiply::new(Rc::new(new_const), multiply.right.clone()).into(),
)) ))
} }
(_, NodeEnum::Constant(c2)) => { (_, 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( Ok(Rc::new(
Multiply::new(Rc::new(new_const), multiply.right.clone()).into(), Multiply::new(Rc::new(new_const), multiply.right.clone()).into(),
)) ))

View file

@ -20,7 +20,7 @@ impl Node for Set {
fn as_string(&self, env: Option<&Environment>) -> String { fn as_string(&self, env: Option<&Environment>) -> String {
format!( format!(
"({})", "[{}]",
self.values self.values
.iter() .iter()
.map(|x| x.as_string(env)) .map(|x| x.as_string(env))

View file

@ -1,6 +1,6 @@
use std::rc::Rc; 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};
@ -17,18 +17,21 @@ impl Node for Subtract {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) { match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule // Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right), (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => 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_left),
// Constant + Constant = Constant // Constant - Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => { (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( (NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
Subtract::new(evaluated_left, evaluated_right).into(), 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( (NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
Subtract::new(evaluated_right, evaluated_left).into(), Subtract::new(evaluated_right, evaluated_left).into(),
)), )),

View file

@ -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::{ use crate::{
environment::Environment, environment::Environment,
node::{ 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)] #[derive(Debug, PartialEq, Clone)]
pub enum TokenType { pub enum TokenType {
// Space, // Space,
Number(ConstantValue), Number(String),
Identifier(String), Identifier(String),
String(String), String(String),
@ -34,6 +51,9 @@ pub enum TokenType {
RParen, RParen,
LParen, LParen,
RSquare,
LSquare,
Comma, Comma,
If, If,
@ -68,6 +88,9 @@ impl TokenType {
TokenType::RParen => 1, TokenType::RParen => 1,
TokenType::LParen => 1, TokenType::LParen => 1,
TokenType::RSquare => 1,
TokenType::LSquare => 1,
TokenType::Comma => 1, TokenType::Comma => 1,
TokenType::If => 2, TokenType::If => 2,
@ -88,7 +111,6 @@ pub struct Lexer<'a> {
#[derive(Debug)] #[derive(Debug)]
pub enum LexerError { pub enum LexerError {
UnexpectedChar(usize, String), UnexpectedChar(usize, String),
NumberParse(usize, String),
} }
impl<'a> Lexer<'a> { impl<'a> Lexer<'a> {
@ -125,7 +147,7 @@ impl<'a> Lexer<'a> {
loop { loop {
let d = self.source.peek(); let d = self.source.peek();
match d { match d {
Some('0'..='9') | Some('e') | Some('E') => { Some('0'..='9') => {
digit.push(*d.unwrap()); digit.push(*d.unwrap());
self.source.next(); self.source.next();
i += 1; i += 1;
@ -148,41 +170,41 @@ impl<'a> Lexer<'a> {
} }
} }
} }
let number = // if let Some(v) = {
if let Some(v) = ConstantValue::from_sci_string_simplest(digit.as_str()) { // v
v // } else {
} else { // return Err(LexerError::NumberParse(
return Err(LexerError::NumberParse( // i,
i, // format!("Failed to convert {digit} to a number"),
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(); let mut buffer = "".to_owned();
loop { loop {
let next = self.source.peek(); let next = self.source.peek();
match next { match next {
Some('"') => { Some('"') => {
tokens.push(Token(i, TokenType::String(buffer.clone()))); tokens.push(Token(i, TokenType::String(buffer.clone())));
self.source.next(); self.source.next();
break; break;
} }
Some(_) => { Some(_) => {
buffer.push(self.source.next().unwrap()); buffer.push(self.source.next().unwrap());
} }
None => { None => {
return Err(LexerError::UnexpectedChar( return Err(LexerError::UnexpectedChar(
i, i,
"Unexpected End of file".to_owned(), "Unexpected End of file".to_owned(),
)); ));
}
} }
} }
}}, }
// LeftArrow (->) // LeftArrow (->)
'-' if self.source.peek() == Some(&'>') => { '-' if self.source.peek() == Some(&'>') => {
@ -221,6 +243,9 @@ impl<'a> Lexer<'a> {
'(' => tokens.push(Token(i, TokenType::LParen)), '(' => tokens.push(Token(i, TokenType::LParen)),
')' => tokens.push(Token(i, TokenType::RParen)), ')' => tokens.push(Token(i, TokenType::RParen)),
'[' => tokens.push(Token(i, TokenType::LSquare)),
']' => tokens.push(Token(i, TokenType::RSquare)),
_ if c.is_alphabetic() || c == '_' => { _ if c.is_alphabetic() || c == '_' => {
tokens.push(self.lex_identifier(&mut i, c)?); tokens.push(self.lex_identifier(&mut i, c)?);
} }
@ -273,6 +298,7 @@ pub enum ParserError {
UnexpectedToken(usize, usize, String), UnexpectedToken(usize, usize, String),
Unimplemented(usize, usize, String), Unimplemented(usize, usize, String),
UnexpectedNode(usize, String), UnexpectedNode(usize, String),
NumberParse(usize, String),
} }
/// Recursive descent parser /// Recursive descent parser
@ -464,34 +490,95 @@ impl<'a> Parser<'a> {
} }
fn call(&mut self) -> Result<Rc<NodeEnum>, ParserError> { fn call(&mut self) -> Result<Rc<NodeEnum>, 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 { loop {
let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() { let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() {
(*i, x.clone()) (*i, x.clone())
} else { } else {
return expr; return Ok(expr);
}; };
// If the next token is a parenthesis then we construct a call
if t == TokenType::LParen { if t == TokenType::LParen {
let potential_parameters = self.primary()?; self.consume();
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])
};
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::<NodeEnum>::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 { } else {
break; break;
} }
} }
expr Ok(expr)
} }
fn function(&mut self) -> Result<Rc<NodeEnum>, ParserError> { fn function(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
@ -588,6 +675,48 @@ impl<'a> Parser<'a> {
return Ok(IfElse::new(condition, expressions, else_branch)); return Ok(IfElse::new(condition, expressions, else_branch));
} }
self.set()
}
fn set(&mut self) -> Result<Rc<NodeEnum>, 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() self.primary()
} }
@ -601,7 +730,17 @@ impl<'a> Parser<'a> {
}; };
match token { 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( TokenType::Identifier(string) => Ok(Rc::new(
Symbol::new_from_str(string, self.environment).into(), Symbol::new_from_str(string, self.environment).into(),
)), )),
@ -611,70 +750,25 @@ impl<'a> Parser<'a> {
TokenType::String(s) => Ok(StringNode::new(s)), TokenType::String(s) => Ok(StringNode::new(s)),
TokenType::LParen => { TokenType::LParen => {
// Empty set
if self.matchType(TokenType::RParen) {
return Ok(Set::new(vec![]));
}
let expr = self.expression()?; 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 { if !self.matchType(TokenType::RParen) {
TokenType::RParen => { if let Some(Token(i, t)) = self.tokens.peek() {
self.consume(); return Err(ParserError::UnexpectedToken(
Ok(expr) *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 { Ok(expr)
// Err(ParserError::UnexpectedToken(i, t.len(), format!("")))
// } else {
// Ok(expr)
// }
} }
_ => Err(ParserError::UnexpectedToken( _ => Err(ParserError::UnexpectedToken(
i, i,