Compare commits
10 commits
a5cc6813d1
...
cd758a7fce
Author | SHA1 | Date | |
---|---|---|---|
|
cd758a7fce | ||
|
40fef756df | ||
|
9da6f7d302 | ||
|
bb1b051598 | ||
|
2c38fb391b | ||
|
04af1e291c | ||
|
ff5855af76 | ||
|
fa8aae88b4 | ||
|
b8f2963769 | ||
|
a450eb05d8 |
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -29,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"
|
||||||
|
@ -363,6 +369,16 @@ 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"
|
||||||
|
@ -425,6 +441,12 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -512,6 +534,7 @@ dependencies = [
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
"font-kit",
|
"font-kit",
|
||||||
"raylib",
|
"raylib",
|
||||||
|
"rug",
|
||||||
"termion",
|
"termion",
|
||||||
"test-case",
|
"test-case",
|
||||||
]
|
]
|
||||||
|
@ -680,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"
|
||||||
|
@ -962,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"
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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"
|
||||||
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"
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
libclang
|
libclang
|
||||||
libllvm
|
libllvm
|
||||||
nerd-fonts.mononoki
|
nerd-fonts.mononoki
|
||||||
|
|
||||||
|
m4
|
||||||
];
|
];
|
||||||
|
|
||||||
LD_LIBRARY_PATH =
|
LD_LIBRARY_PATH =
|
||||||
|
|
336
src/cli-repl.rs
336
src/cli-repl.rs
|
@ -1,10 +1,15 @@
|
||||||
|
use std::f64;
|
||||||
use std::io::{self, StdoutLock, Write, stdout};
|
use std::io::{self, StdoutLock, Write, stdout};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use libopenbirch::environment::Environment;
|
use libopenbirch::environment::{Environment, EnvironmentInternalSymbolKey};
|
||||||
use libopenbirch::node::Node;
|
use libopenbirch::node::call::Call;
|
||||||
use libopenbirch::parser::{Lexer, Parser, ParserError};
|
use libopenbirch::node::constant::Constant;
|
||||||
#[cfg(feature = "async")]
|
use libopenbirch::node::empty::Empty;
|
||||||
use termion::AsyncReader;
|
use libopenbirch::node::set::Set;
|
||||||
|
use libopenbirch::node::string_node::StringNode;
|
||||||
|
use libopenbirch::node::{Node, NodeEnum};
|
||||||
|
use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError};
|
||||||
use termion::color;
|
use termion::color;
|
||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
use termion::input::TermReadEventsAndRaw;
|
use termion::input::TermReadEventsAndRaw;
|
||||||
|
@ -17,6 +22,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 {
|
||||||
|
@ -29,6 +37,9 @@ impl Input {
|
||||||
stdout,
|
stdout,
|
||||||
buffer: "".into(),
|
buffer: "".into(),
|
||||||
current_char: 0,
|
current_char: 0,
|
||||||
|
|
||||||
|
history: vec![],
|
||||||
|
history_idx: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,9 +73,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));
|
||||||
}
|
}
|
||||||
|
@ -101,6 +114,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;
|
||||||
|
@ -117,19 +169,19 @@ impl Input {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
termion::event::Event::Mouse(mouse_event) => {}
|
termion::event::Event::Mouse(_mouse_event) => {}
|
||||||
termion::event::Event::Unsupported(items) => {}
|
termion::event::Event::Unsupported(_items) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_raw(&mut self) {
|
pub fn disable_raw(&mut self) -> io::Result<()> {
|
||||||
self.stdout.suspend_raw_mode();
|
self.stdout.suspend_raw_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_raw(&mut self) {
|
pub fn enable_raw(&mut self) -> io::Result<()> {
|
||||||
self.stdout.activate_raw_mode();
|
self.stdout.activate_raw_mode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,31 +200,273 @@ fn print_err(i: usize, len: usize, exp: String) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
for expr in args {
|
||||||
|
println!("\r{}", expr.as_string(Some(env)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Empty::new(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
for arg in args {
|
||||||
|
if let NodeEnum::StringNode(string) = arg.as_ref() {
|
||||||
|
if let Some(id) = env.str_to_id(string.get_value()) {
|
||||||
|
env.undefine(*id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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<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 benchmark(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
if args.len() != 1 {
|
||||||
|
Err(format!("Expected 1 argument but got {}", args.len()))?
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::time::Instant;
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
let result = Call::new(args.first().unwrap().clone(), vec![]).evaluate(env)?;
|
||||||
|
|
||||||
|
let elapsed = now.elapsed();
|
||||||
|
|
||||||
|
let time_str = StringNode::new(elapsed.as_secs_f64().to_string()).into();
|
||||||
|
let time_const = Rc::new(Constant::new_from_float(elapsed.as_secs_f64(), &env).into());
|
||||||
|
|
||||||
|
Ok(Set::new(vec![time_str, time_const, result]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn head(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(format!(
|
||||||
|
"Expected a Set but got a {}",
|
||||||
|
args.first().unwrap().type_str()
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let head = set.first().cloned().unwrap_or(Set::new(vec![]));
|
||||||
|
let len = set.len();
|
||||||
|
let tail = if len > 1 {
|
||||||
|
set[1..len].to_vec()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Set::new(vec![head, Set::new(tail)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(format!("Expected 1-2 argument but got {}", args.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr = args.first().unwrap().clone();
|
||||||
|
|
||||||
|
let var = if let NodeEnum::Symbol(symbol) = args.get(1).unwrap().as_ref() {
|
||||||
|
symbol.get_value()
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"Argument 2 is expected to be a Symbol but got a {}",
|
||||||
|
args.get(1).unwrap().type_str()
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff_internally(
|
||||||
|
expr: Rc<NodeEnum>,
|
||||||
|
var: EnvironmentInternalSymbolKey,
|
||||||
|
env: &mut Environment,
|
||||||
|
) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
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("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("head", head);
|
||||||
|
env.define_native_function("get", get);
|
||||||
|
env.define_native_function("length", length);
|
||||||
|
env.define_native_function("benchmark", benchmark);
|
||||||
|
|
||||||
|
env.define(
|
||||||
|
"pi",
|
||||||
|
Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()),
|
||||||
|
);
|
||||||
|
|
||||||
while let Some(source) = input.get()? {
|
while let Some(source) = input.get()? {
|
||||||
|
input.disable_raw()?;
|
||||||
|
|
||||||
let mut lexer = Lexer::new(&source);
|
let mut lexer = Lexer::new(&source);
|
||||||
let tokens_result = lexer.lex();
|
let tokens_result = lexer.lex();
|
||||||
|
|
||||||
if tokens_result.is_err() {
|
if tokens_result.is_err() {
|
||||||
match tokens_result.err().unwrap() {
|
match tokens_result.err().unwrap() {
|
||||||
libopenbirch::parser::LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
|
LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
input.disable_raw();
|
|
||||||
|
|
||||||
let tokens = tokens_result.unwrap();
|
let tokens = tokens_result.unwrap();
|
||||||
let mut parser = Parser::new(tokens, &mut env);
|
let mut parser = Parser::new(tokens, &mut env);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
input.enable_raw();
|
|
||||||
|
|
||||||
let nodes = match parser.parse() {
|
let nodes = match parser.parse() {
|
||||||
Ok(nodes) => nodes,
|
Ok(nodes) => nodes,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -180,7 +474,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;
|
||||||
}
|
}
|
||||||
|
@ -196,6 +492,8 @@ fn main() -> Result<(), io::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.enable_raw()?;
|
||||||
|
|
||||||
print!("{}", color::Fg(color::Reset));
|
print!("{}", color::Fg(color::Reset));
|
||||||
|
|
||||||
// println!("\t{}{source}{}", termion::color::Fg(termion::color::Blue), termion::color::Fg(termion::color::Reset));
|
// println!("\t{}{source}{}", termion::color::Fg(termion::color::Blue), termion::color::Fg(termion::color::Reset));
|
||||||
|
|
|
@ -1,34 +1,198 @@
|
||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use crate::node::NodeEnum;
|
use crate::node::{
|
||||||
|
NodeEnum,
|
||||||
|
function::{Function, FunctionType, NativeFunctionType},
|
||||||
|
};
|
||||||
|
|
||||||
pub type EnvironmentInternalSymbolKey = u16;
|
pub type EnvironmentInternalSymbolKey = u16;
|
||||||
// pub type Environment = HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>;
|
// pub type Environment = HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>;
|
||||||
|
|
||||||
|
struct Scope {
|
||||||
|
pub stack_len: usize,
|
||||||
|
pub shadow_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
map: HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>,
|
map: HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>,
|
||||||
|
stack_depth: usize,
|
||||||
|
max_stack_depth: usize,
|
||||||
|
|
||||||
|
stack_sizes: Vec<Scope>,
|
||||||
|
stack: Vec<EnvironmentInternalSymbolKey>,
|
||||||
|
stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
|
||||||
|
|
||||||
|
hidden: Vec<EnvironmentInternalSymbolKey>,
|
||||||
|
enable_closures: bool,
|
||||||
|
|
||||||
|
float_precision: u64,
|
||||||
|
|
||||||
|
// stack: Vec<Vec<EnvironmentInternalSymbolKey>>,
|
||||||
symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>,
|
symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>,
|
||||||
id_to_symbol: HashMap<EnvironmentInternalSymbolKey, String>,
|
id_to_symbol: HashMap<EnvironmentInternalSymbolKey, String>,
|
||||||
unique_keys: EnvironmentInternalSymbolKey
|
unique_keys: EnvironmentInternalSymbolKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
map: HashMap::new(),
|
map: HashMap::new(),
|
||||||
|
stack_depth: 0,
|
||||||
|
max_stack_depth: 5000,
|
||||||
|
|
||||||
|
stack_sizes: vec![],
|
||||||
|
stack: vec![],
|
||||||
|
stack_shadows: vec![],
|
||||||
|
hidden: vec![],
|
||||||
|
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(),
|
||||||
unique_keys: 0
|
unique_keys: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hide(&mut self, key: EnvironmentInternalSymbolKey) {
|
||||||
|
match self.hidden.binary_search(&key) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(pos) => self.hidden.insert(pos, key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unhide(&mut self, key: EnvironmentInternalSymbolKey) {
|
||||||
|
match self.hidden.binary_search(&key) {
|
||||||
|
Ok(pos) => {
|
||||||
|
self.hidden.remove(pos);
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_float_precision(&self) -> u64 {
|
||||||
|
self.float_precision
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_float_precision(&mut self, precision: u64) {
|
||||||
|
self.float_precision = precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
if self.stack_depth == self.max_stack_depth {
|
||||||
|
self.unwind_stack();
|
||||||
|
return Err("Max Stack depth exceeded".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stack_depth += 1;
|
||||||
|
self.stack_sizes.push(Scope {
|
||||||
|
stack_len: 0,
|
||||||
|
shadow_len: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
// debug_assert!(self.stack_depth == self.stack.len() - 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_stack(&mut self) -> Result<(), String> {
|
||||||
|
if self.stack_depth == 0 {
|
||||||
|
return Err("Trying to pop empty stack".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stack_depth -= 1;
|
||||||
|
let scope = self.stack_sizes.pop().unwrap();
|
||||||
|
for _ in 0..scope.stack_len {
|
||||||
|
self.map.remove(&self.stack.pop().unwrap());
|
||||||
|
}
|
||||||
|
for _ in 0..scope.shadow_len {
|
||||||
|
let (id, value) = self.stack_shadows.pop().unwrap();
|
||||||
|
self.map.insert(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwind_stack(&mut self) {
|
||||||
|
for scope in &self.stack_sizes {
|
||||||
|
for _ in 0..scope.stack_len {
|
||||||
|
self.map.remove(&self.stack.pop().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stack_sizes = vec![];
|
||||||
|
self.stack_shadows = vec![];
|
||||||
|
self.stack_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define(&mut self, name: impl Into<String>, value: Rc<NodeEnum>) {
|
||||||
|
let name = name.into();
|
||||||
|
let id = if let Some(value) = self.str_to_id(&name) {
|
||||||
|
*value
|
||||||
|
} else {
|
||||||
|
self.get_new_id()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.map.insert(id, value);
|
||||||
|
self.insert_id_to_str(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn undefine(&mut self, key: EnvironmentInternalSymbolKey) {
|
||||||
|
self.map.remove(&key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_native_function(&mut self, name: &'static str, func: NativeFunctionType) {
|
||||||
|
let f = Function::new(FunctionType::Native(name, func));
|
||||||
|
|
||||||
|
self.define(name.to_owned(), f);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: &EnvironmentInternalSymbolKey) -> Option<&Rc<NodeEnum>> {
|
pub fn get(&self, key: &EnvironmentInternalSymbolKey) -> Option<&Rc<NodeEnum>> {
|
||||||
|
if self.hidden.binary_search(key).is_ok() {
|
||||||
|
// This value is hidden, so we pretend its not defined
|
||||||
|
return None;
|
||||||
|
}
|
||||||
self.map.get(key)
|
self.map.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_current_scope(&mut self) -> &[u16] {
|
||||||
|
let len = self.stack.len();
|
||||||
|
let current_scope_size = self.stack_sizes.last().unwrap().stack_len;
|
||||||
|
let start = len - current_scope_size;
|
||||||
|
self.stack.get(start..len).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, key: EnvironmentInternalSymbolKey, value: Rc<NodeEnum>) {
|
pub fn insert(&mut self, key: EnvironmentInternalSymbolKey, value: Rc<NodeEnum>) {
|
||||||
|
if self.stack_depth != 0 {
|
||||||
|
let mut shadow = false;
|
||||||
|
if self.map.get(&key).is_some() {
|
||||||
|
if !self.get_current_scope().contains(&key) {
|
||||||
|
// We need to shadow this variable
|
||||||
|
self.stack_shadows
|
||||||
|
.push((key, self.map.insert(key, value.clone()).unwrap()));
|
||||||
|
shadow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let scope = unsafe { self.stack_sizes.get_unchecked_mut(self.stack_depth - 1) };
|
||||||
|
scope.stack_len += 1;
|
||||||
|
self.stack.push(key);
|
||||||
|
if shadow {
|
||||||
|
scope.shadow_len += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
self.map.insert(key, value);
|
self.map.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
use std::io::{self, stdout, BufRead, Write};
|
|
||||||
|
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod environment;
|
pub mod environment;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
fn main() {
|
|
||||||
loop {
|
|
||||||
print!("> ");
|
|
||||||
let _ = stdout().flush();
|
|
||||||
|
|
||||||
let mut line = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut line).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
use rug::Float;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Environment, Node, NodeEnum, Precedence, constant::Constant, set::Set, string_node::StringNode,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Add {
|
pub struct Add {
|
||||||
|
@ -15,24 +19,38 @@ 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() == 0. => Ok(evaluated_right),
|
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right),
|
||||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => 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(),
|
||||||
|
)),
|
||||||
|
|
||||||
// Symbol + Constant we just return the same
|
// Move constants after symbols in add
|
||||||
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => {
|
|
||||||
Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant + Symbol we switch them around so the constant is last
|
|
||||||
(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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate strings
|
||||||
|
(NodeEnum::StringNode(s1), NodeEnum::StringNode(s2)) => {
|
||||||
|
Ok(StringNode::new(s1.get_value().clone() + s2.get_value()))
|
||||||
|
}
|
||||||
|
|
||||||
_ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())),
|
_ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +79,10 @@ impl Add {
|
||||||
Self { left, right }
|
Self { left, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_left(&self) -> Rc<NodeEnum> {
|
pub fn get_left(&self) -> Rc<NodeEnum> {
|
||||||
self.left.clone()
|
self.left.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ pub struct Assign {
|
||||||
|
|
||||||
impl Node for Assign {
|
impl Node for Assign {
|
||||||
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
if let NodeEnum::Symbol(symbol) = self.left.as_ref() {
|
match self.left.as_ref() {
|
||||||
|
NodeEnum::Symbol(symbol) => {
|
||||||
let name = env
|
let name = env
|
||||||
.id_to_str(&symbol.get_value())
|
.id_to_str(&symbol.get_value())
|
||||||
.expect("Unknown symbol")
|
.expect("Unknown symbol")
|
||||||
|
@ -22,11 +23,57 @@ impl Node for Assign {
|
||||||
"{name} := {}",
|
"{name} := {}",
|
||||||
right.as_string(Some(env))
|
right.as_string(Some(env))
|
||||||
)))
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeEnum::Set(set) => {
|
||||||
|
// Check if every element of the set is a symbol
|
||||||
|
let values = set.get_values();
|
||||||
|
|
||||||
|
if values.len() == 0 {
|
||||||
|
return Err(format!("Cannot assign to an empty Set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut symbols = Vec::with_capacity(values.len());
|
||||||
|
for node in values {
|
||||||
|
if let NodeEnum::Symbol(symbol) = node.as_ref() {
|
||||||
|
symbols.push(symbol);
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
return Err(format!("Cannot assign to a {}", node.type_str()));
|
||||||
"Cannot assign to a {}",
|
}
|
||||||
self.left.as_string(Some(env))
|
}
|
||||||
))
|
|
||||||
|
// Evaluate right hand side and check that it is a set of the same size
|
||||||
|
let right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
|
if let NodeEnum::Set(set) = right.as_ref() {
|
||||||
|
let values = set.get_values();
|
||||||
|
if values.len() != symbols.len() {
|
||||||
|
return Err(format!(
|
||||||
|
"Expected rhs with {} elements but got {}",
|
||||||
|
symbols.len(),
|
||||||
|
values.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (symbol,value) in symbols.iter().zip(values.into_iter()) {
|
||||||
|
env.insert(symbol.get_value(), value.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Empty::new(format!(
|
||||||
|
"[{}] := {}",
|
||||||
|
symbols
|
||||||
|
.iter()
|
||||||
|
.map(|x| env.id_to_str(&x.get_value()).unwrap().clone())
|
||||||
|
.reduce(|x, acc| format!("{x}, {acc}"))
|
||||||
|
.unwrap(),
|
||||||
|
right.as_string(Some(env))
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
return Err(format!("Cannot unpack {} for assignment", right.type_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(format!("Cannot assign to a {}", self.left.type_str())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,70 +1,104 @@
|
||||||
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, symbol::Symbol};
|
use super::{Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
#[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.closures_enabled() {
|
||||||
|
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()?;
|
||||||
|
|
||||||
|
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"
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if argument counts match
|
// Call function body with arguments
|
||||||
let fargs = func.get_arguments();
|
let ret = match func.get_body() {
|
||||||
|
// Pass arguments to native function
|
||||||
|
FunctionType::Native(_name, native_function) => native_function(&arguments, env),
|
||||||
|
FunctionType::UserFunction(body, fargs) => {
|
||||||
if fargs.len() != arguments.len() {
|
if fargs.len() != arguments.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error calling function. Expected {} arguments, but got {}",
|
"Error calling function. Expected {} arguments, but got {}",
|
||||||
func.get_arguments().len(),
|
fargs.len(),
|
||||||
arguments.len()
|
arguments.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call function body with arguments
|
|
||||||
match func.get_body() {
|
|
||||||
// Pass arguments to native function
|
|
||||||
FunctionType::Native(_name, native_function) => native_function(&arguments),
|
|
||||||
FunctionType::UserFunction(node_enum) => {
|
|
||||||
// TODO: Push scope
|
|
||||||
// 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 = node_enum.evaluate(env);
|
let ev = body.evaluate(env);
|
||||||
// TODO: Pop scope
|
|
||||||
|
if ev.is_err() {
|
||||||
|
env.pop_stack()?;
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ev = ev?;
|
||||||
|
|
||||||
// Return evaluated return value for function
|
// Return evaluated return value for function
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +108,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
67
src/lib/node/closure.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
237
src/lib/node/comparison.rs
Normal file
237
src/lib/node/comparison.rs
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::environment::Environment;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, if_else::Bool};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Greater {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Vec<Rc<NodeEnum>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Less {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Vec<Rc<NodeEnum>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct GreaterEquals {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Vec<Rc<NodeEnum>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct LessEquals {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Vec<Rc<NodeEnum>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Greater {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut expr = self.left.evaluate(env)?;
|
||||||
|
for e in &self.right {
|
||||||
|
let e = e.evaluate(env)?;
|
||||||
|
let evaluation = expr.partial_cmp(&e);
|
||||||
|
match evaluation {
|
||||||
|
Some(cmp) if cmp != std::cmp::Ordering::Greater => {
|
||||||
|
return Ok(Rc::new(Bool::False.into()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return self.abort(env);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
return Ok(Rc::new(Bool::True.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
format!(
|
||||||
|
"{} > {}",
|
||||||
|
self.left.as_string(env),
|
||||||
|
self.right
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|a, b| a + " > " + &b)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Compare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Less {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut expr = self.left.evaluate(env)?;
|
||||||
|
for e in &self.right {
|
||||||
|
let e = e.evaluate(env)?;
|
||||||
|
let evaluation = expr.partial_cmp(&e);
|
||||||
|
match evaluation {
|
||||||
|
Some(cmp) if cmp != std::cmp::Ordering::Less => {
|
||||||
|
return Ok(Rc::new(Bool::False.into()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return self.abort(env);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
return Ok(Rc::new(Bool::True.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
format!(
|
||||||
|
"{} < {}",
|
||||||
|
self.left.as_string(env),
|
||||||
|
self.right
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|a, b| a + " < " + &b)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Compare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for GreaterEquals {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut expr = self.left.evaluate(env)?;
|
||||||
|
for e in &self.right {
|
||||||
|
let e = e.evaluate(env)?;
|
||||||
|
let evaluation = expr.partial_cmp(&e);
|
||||||
|
match evaluation {
|
||||||
|
Some(cmp)
|
||||||
|
if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Greater =>
|
||||||
|
{
|
||||||
|
return Ok(Rc::new(Bool::False.into()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return self.abort(env);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
return Ok(Rc::new(Bool::True.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
format!(
|
||||||
|
"{} >= {}",
|
||||||
|
self.left.as_string(env),
|
||||||
|
self.right
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|a, b| a + " >= " + &b)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Compare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for LessEquals {
|
||||||
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut expr = self.left.evaluate(env)?;
|
||||||
|
for e in &self.right {
|
||||||
|
let e = e.evaluate(env)?;
|
||||||
|
let evaluation = expr.partial_cmp(&e);
|
||||||
|
match evaluation {
|
||||||
|
Some(cmp)
|
||||||
|
if cmp != std::cmp::Ordering::Equal || cmp != std::cmp::Ordering::Less =>
|
||||||
|
{
|
||||||
|
return Ok(Rc::new(Bool::False.into()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return self.abort(env);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
return Ok(Rc::new(Bool::True.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
format!(
|
||||||
|
"{} <= {}",
|
||||||
|
self.left.as_string(env),
|
||||||
|
self.right
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|a, b| a + " <= " + &b)
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Compare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Greater {
|
||||||
|
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut arguments = vec![];
|
||||||
|
for value in self.right.iter() {
|
||||||
|
arguments.push(value.evaluate(env)?);
|
||||||
|
}
|
||||||
|
Ok(Self::new(self.left.evaluate(env)?, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Less {
|
||||||
|
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut arguments = vec![];
|
||||||
|
for value in self.right.iter() {
|
||||||
|
arguments.push(value.evaluate(env)?);
|
||||||
|
}
|
||||||
|
Ok(Self::new(self.left.evaluate(env)?, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LessEquals {
|
||||||
|
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut arguments = vec![];
|
||||||
|
for value in self.right.iter() {
|
||||||
|
arguments.push(value.evaluate(env)?);
|
||||||
|
}
|
||||||
|
Ok(Self::new(self.left.evaluate(env)?, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GreaterEquals {
|
||||||
|
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abort(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
|
let mut arguments = vec![];
|
||||||
|
for value in self.right.iter() {
|
||||||
|
arguments.push(value.evaluate(env)?);
|
||||||
|
}
|
||||||
|
Ok(Self::new(self.left.evaluate(env)?, arguments))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use rug::Float;
|
||||||
|
|
||||||
use super::{Environment, Node, Precedence};
|
use super::{Environment, Node, Precedence};
|
||||||
|
|
||||||
pub type ConstantValue = f64;
|
pub type ConstantValue = Float;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Constant {
|
pub struct Constant {
|
||||||
|
@ -21,7 +23,19 @@ impl Node for Constant {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self, _env: Option<&Environment>) -> String {
|
fn as_string(&self, _env: Option<&Environment>) -> String {
|
||||||
self.value.to_string()
|
if self.value.is_zero() {
|
||||||
|
return "0".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.value < 1. && self.value > 0.001 {
|
||||||
|
return self.value.to_f64().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.value
|
||||||
|
.to_string_radix(10, Some(8))
|
||||||
|
.trim_end_matches('0')
|
||||||
|
.trim_end_matches('.')
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn precedence(&self) -> Precedence {
|
fn precedence(&self) -> Precedence {
|
||||||
|
@ -34,8 +48,14 @@ impl Constant {
|
||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_value(&self) -> ConstantValue {
|
pub fn new_from_float(value: impl Into<f64>, env: &Environment) -> Self {
|
||||||
self.value
|
Self {
|
||||||
|
value: Float::with_val_64(env.get_float_precision(), value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> &ConstantValue {
|
||||||
|
&self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_value(&mut self, value: ConstantValue) {
|
pub fn set_value(&mut self, value: ConstantValue) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{constant::Constant, set::Set, Node, NodeEnum, Precedence};
|
use rug::Float;
|
||||||
|
|
||||||
|
use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Divide {
|
pub struct Divide {
|
||||||
|
@ -15,27 +17,47 @@ 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() == 0. => {
|
(_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => {
|
||||||
Err("Division by Zero".into())
|
Err("Division by Zero".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero rule
|
(_, _) if evaluated_left == evaluated_right => {
|
||||||
(NodeEnum::Constant(zero), _) if zero.get_value() == 0. => {
|
Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
|
||||||
Ok(evaluated_left)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant + Constant = Constant
|
// Zero rule
|
||||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_left),
|
||||||
Ok(Rc::new(Constant::new(a.get_value() / b.get_value()).into()))
|
|
||||||
|
// Cancel out symbols and constants
|
||||||
|
(NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
|
||||||
|
match (
|
||||||
|
(m1.get_left().as_ref(), m1.get_right().as_ref()),
|
||||||
|
(m2.get_left().as_ref(), m2.get_right().as_ref()),
|
||||||
|
) {
|
||||||
|
((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2))
|
||||||
|
| ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2))
|
||||||
|
| ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2)))
|
||||||
|
| ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => {
|
||||||
|
if s1 == s2 {
|
||||||
|
Divide::new(Rc::new(o1.clone()), Rc::new(o2.clone())).evaluate(env)
|
||||||
|
} else {
|
||||||
|
Ok(Divide::new_rc(evaluated_left, evaluated_right))
|
||||||
}
|
}
|
||||||
// Symbol + Constant we just return the same
|
|
||||||
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => {
|
|
||||||
Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into()))
|
|
||||||
}
|
}
|
||||||
// Constant + Symbol we switch them around so the constant is last
|
|
||||||
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
|
_ => Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into())),
|
||||||
Ok(Rc::new(Divide::new(evaluated_right, evaluated_left).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();
|
||||||
|
@ -90,6 +112,10 @@ impl Divide {
|
||||||
Self { left, right }
|
Self { left, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self::new(left, right).into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_left(&self) -> Rc<NodeEnum> {
|
pub fn get_left(&self) -> Rc<NodeEnum> {
|
||||||
self.left.clone()
|
self.left.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,18 @@ use std::{rc::Rc, sync::LazyLock};
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence};
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialOrd)]
|
||||||
pub struct Empty {
|
pub struct Empty {
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Empty {
|
||||||
|
fn eq(&self, _other: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for Empty {}
|
||||||
|
|
||||||
impl Node for Empty {
|
impl Node for Empty {
|
||||||
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
Ok(Empty::EMPTY.clone())
|
Ok(Empty::EMPTY.clone())
|
||||||
|
@ -15,7 +22,7 @@ impl Node for Empty {
|
||||||
fn as_string(&self, _: Option<&Environment>) -> String {
|
fn as_string(&self, _: Option<&Environment>) -> String {
|
||||||
match &self.message {
|
match &self.message {
|
||||||
Some(m) => m.clone(),
|
Some(m) => m.clone(),
|
||||||
None => String::from("{{#VOID}}"),
|
None => String::from("{{#EMPTY}}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,31 @@ use super::{Environment, Node, NodeEnum, Precedence, if_else::Bool};
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Equals {
|
pub struct Equals {
|
||||||
left: Rc<NodeEnum>,
|
left: Rc<NodeEnum>,
|
||||||
right: Rc<NodeEnum>,
|
right: Vec<Rc<NodeEnum>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Equals {
|
impl Node for Equals {
|
||||||
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||||
let left = self.left.evaluate(env)?;
|
let left = self.left.evaluate(env)?;
|
||||||
let right = self.right.evaluate(env)?;
|
|
||||||
|
|
||||||
match left == right {
|
for expr in &self.right {
|
||||||
true => Ok(Rc::new(Bool::True.into())),
|
if left != expr.evaluate(env)? {
|
||||||
false => Ok(Rc::new(Bool::False.into())),
|
return Ok(Rc::new(Bool::False.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Ok(Rc::new(Bool::True.into()));
|
||||||
|
}
|
||||||
|
|
||||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
format!(
|
format!(
|
||||||
"({}={})",
|
"{} = {}",
|
||||||
self.left.as_string(env),
|
self.left.as_string(env),
|
||||||
self.right.as_string(env)
|
self.right
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|a, b| a + " = " + &b)
|
||||||
|
.unwrap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +39,7 @@ impl Node for Equals {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Equals {
|
impl Equals {
|
||||||
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
|
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Self {
|
||||||
Self { left, right }
|
Self { left, right }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
62
src/lib/node/exponent.rs
Normal file
62
src/lib/node/exponent.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use rug::{Float, ops::Pow};
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Exponent {
|
||||||
|
left: Rc<NodeEnum>,
|
||||||
|
right: Rc<NodeEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Exponent {
|
||||||
|
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
|
let evaluated_left = self.left.evaluate(env)?;
|
||||||
|
let evaluated_right = self.right.evaluate(env)?;
|
||||||
|
|
||||||
|
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||||
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => 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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
|
let left_string = if self.left.precedence() <= self.precedence() {
|
||||||
|
format!("({})", self.left.as_string(env))
|
||||||
|
} else {
|
||||||
|
self.left.as_string(env)
|
||||||
|
};
|
||||||
|
let right_string = if self.right.precedence() <= self.precedence() {
|
||||||
|
format!("({})", self.right.as_string(env))
|
||||||
|
} else {
|
||||||
|
self.right.as_string(env)
|
||||||
|
};
|
||||||
|
format!("{}^{}", left_string, right_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Exponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Exponent {
|
||||||
|
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_left(&self) -> &Rc<NodeEnum> {
|
||||||
|
&self.left
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_right(&self) -> &Rc<NodeEnum> {
|
||||||
|
&self.right
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,41 +2,53 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use crate::environment::Environment;
|
use crate::environment::Environment;
|
||||||
|
|
||||||
use super::{Node, NodeEnum, Precedence, symbol::Symbol};
|
use super::{symbol::Symbol, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
pub type NativeFunctionType =
|
||||||
|
fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub enum FunctionType {
|
pub enum FunctionType {
|
||||||
Native(
|
Native(&'static str, NativeFunctionType),
|
||||||
&'static str,
|
UserFunction(Rc<NodeEnum>, Vec<Symbol>),
|
||||||
fn(&Vec<Rc<NodeEnum>>) -> Result<Rc<NodeEnum>, String>,
|
|
||||||
),
|
|
||||||
UserFunction(Rc<NodeEnum>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
function: FunctionType,
|
function: FunctionType,
|
||||||
// TODO: Finish reasoning about whether or not this is
|
|
||||||
// the right way to implement functions
|
|
||||||
arguments: Vec<Symbol>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Function {
|
impl Node for Function {
|
||||||
fn evaluate(&self, _: &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(
|
||||||
|
// 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 {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
let args = self
|
|
||||||
.arguments
|
|
||||||
.iter()
|
|
||||||
.map(|x| Node::as_string(x, env))
|
|
||||||
.reduce(|acc, e| format!("{acc}, {e}"))
|
|
||||||
.unwrap_or("()".to_owned());
|
|
||||||
|
|
||||||
match &self.function {
|
match &self.function {
|
||||||
FunctionType::Native(name, _) => name.to_owned().to_owned(),
|
FunctionType::Native(name, _) => format!("(Native Function `{name}`)"),
|
||||||
FunctionType::UserFunction(body) => format!("({args} -> {})", body.as_string(env)),
|
FunctionType::UserFunction(body, args) => format!(
|
||||||
|
"([{}] -> {})",
|
||||||
|
args.iter()
|
||||||
|
.map(|x| x.as_string(env))
|
||||||
|
.reduce(|acc, e| format!("{acc}, {e}"))
|
||||||
|
.unwrap_or("[]".to_owned()),
|
||||||
|
body.as_string(env)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,21 +58,11 @@ impl Node for Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(t: FunctionType, args: Vec<Symbol>) -> Rc<NodeEnum> {
|
pub fn new(t: FunctionType) -> Rc<NodeEnum> {
|
||||||
Rc::new(
|
Rc::new(Self { function: t }.into())
|
||||||
Self {
|
|
||||||
function: t,
|
|
||||||
arguments: args,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_body(&self) -> &FunctionType {
|
pub fn get_body(&self) -> &FunctionType {
|
||||||
&self.function
|
&self.function
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_arguments(&self) -> &Vec<Symbol> {
|
|
||||||
&self.arguments
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::node::empty::Empty;
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence};
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
@ -28,8 +30,9 @@ impl Node for Bool {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub enum ElseBranchEnum {
|
pub enum ElseBranchEnum {
|
||||||
ElseIf(Rc<IfElse>),
|
ElseIf(Rc<NodeEnum>),
|
||||||
Block(Vec<Rc<NodeEnum>>),
|
Block(Vec<Rc<NodeEnum>>),
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
@ -56,16 +59,17 @@ impl Node for IfElse {
|
||||||
block: &Vec<Rc<NodeEnum>>,
|
block: &Vec<Rc<NodeEnum>>,
|
||||||
env: &mut Environment,
|
env: &mut Environment,
|
||||||
) -> Result<Rc<NodeEnum>, String> {
|
) -> Result<Rc<NodeEnum>, String> {
|
||||||
// TODO: Push new scope
|
env.push_stack()?;
|
||||||
if let Some((last, to_evaluate)) = block.split_last() {
|
let ret = if let Some((last, to_evaluate)) = block.split_last() {
|
||||||
for expr in to_evaluate {
|
for expr in to_evaluate {
|
||||||
expr.evaluate(env)?;
|
expr.evaluate(env)?;
|
||||||
}
|
}
|
||||||
last.evaluate(env)
|
last.evaluate(env)?
|
||||||
} else {
|
} else {
|
||||||
Err("Empty if statemenent true branch")?
|
Empty::EMPTY.clone()
|
||||||
}
|
};
|
||||||
// TODO: Pop scope
|
env.pop_stack()?;
|
||||||
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
match condition {
|
match condition {
|
||||||
|
@ -73,28 +77,32 @@ impl Node for IfElse {
|
||||||
Bool::False => match &self.else_branch {
|
Bool::False => match &self.else_branch {
|
||||||
ElseBranchEnum::ElseIf(if_else) => if_else.evaluate(env),
|
ElseBranchEnum::ElseIf(if_else) => if_else.evaluate(env),
|
||||||
ElseBranchEnum::Block(node_enums) => evaluate_block(node_enums, env),
|
ElseBranchEnum::Block(node_enums) => evaluate_block(node_enums, env),
|
||||||
|
ElseBranchEnum::None => Ok(Empty::EMPTY.clone()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
format!(
|
format!(
|
||||||
"if {} then\n{}\nelse\n{}",
|
"if {} then {} {}",
|
||||||
self.condition.as_string(env),
|
self.condition.as_string(env),
|
||||||
self.true_branch
|
self.true_branch
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.as_string(env))
|
.map(|x| x.as_string(env))
|
||||||
.reduce(|a, b| a + "\n" + &b)
|
.reduce(|a, b| a + " " + &b)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str(),
|
.as_str(),
|
||||||
match &self.else_branch {
|
match &self.else_branch {
|
||||||
ElseBranchEnum::ElseIf(if_else) => if_else.as_string(env),
|
ElseBranchEnum::ElseIf(if_else) => if_else.as_string(env),
|
||||||
ElseBranchEnum::Block(vec) =>
|
ElseBranchEnum::Block(vec) =>
|
||||||
vec.iter()
|
"else ".to_owned()
|
||||||
|
+ &vec
|
||||||
|
.iter()
|
||||||
.map(|x| x.as_string(env))
|
.map(|x| x.as_string(env))
|
||||||
.reduce(|a, b| a + "\n" + &b)
|
.reduce(|a, b| a + "\n" + &b)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
+ "\nend",
|
+ "end",
|
||||||
|
ElseBranchEnum::None => " end".to_owned(),
|
||||||
}
|
}
|
||||||
.as_str()
|
.as_str()
|
||||||
)
|
)
|
||||||
|
@ -104,3 +112,20 @@ impl Node for IfElse {
|
||||||
Precedence::Primary
|
Precedence::Primary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IfElse {
|
||||||
|
pub fn new(
|
||||||
|
condition: Rc<NodeEnum>,
|
||||||
|
true_branch: Vec<Rc<NodeEnum>>,
|
||||||
|
else_branch: ElseBranchEnum,
|
||||||
|
) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(
|
||||||
|
Self {
|
||||||
|
condition,
|
||||||
|
true_branch,
|
||||||
|
else_branch,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,52 +3,62 @@ use std::{fmt::Display, rc::Rc};
|
||||||
use add::Add;
|
use add::Add;
|
||||||
use assign::Assign;
|
use assign::Assign;
|
||||||
use call::Call;
|
use call::Call;
|
||||||
|
use comparison::{Greater, GreaterEquals, Less, LessEquals};
|
||||||
use constant::Constant;
|
use constant::Constant;
|
||||||
use divide::Divide;
|
use divide::Divide;
|
||||||
use empty::Empty;
|
use empty::Empty;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use equals::Equals;
|
||||||
|
use exponent::Exponent;
|
||||||
use function::Function;
|
use function::Function;
|
||||||
use if_else::{Bool, IfElse};
|
use if_else::{Bool, IfElse};
|
||||||
use multiply::Multiply;
|
use multiply::Multiply;
|
||||||
|
use set::Set;
|
||||||
use subtract::Subtract;
|
use subtract::Subtract;
|
||||||
use symbol::Symbol;
|
use symbol::Symbol;
|
||||||
use equals::Equals;
|
use string_node::StringNode;
|
||||||
use set::Set;
|
use closure::Closure;
|
||||||
|
|
||||||
use crate::environment::Environment;
|
use crate::environment::Environment;
|
||||||
|
|
||||||
pub mod add;
|
pub mod add;
|
||||||
|
pub mod assign;
|
||||||
|
pub mod call;
|
||||||
|
pub mod comparison;
|
||||||
pub mod constant;
|
pub mod constant;
|
||||||
pub mod divide;
|
pub mod divide;
|
||||||
|
pub mod empty;
|
||||||
|
pub mod equals;
|
||||||
|
pub mod exponent;
|
||||||
|
pub mod function;
|
||||||
|
pub mod if_else;
|
||||||
pub mod multiply;
|
pub mod multiply;
|
||||||
|
pub mod node_ref;
|
||||||
|
pub mod set;
|
||||||
pub mod subtract;
|
pub mod subtract;
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
pub mod node_ref;
|
pub mod string_node;
|
||||||
pub mod assign;
|
pub mod closure;
|
||||||
pub mod empty;
|
|
||||||
pub mod function;
|
|
||||||
pub mod call;
|
|
||||||
pub mod if_else;
|
|
||||||
pub mod equals;
|
|
||||||
pub mod set;
|
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum NodeEnum {
|
pub enum NodeEnum {
|
||||||
Constant,
|
Constant,
|
||||||
|
StringNode,
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
Add,
|
Add,
|
||||||
Subtract,
|
Subtract,
|
||||||
Multiply,
|
Multiply,
|
||||||
Divide,
|
Divide,
|
||||||
|
Exponent,
|
||||||
|
|
||||||
Symbol,
|
Symbol,
|
||||||
// NodeRef, // DEPRECATED, use Symbol
|
|
||||||
Assign,
|
Assign,
|
||||||
Empty,
|
Empty,
|
||||||
Function,
|
Function,
|
||||||
|
Closure, // IMPLEMENT THIS SO CURRYING WORKS
|
||||||
Call,
|
Call,
|
||||||
|
|
||||||
Bool,
|
Bool,
|
||||||
|
@ -57,11 +67,10 @@ pub enum NodeEnum {
|
||||||
Set,
|
Set,
|
||||||
|
|
||||||
Equals,
|
Equals,
|
||||||
// Greater,
|
Greater,
|
||||||
// GreaterEquals,
|
GreaterEquals,
|
||||||
// Less,
|
Less,
|
||||||
// LessEquals
|
LessEquals,
|
||||||
|
|
||||||
// Logical operators
|
// Logical operators
|
||||||
// In,
|
// In,
|
||||||
// Where,
|
// Where,
|
||||||
|
@ -79,6 +88,7 @@ pub enum Precedence {
|
||||||
Term,
|
Term,
|
||||||
Factor,
|
Factor,
|
||||||
Unary,
|
Unary,
|
||||||
|
Exponent,
|
||||||
Call,
|
Call,
|
||||||
Primary,
|
Primary,
|
||||||
}
|
}
|
||||||
|
@ -95,3 +105,83 @@ impl Display for NodeEnum {
|
||||||
write!(f, "{}", Node::as_string(self, None))
|
write!(f, "{}", Node::as_string(self, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for NodeEnum {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
match (self, other) {
|
||||||
|
(NodeEnum::Constant(constant1), NodeEnum::Constant(constant2)) => {
|
||||||
|
constant1.partial_cmp(constant2)
|
||||||
|
}
|
||||||
|
|
||||||
|
(NodeEnum::Add(add1), NodeEnum::Add(add2)) => add1.partial_cmp(add2),
|
||||||
|
(NodeEnum::Subtract(subtract1), NodeEnum::Subtract(subtract2)) => {
|
||||||
|
subtract1.partial_cmp(subtract2)
|
||||||
|
}
|
||||||
|
(NodeEnum::Multiply(multiply1), NodeEnum::Multiply(multiply2)) => {
|
||||||
|
multiply1.partial_cmp(multiply2)
|
||||||
|
}
|
||||||
|
(NodeEnum::Divide(divide1), NodeEnum::Divide(divide2)) => divide1.partial_cmp(divide2),
|
||||||
|
(NodeEnum::Exponent(exponent1), NodeEnum::Exponent(exponent2)) => {
|
||||||
|
exponent1.partial_cmp(exponent2)
|
||||||
|
}
|
||||||
|
|
||||||
|
(NodeEnum::Symbol(symbol1), NodeEnum::Symbol(symbol2)) => symbol1.partial_cmp(symbol2),
|
||||||
|
(NodeEnum::Assign(assign1), NodeEnum::Assign(assign2)) => assign1.partial_cmp(assign2),
|
||||||
|
(NodeEnum::Empty(empty1), NodeEnum::Empty(empty2)) => empty1.partial_cmp(empty2),
|
||||||
|
(NodeEnum::Function(function1), NodeEnum::Function(function2)) => {
|
||||||
|
function1.partial_cmp(function2)
|
||||||
|
}
|
||||||
|
(NodeEnum::Call(call1), NodeEnum::Call(call2)) => call1.partial_cmp(call2),
|
||||||
|
|
||||||
|
(NodeEnum::Bool(bool1), NodeEnum::Bool(bool2)) => bool1.partial_cmp(bool2),
|
||||||
|
(NodeEnum::IfElse(if_else1), NodeEnum::IfElse(if_else2)) => {
|
||||||
|
if_else1.partial_cmp(if_else2)
|
||||||
|
}
|
||||||
|
|
||||||
|
(NodeEnum::Set(set1), NodeEnum::Set(set2)) => set1.partial_cmp(set2),
|
||||||
|
|
||||||
|
(NodeEnum::Equals(equals1), NodeEnum::Equals(equals2)) => equals1.partial_cmp(equals2),
|
||||||
|
(NodeEnum::Greater(greater1), NodeEnum::Greater(greater2)) => {
|
||||||
|
greater1.partial_cmp(greater2)
|
||||||
|
}
|
||||||
|
(
|
||||||
|
NodeEnum::GreaterEquals(greater_equals1),
|
||||||
|
NodeEnum::GreaterEquals(greater_equals2),
|
||||||
|
) => greater_equals1.partial_cmp(greater_equals2),
|
||||||
|
(NodeEnum::Less(less1), NodeEnum::Less(less2)) => less1.partial_cmp(less2),
|
||||||
|
(NodeEnum::LessEquals(less_equals1), NodeEnum::LessEquals(less_equals2)) => {
|
||||||
|
less_equals1.partial_cmp(less_equals2)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use rug::Float;
|
||||||
|
|
||||||
use crate::environment::Environment;
|
use crate::environment::Environment;
|
||||||
|
|
||||||
use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set};
|
use super::{
|
||||||
|
Node, NodeEnum, Precedence, add::Add, constant::Constant, divide::Divide, set::Set,
|
||||||
|
subtract::Subtract,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Multiply {
|
pub struct Multiply {
|
||||||
|
@ -18,24 +23,70 @@ 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() == 0. =>
|
if zero.get_value() == &0.0 =>
|
||||||
{
|
{
|
||||||
Ok(Rc::new(Constant::new(0.).into()))
|
Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant + Constant = Constant
|
// Identity rule
|
||||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
(NodeEnum::Constant(one), _) if one.get_value() == &0.0 => Ok(evaluated_right),
|
||||||
Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into()))
|
(_, NodeEnum::Constant(one)) if one.get_value() == &0.0 => Ok(evaluated_left),
|
||||||
}
|
|
||||||
|
|
||||||
// Symbol + Constant we just return the same
|
// Multiply into parenthesis (add)
|
||||||
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
|
(_, NodeEnum::Add(add)) => Ok(Add::new_rc(
|
||||||
Multiply::new(evaluated_right, evaluated_left).into(),
|
Self::new_rc(evaluated_left.clone(), add.get_left()).evaluate(env)?,
|
||||||
|
Self::new_rc(evaluated_left, add.get_right()).evaluate(env)?,
|
||||||
|
)),
|
||||||
|
(NodeEnum::Add(add), _) => Ok(Add::new_rc(
|
||||||
|
Self::new_rc(evaluated_right.clone(), add.get_left()).evaluate(env)?,
|
||||||
|
Self::new_rc(evaluated_right, add.get_right()).evaluate(env)?,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// Constant + Symbol we switch them around so the constant is last
|
// Multiply into parenthesis (sub)
|
||||||
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
|
(_, NodeEnum::Subtract(sub)) => Ok(Subtract::new_rc(
|
||||||
Multiply::new(evaluated_left, evaluated_right).into(),
|
Self::new_rc(evaluated_left.clone(), sub.get_left()).evaluate(env)?,
|
||||||
|
Self::new_rc(evaluated_left, sub.get_right()).evaluate(env)?,
|
||||||
|
)),
|
||||||
|
(NodeEnum::Subtract(sub), _) => Ok(Subtract::new_rc(
|
||||||
|
Self::new_rc(evaluated_right.clone(), sub.get_left()).evaluate(env)?,
|
||||||
|
Self::new_rc(evaluated_right, sub.get_right()).evaluate(env)?,
|
||||||
|
)),
|
||||||
|
|
||||||
|
// Multiply fraction
|
||||||
|
(NodeEnum::Divide(div), _) => Ok(Divide::new_rc(
|
||||||
|
Self::new_rc(evaluated_right, div.get_left()).evaluate(env)?,
|
||||||
|
div.get_right(),
|
||||||
|
)),
|
||||||
|
|
||||||
|
(_, NodeEnum::Divide(div)) => Ok(Divide::new_rc(
|
||||||
|
Self::new_rc(evaluated_left, div.get_left()).evaluate(env)?,
|
||||||
|
div.get_right(),
|
||||||
|
)),
|
||||||
|
|
||||||
|
// 0.5*n -> n/2
|
||||||
|
(NodeEnum::Constant(c), _) if c.get_value() == &0.5 => Divide::new(
|
||||||
|
evaluated_right,
|
||||||
|
Rc::new(Constant::new_from_float(2, &env).into()),
|
||||||
|
)
|
||||||
|
.evaluate(env),
|
||||||
|
(_, NodeEnum::Constant(c)) if c.get_value() == &0.5 => Divide::new(
|
||||||
|
evaluated_left,
|
||||||
|
Rc::new(Constant::new_from_float(2, &env).into()),
|
||||||
|
)
|
||||||
|
.evaluate(env),
|
||||||
|
|
||||||
|
// Constant * Constant = Constant
|
||||||
|
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||||
|
Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64(
|
||||||
|
env.get_float_precision(),
|
||||||
|
a.get_value() * b.get_value(),
|
||||||
|
)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move constant infront of symbols
|
||||||
|
(NodeEnum::Symbol(_), NodeEnum::Constant(_))
|
||||||
|
| (NodeEnum::Exponent(_), NodeEnum::Constant(_)) => Ok(Rc::new(
|
||||||
|
Multiply::new(evaluated_right, evaluated_left).into(),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// Multiply a set with a constant
|
// Multiply a set with a constant
|
||||||
|
@ -67,10 +118,10 @@ impl Node for Multiply {
|
||||||
Ok(Set::new(values))
|
Ok(Set::new(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
(NodeEnum::Constant(c), NodeEnum::Multiply(m))
|
// (NodeEnum::Constant(c), NodeEnum::Multiply(m))
|
||||||
| (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => {
|
// | (NodeEnum::Multiply(m), NodeEnum::Constant(c)) => {
|
||||||
Self::collapse_nested_multiply(c, m, env)?.evaluate(env)
|
// Self::collapse_nested_multiply(c, m, env)?.evaluate(env)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
|
// (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
|
||||||
// Self::move_constants_to_left(evaluated_left.clone(), evaluated_right.clone())
|
// Self::move_constants_to_left(evaluated_left.clone(), evaluated_right.clone())
|
||||||
|
@ -110,14 +161,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(),
|
||||||
))
|
))
|
||||||
|
@ -143,6 +202,7 @@ impl Multiply {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn move_constants_to_left(
|
fn move_constants_to_left(
|
||||||
left: Rc<NodeEnum>,
|
left: Rc<NodeEnum>,
|
||||||
right: Rc<NodeEnum>,
|
right: Rc<NodeEnum>,
|
||||||
|
@ -152,7 +212,7 @@ impl Multiply {
|
||||||
(NodeEnum::Constant(_), NodeEnum::Multiply(_))
|
(NodeEnum::Constant(_), NodeEnum::Multiply(_))
|
||||||
| (NodeEnum::Symbol(_), NodeEnum::Symbol(_)) => Ok(Self::new_rc(left, right)),
|
| (NodeEnum::Symbol(_), NodeEnum::Symbol(_)) => Ok(Self::new_rc(left, right)),
|
||||||
|
|
||||||
(NodeEnum::Multiply(m), NodeEnum::Constant(c)) => Ok(Self::new_rc(
|
(NodeEnum::Multiply(m), NodeEnum::Constant(_c)) => Ok(Self::new_rc(
|
||||||
right.clone(),
|
right.clone(),
|
||||||
Self::move_constants_to_left(m.left.clone(), m.right.clone())?,
|
Self::move_constants_to_left(m.left.clone(), m.right.clone())?,
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -20,12 +20,12 @@ 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))
|
||||||
.reduce(|a, b| a + ", " + &b)
|
.reduce(|a, b| a + ", " + &b)
|
||||||
.unwrap()
|
.unwrap_or("".to_owned())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
38
src/lib/node/string_node.rs
Normal file
38
src/lib/node/string_node.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct StringNode {
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for StringNode {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for StringNode {
|
||||||
|
fn evaluate(&self, _: &mut super::Environment) -> Result<Rc<super::NodeEnum>, String> {
|
||||||
|
Ok(Rc::new(self.clone().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string(&self, _env: Option<&Environment>) -> String {
|
||||||
|
format!("\"{}\"", self.value.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> Precedence {
|
||||||
|
Precedence::Primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringNode {
|
||||||
|
pub fn new(value: String) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { value }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> &String {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
use rug::Float;
|
||||||
|
|
||||||
|
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant, multiply::Multiply};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Subtract {
|
pub struct Subtract {
|
||||||
|
@ -15,23 +17,80 @@ 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() == 0. => Ok(evaluated_right),
|
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right),
|
||||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => 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(_)) => {
|
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
|
||||||
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
|
||||||
|
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
|
||||||
|
Subtract::new(evaluated_right, evaluated_left).into(),
|
||||||
|
)),
|
||||||
|
|
||||||
|
(NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => {
|
||||||
|
match (
|
||||||
|
(m1.get_left().as_ref(), m1.get_right().as_ref()),
|
||||||
|
(m2.get_left().as_ref(), m2.get_right().as_ref()),
|
||||||
|
) {
|
||||||
|
((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2))
|
||||||
|
| ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2))
|
||||||
|
| ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2)))
|
||||||
|
| ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => {
|
||||||
|
if s1 == s2 {
|
||||||
|
Multiply::new_rc(
|
||||||
|
Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone()))
|
||||||
|
.evaluate(env)?,
|
||||||
|
Rc::new(s1.clone().into()),
|
||||||
|
)
|
||||||
|
.evaluate(env)
|
||||||
|
} else {
|
||||||
|
Ok(Rc::new(
|
||||||
|
Subtract::new(evaluated_left, evaluated_right).into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
// 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()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
((NodeEnum::Exponent(e1), o1), (NodeEnum::Exponent(e2), o2))
|
||||||
|
| ((o1, NodeEnum::Exponent(e1)), (NodeEnum::Exponent(e2), o2))
|
||||||
|
| ((NodeEnum::Exponent(e1), o1), (o2, NodeEnum::Exponent(e2)))
|
||||||
|
| ((o1, NodeEnum::Exponent(e1)), (o2, NodeEnum::Exponent(e2))) => {
|
||||||
|
if e1 == e2 {
|
||||||
|
Multiply::new_rc(
|
||||||
|
Subtract::new(Rc::new(o1.clone()), Rc::new(o2.clone()))
|
||||||
|
.evaluate(env)?,
|
||||||
|
Rc::new(e1.clone().into()),
|
||||||
|
)
|
||||||
|
.evaluate(env)
|
||||||
|
} else {
|
||||||
|
Ok(Rc::new(
|
||||||
|
Subtract::new(evaluated_left, evaluated_right).into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Ok(Rc::new(
|
||||||
|
Subtract::new(evaluated_left, evaluated_right).into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
Ok(Rc::new(Subtract::new(evaluated_left, evaluated_right).into()))
|
if evaluated_left == evaluated_right {
|
||||||
|
Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
|
||||||
|
} else {
|
||||||
|
Ok(Rc::new(
|
||||||
|
Subtract::new(evaluated_left, evaluated_right).into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,10 +116,11 @@ impl Node for Subtract {
|
||||||
|
|
||||||
impl Subtract {
|
impl Subtract {
|
||||||
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
|
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
|
||||||
Self {
|
Self { left, right }
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
||||||
|
Rc::new(Self { left, right }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_left(&self) -> Rc<NodeEnum> {
|
pub fn get_left(&self) -> Rc<NodeEnum> {
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
use std::{
|
use std::rc::Rc;
|
||||||
collections::HashMap,
|
|
||||||
rc::Rc,
|
|
||||||
sync::{LazyLock, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence, node_ref::NodeRef};
|
use super::{Environment, Node, NodeEnum, Precedence};
|
||||||
use crate::environment::EnvironmentInternalSymbolKey;
|
use crate::environment::EnvironmentInternalSymbolKey;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
value: EnvironmentInternalSymbolKey,
|
value: EnvironmentInternalSymbolKey,
|
||||||
}
|
}
|
||||||
|
@ -25,15 +21,15 @@ impl Node for Symbol {
|
||||||
|
|
||||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||||
if let Some(env) = env {
|
if let Some(env) = env {
|
||||||
if let Some(value) = env.get(&self.value) {
|
if let Some(str) = env.id_to_str(&self.value) {
|
||||||
value.as_string(Some(env))
|
str.clone()
|
||||||
} else {
|
} else {
|
||||||
env.id_to_str(&self.value)
|
env.id_to_str(&self.value)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(format!("{{#SYMBOL {}}}", self.value))
|
.unwrap_or(format!("{{#SYMBOL {:?}}}", self.value))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{{#SYMBOL {}}}", self.value)
|
format!("{{#SYMBOL {:?}}}", self.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,3 +58,9 @@ impl Symbol {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Symbol {
|
||||||
|
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
use std::{collections::HashMap, iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter};
|
use std::{
|
||||||
|
iter::Peekable,
|
||||||
|
rc::Rc,
|
||||||
|
str::{Chars, SplitTerminator},
|
||||||
|
vec::IntoIter,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rug::Float;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
environment::Environment,
|
environment::Environment,
|
||||||
|
@ -7,12 +14,16 @@ use crate::{
|
||||||
add::Add,
|
add::Add,
|
||||||
assign::Assign,
|
assign::Assign,
|
||||||
call::Call,
|
call::Call,
|
||||||
constant::{Constant, ConstantValue},
|
comparison::{Greater, GreaterEquals, Less, LessEquals},
|
||||||
|
constant::Constant,
|
||||||
divide::Divide,
|
divide::Divide,
|
||||||
equals::Equals,
|
equals::Equals,
|
||||||
|
exponent::Exponent,
|
||||||
function::{Function, FunctionType},
|
function::{Function, FunctionType},
|
||||||
|
if_else::{Bool, ElseBranchEnum, IfElse},
|
||||||
multiply::Multiply,
|
multiply::Multiply,
|
||||||
set::Set,
|
set::Set,
|
||||||
|
string_node::StringNode,
|
||||||
subtract::Subtract,
|
subtract::Subtract,
|
||||||
symbol::Symbol,
|
symbol::Symbol,
|
||||||
},
|
},
|
||||||
|
@ -24,52 +35,80 @@ 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),
|
||||||
|
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
Star,
|
Star,
|
||||||
Slash,
|
Slash,
|
||||||
|
Hat,
|
||||||
|
|
||||||
Equals,
|
Equals,
|
||||||
|
Greater,
|
||||||
|
Less,
|
||||||
|
GreaterEquals,
|
||||||
|
LessEquals,
|
||||||
|
|
||||||
ColonEquals,
|
ColonEquals,
|
||||||
LeftArrow,
|
LeftArrow,
|
||||||
|
|
||||||
RParen,
|
RParen,
|
||||||
LParen,
|
LParen,
|
||||||
|
|
||||||
|
RSquare,
|
||||||
|
LSquare,
|
||||||
Comma,
|
Comma,
|
||||||
|
|
||||||
If,
|
If,
|
||||||
Then,
|
Then,
|
||||||
Else,
|
Else,
|
||||||
End,
|
End,
|
||||||
|
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
|
||||||
|
Terminator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenType {
|
impl TokenType {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
TokenType::Number(n) => n.to_string().len(),
|
TokenType::Number(n) => n.to_string().len(),
|
||||||
TokenType::Identifier(s) => s.len(),
|
TokenType::Identifier(s) | TokenType::String(s) => s.len(),
|
||||||
|
|
||||||
TokenType::Plus => 1,
|
TokenType::Plus => 1,
|
||||||
TokenType::Minus => 1,
|
TokenType::Minus => 1,
|
||||||
TokenType::Star => 1,
|
TokenType::Star => 1,
|
||||||
TokenType::Slash => 1,
|
TokenType::Slash => 1,
|
||||||
|
TokenType::Hat => 1,
|
||||||
|
|
||||||
TokenType::Equals => 1,
|
TokenType::Equals => 1,
|
||||||
|
TokenType::Greater => 1,
|
||||||
|
TokenType::Less => 1,
|
||||||
|
TokenType::GreaterEquals => 2,
|
||||||
|
TokenType::LessEquals => 2,
|
||||||
|
|
||||||
TokenType::ColonEquals => 2,
|
TokenType::ColonEquals => 2,
|
||||||
TokenType::LeftArrow => 2,
|
TokenType::LeftArrow => 2,
|
||||||
|
|
||||||
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,
|
||||||
TokenType::Then => 4,
|
TokenType::Then => 4,
|
||||||
TokenType::Else => 4,
|
TokenType::Else => 4,
|
||||||
TokenType::End => 3,
|
TokenType::End => 3,
|
||||||
|
|
||||||
|
TokenType::True => 4,
|
||||||
|
TokenType::False => 5,
|
||||||
|
|
||||||
|
Self::Terminator => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,26 +152,26 @@ impl<'a> Lexer<'a> {
|
||||||
// Numbers with decimal points
|
// Numbers with decimal points
|
||||||
'0'..='9' | '.' => {
|
'0'..='9' | '.' => {
|
||||||
let mut digit = String::from(c);
|
let mut digit = String::from(c);
|
||||||
|
let mut has_decimal = c == '.';
|
||||||
loop {
|
loop {
|
||||||
let d = self.source.peek();
|
let d = self.source.peek();
|
||||||
let mut has_decimal = c == '.';
|
|
||||||
match d {
|
match d {
|
||||||
Some('0'..='9') => {
|
Some('0'..='9') => {
|
||||||
digit.push(*d.unwrap());
|
digit.push(*d.unwrap());
|
||||||
self.source.next();
|
self.source.next();
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
#[allow(unused_assignments)] // For some reason it thinks has_decimal
|
|
||||||
// is never read
|
|
||||||
Some('.') => {
|
Some('.') => {
|
||||||
if has_decimal {
|
if has_decimal {
|
||||||
return Err(LexerError::UnexpectedChar(
|
return Err(LexerError::UnexpectedChar(
|
||||||
i,
|
i + 1,
|
||||||
"Invalid digit with multiple decimal points".into(),
|
"Invalid digit with multiple decimal points".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
digit.push(*d.unwrap());
|
digit.push(*d.unwrap());
|
||||||
|
self.source.next();
|
||||||
|
i += 1;
|
||||||
has_decimal = true;
|
has_decimal = true;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -140,9 +179,40 @@ impl<'a> Lexer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let number = digit.parse::<ConstantValue>().unwrap();
|
// 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();
|
||||||
|
|
||||||
|
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 (->)
|
// LeftArrow (->)
|
||||||
|
@ -152,12 +222,26 @@ impl<'a> Lexer<'a> {
|
||||||
tokens.push(Token(i, TokenType::LeftArrow));
|
tokens.push(Token(i, TokenType::LeftArrow));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'<' if self.source.peek() == Some(&'=') => {
|
||||||
|
self.source.next();
|
||||||
|
i += 1;
|
||||||
|
tokens.push(Token(i, TokenType::LessEquals));
|
||||||
|
}
|
||||||
|
'>' if self.source.peek() == Some(&'=') => {
|
||||||
|
self.source.next();
|
||||||
|
i += 1;
|
||||||
|
tokens.push(Token(i, TokenType::GreaterEquals));
|
||||||
|
}
|
||||||
|
'<' => tokens.push(Token(i, TokenType::Less)),
|
||||||
|
'>' => tokens.push(Token(i, TokenType::Greater)),
|
||||||
|
|
||||||
'+' => tokens.push(Token(i, TokenType::Plus)),
|
'+' => tokens.push(Token(i, TokenType::Plus)),
|
||||||
'-' => tokens.push(Token(i, TokenType::Minus)),
|
'-' => tokens.push(Token(i, TokenType::Minus)),
|
||||||
'*' => tokens.push(Token(i, TokenType::Star)),
|
'*' => tokens.push(Token(i, TokenType::Star)),
|
||||||
'/' => tokens.push(Token(i, TokenType::Slash)),
|
'/' => tokens.push(Token(i, TokenType::Slash)),
|
||||||
'=' => tokens.push(Token(i, TokenType::Equals)),
|
'=' => tokens.push(Token(i, TokenType::Equals)),
|
||||||
',' => tokens.push(Token(i, TokenType::Comma)),
|
',' => tokens.push(Token(i, TokenType::Comma)),
|
||||||
|
'^' => tokens.push(Token(i, TokenType::Hat)),
|
||||||
|
|
||||||
':' if self.source.peek() == Some(&'=') => {
|
':' if self.source.peek() == Some(&'=') => {
|
||||||
self.source.next();
|
self.source.next();
|
||||||
|
@ -165,9 +249,14 @@ impl<'a> Lexer<'a> {
|
||||||
tokens.push(Token(i, TokenType::ColonEquals));
|
tokens.push(Token(i, TokenType::ColonEquals));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
';' => tokens.push(Token(i, TokenType::Terminator)),
|
||||||
|
|
||||||
'(' => 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)?);
|
||||||
}
|
}
|
||||||
|
@ -205,27 +294,33 @@ impl<'a> Lexer<'a> {
|
||||||
"then" => TokenType::Then,
|
"then" => TokenType::Then,
|
||||||
"else" => TokenType::Else,
|
"else" => TokenType::Else,
|
||||||
"end" => TokenType::End,
|
"end" => TokenType::End,
|
||||||
|
|
||||||
|
"true" => TokenType::True,
|
||||||
|
"false" => TokenType::False,
|
||||||
|
|
||||||
_ => TokenType::Identifier(identifier),
|
_ => TokenType::Identifier(identifier),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
UnexpectedEndOfTokens(String),
|
UnexpectedEndOfTokens(String),
|
||||||
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
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
tokens: Peekable<IntoIter<Token>>,
|
tokens: Peekable<IntoIter<Token>>,
|
||||||
environment: &'a mut Environment,
|
environment: &'a mut Environment,
|
||||||
previous: Option<&'a Token>,
|
previous: Option<Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tokens<'a> = Peekable<Iter<'a, Token>>;
|
// type Tokens<'a> = Peekable<Iter<'a, Token>>;
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub fn new(tokens: Vec<Token>, env: &'a mut Environment) -> Self {
|
pub fn new(tokens: Vec<Token>, env: &'a mut Environment) -> Self {
|
||||||
|
@ -242,27 +337,53 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse(&mut self) -> Result<Vec<Rc<NodeEnum>>, ParserError> {
|
pub fn parse(&mut self) -> Result<Vec<Rc<NodeEnum>>, ParserError> {
|
||||||
let mut expressions = vec![];
|
let mut expressions = vec![];
|
||||||
|
|
||||||
while self.tokens.peek().is_some() {
|
if self.tokens.len() == 0 {
|
||||||
|
return Ok(expressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
expressions.push(self.expression()?);
|
expressions.push(self.expression()?);
|
||||||
|
|
||||||
|
if !self.is_at_end() {
|
||||||
|
self.match_or_err(TokenType::Terminator)?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expressions)
|
Ok(expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matchType<'b>(&'b mut self, t: TokenType) -> bool {
|
fn consume<'b>(&'b mut self) -> &'b Option<Token> {
|
||||||
if let Some(Token(_, token_type)) = self.tokens.peek() {
|
self.previous = self.tokens.next();
|
||||||
if *token_type == t {
|
&self.previous
|
||||||
self.tokens.next();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_at_end(&mut self) -> bool {
|
||||||
|
if self.tokens.peek().is_none() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the next token is `t`, if it is then consume it and return true. Otherwise does
|
||||||
|
/// nothing and returns false.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matchOrErr(&mut self, t: TokenType) -> Result<bool, ParserError> {
|
fn match_type<'b>(&'b mut self, t: TokenType) -> bool {
|
||||||
|
if let Some(Token(_, token_type)) = self.tokens.peek() {
|
||||||
|
if *token_type == t {
|
||||||
|
self.consume();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn match_or_err(&mut self, t: TokenType) -> Result<bool, ParserError> {
|
||||||
let (i, tt) = if let Some(Token(i, tt)) = self.tokens.peek() {
|
let (i, tt) = if let Some(Token(i, tt)) = self.tokens.peek() {
|
||||||
(*i, tt.clone())
|
(*i, tt.clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -271,7 +392,7 @@ impl<'a> Parser<'a> {
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.matchType(t.clone()) {
|
if self.match_type(t.clone()) {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Err(ParserError::UnexpectedToken(
|
Err(ParserError::UnexpectedToken(
|
||||||
|
@ -287,36 +408,75 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignment(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn assignment(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
let expr = self.equality();
|
let expr = self.equality()?;
|
||||||
|
|
||||||
if let Some(Token(_, TokenType::ColonEquals)) = self.tokens.peek() {
|
if self.match_type(TokenType::ColonEquals) {
|
||||||
self.tokens.next();
|
return Ok(Rc::new(Assign::new(expr, self.equality()?).into()));
|
||||||
return Ok(Rc::new(Assign::new(expr?, self.equality()?).into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expr
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn equality(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn equality(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
// TODO: Implement equality
|
// TODO: Implement equality
|
||||||
let expr = self.comparison();
|
let expr = self.comparison()?;
|
||||||
if self.matchType(TokenType::Equals) {
|
if self.match_type(TokenType::Equals) {
|
||||||
return Ok(Rc::new(Equals::new(expr?, self.equality()?).into()));
|
let mut expressions = vec![];
|
||||||
|
loop {
|
||||||
|
expressions.push(self.comparison()?);
|
||||||
|
if !self.match_type(TokenType::Equals) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
expr
|
}
|
||||||
|
return Ok(Rc::new(Equals::new(expr, expressions).into()));
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comparison(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn comparison(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
// TODO: Implement comparison
|
// TODO: Implement comparison
|
||||||
self.term()
|
let expr = self.term()?;
|
||||||
|
|
||||||
|
let t = if let Some(Token(_i, t)) = self.tokens.peek() {
|
||||||
|
t.clone()
|
||||||
|
} else {
|
||||||
|
return Ok(expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
if match t {
|
||||||
|
TokenType::Greater => true,
|
||||||
|
TokenType::Less => true,
|
||||||
|
TokenType::GreaterEquals => true,
|
||||||
|
TokenType::LessEquals => true,
|
||||||
|
_ => false,
|
||||||
|
} {
|
||||||
|
self.consume();
|
||||||
|
let mut expressions = vec![];
|
||||||
|
loop {
|
||||||
|
expressions.push(self.term()?);
|
||||||
|
if !self.match_type(t.clone()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match t {
|
||||||
|
TokenType::Greater => Ok(Greater::new(expr, expressions)),
|
||||||
|
TokenType::Less => Ok(Less::new(expr, expressions)),
|
||||||
|
TokenType::GreaterEquals => Ok(GreaterEquals::new(expr, expressions)),
|
||||||
|
TokenType::LessEquals => Ok(LessEquals::new(expr, expressions)),
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn term(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn term(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
let expr = self.factor()?;
|
let expr = self.factor()?;
|
||||||
if self.matchType(TokenType::Plus) {
|
if self.match_type(TokenType::Plus) {
|
||||||
Ok(Rc::new(Add::new(expr, self.comparison()?).into()))
|
Ok(Rc::new(Add::new(expr, self.comparison()?).into()))
|
||||||
} else if let Some(Token(_, TokenType::Minus)) = self.tokens.peek() {
|
} else if let Some(Token(_, TokenType::Minus)) = self.tokens.peek() {
|
||||||
self.tokens.next();
|
self.consume();
|
||||||
Ok(Rc::new(Subtract::new(expr, self.comparison()?).into()))
|
Ok(Rc::new(Subtract::new(expr, self.comparison()?).into()))
|
||||||
} else {
|
} else {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
|
@ -324,16 +484,31 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn factor(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn factor(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
let expr = self.unary()?;
|
let mut expr = self.unary()?;
|
||||||
if let Some(Token(_, TokenType::Star)) = self.tokens.peek() {
|
// if let Some(Token(_, TokenType::Star)) = self.tokens.peek() {
|
||||||
self.tokens.next();
|
// self.consume();
|
||||||
Ok(Rc::new(Multiply::new(expr, self.comparison()?).into()))
|
// Ok(Rc::new(Multiply::new(expr, self.comparison()?).into()))
|
||||||
} else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() {
|
// } else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() {
|
||||||
self.tokens.next();
|
// self.consume();
|
||||||
Ok(Rc::new(Divide::new(expr, self.comparison()?).into()))
|
// Ok(Rc::new(Divide::new(expr, self.comparison()?).into()))
|
||||||
} else {
|
// } else {
|
||||||
Ok(expr)
|
// Ok(expr)
|
||||||
|
// }
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if self.match_type(TokenType::Star) {
|
||||||
|
let right = self.unary()?;
|
||||||
|
expr = Multiply::new_rc(expr, right);
|
||||||
|
continue;
|
||||||
|
} else if self.match_type(TokenType::Slash) {
|
||||||
|
let right = self.unary()?;
|
||||||
|
expr = Divide::new_rc(expr, right);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn unary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
|
@ -341,38 +516,106 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exponent(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn exponent(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
self.call()
|
let expr = self.call();
|
||||||
|
|
||||||
|
if self.match_type(TokenType::Hat) {
|
||||||
|
let right = self.unary()?;
|
||||||
|
return Ok(Exponent::new(expr?, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Calls can have 0 arguments, so check and return early
|
||||||
|
if self.match_type(TokenType::RParen) {
|
||||||
|
expr = Call::new(expr, vec![]);
|
||||||
} else {
|
} else {
|
||||||
// return Err(ParserError::UnexpectedNode(
|
// Parse expressions until a patching Right-Parenthesis is found
|
||||||
// i,
|
let mut parameters = vec![self.equality()?];
|
||||||
// format!("Expected a Set here, but got a {potential_parameters:?}"),
|
|
||||||
// ));
|
while self.match_type(TokenType::Comma) {
|
||||||
Set::new(vec![potential_parameters])
|
parameters.push(self.equality()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.match_type(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.match_type(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()
|
||||||
|
),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
expr = Ok(Call::new(expr?, parameters));
|
// 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> {
|
||||||
|
@ -382,21 +625,23 @@ impl<'a> Parser<'a> {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = self.primary()?;
|
let expr = self.if_else()?;
|
||||||
|
|
||||||
if self.matchType(TokenType::LeftArrow) {
|
if self.match_type(TokenType::LeftArrow) {
|
||||||
let right = self.equality()?;
|
let right = self.equality()?;
|
||||||
match expr.clone().as_ref() {
|
match expr.clone().as_ref() {
|
||||||
NodeEnum::Symbol(symbol) => {
|
NodeEnum::Symbol(symbol) => {
|
||||||
return Ok(Function::new(
|
return Ok(Function::new(FunctionType::UserFunction(
|
||||||
FunctionType::UserFunction(right),
|
right,
|
||||||
vec![symbol.clone()],
|
vec![symbol.clone()],
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
NodeEnum::Set(set) => {
|
NodeEnum::Set(set) => {
|
||||||
let mut symbols = vec![];
|
let mut symbols = vec![];
|
||||||
for (i, value) in set.get_values().into_iter().enumerate() {
|
for (i, value) in set.get_values().into_iter().enumerate() {
|
||||||
match value.as_ref() {
|
match value.as_ref() {
|
||||||
|
NodeEnum::Symbol(symbol) => symbols.push(symbol.clone()),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::UnexpectedNode(
|
return Err(ParserError::UnexpectedNode(
|
||||||
error_loc,
|
error_loc,
|
||||||
|
@ -405,12 +650,10 @@ impl<'a> Parser<'a> {
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeEnum::Symbol(symbol) => symbols.push(symbol.clone()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Function::new(FunctionType::UserFunction(right), symbols));
|
return Ok(Function::new(FunctionType::UserFunction(right, symbols)));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::UnexpectedNode(
|
return Err(ParserError::UnexpectedNode(
|
||||||
|
@ -424,40 +667,65 @@ impl<'a> Parser<'a> {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
fn if_else(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
let (i, token) = if let Some(Token(i, token)) = self.tokens.next() {
|
if self.match_type(TokenType::If) {
|
||||||
(i, token)
|
let condition = self.equality()?;
|
||||||
} else {
|
|
||||||
return Err(ParserError::UnexpectedEndOfTokens(
|
|
||||||
"Expected a Primary here".into(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
match token {
|
let _ = self.match_or_err(TokenType::Then)?;
|
||||||
TokenType::Number(value) => Ok(Rc::new(Constant::new(value).into())),
|
|
||||||
TokenType::Identifier(string) => Ok(Rc::new(
|
|
||||||
Symbol::new_from_str(string, self.environment).into(),
|
|
||||||
)),
|
|
||||||
TokenType::LParen => {
|
|
||||||
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 {
|
let mut expressions = vec![];
|
||||||
TokenType::RParen => {
|
|
||||||
self.tokens.next();
|
while !(self.match_type(TokenType::End) || self.match_type(TokenType::Else)) {
|
||||||
Ok(expr)
|
if self.is_at_end() {
|
||||||
|
return Err(ParserError::UnexpectedEndOfTokens(
|
||||||
|
"Expected an else or end here".to_owned(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
TokenType::Comma => {
|
expressions.push(self.expression()?);
|
||||||
let mut values = vec![expr];
|
}
|
||||||
|
|
||||||
|
// Safe to unwrap since the while loop would terminate if previous was none (it didnt
|
||||||
|
// find an End or Else before running out of tokens)
|
||||||
|
let else_branch = match self.previous.as_ref().unwrap() {
|
||||||
|
Token(_, TokenType::End) => ElseBranchEnum::None,
|
||||||
|
Token(_, TokenType::Else) => {
|
||||||
|
if let Some(Token(_, TokenType::If)) = self.tokens.peek() {
|
||||||
|
ElseBranchEnum::ElseIf(self.if_else()?)
|
||||||
|
} else {
|
||||||
|
let mut expressions = vec![];
|
||||||
|
while !self.match_type(TokenType::End) {
|
||||||
|
if self.is_at_end() {
|
||||||
|
return Err(ParserError::UnexpectedEndOfTokens(
|
||||||
|
"Expected an end here".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
expressions.push(self.expression()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElseBranchEnum::Block(expressions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => panic!("Not possible"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(IfElse::new(condition, expressions, else_branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
|
if self.match_type(TokenType::LSquare) {
|
||||||
|
// Empty set
|
||||||
|
if self.match_type(TokenType::RSquare) {
|
||||||
|
return Ok(Set::new(vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut values = vec![self.equality()?];
|
||||||
while {
|
while {
|
||||||
if let Some(Token(_, TokenType::RParen)) = self.tokens.peek() {
|
if let Some(Token(_, TokenType::RSquare)) = self.tokens.peek() {
|
||||||
self.tokens.next();
|
self.consume();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -472,7 +740,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if *token == TokenType::Comma {
|
if *token == TokenType::Comma {
|
||||||
self.tokens.next();
|
self.consume();
|
||||||
} else {
|
} else {
|
||||||
return Err(ParserError::UnexpectedToken(
|
return Err(ParserError::UnexpectedToken(
|
||||||
*i,
|
*i,
|
||||||
|
@ -484,26 +752,85 @@ impl<'a> Parser<'a> {
|
||||||
values.push(self.equality()?);
|
values.push(self.equality()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Set::new(values))
|
return Ok(Set::new(values));
|
||||||
}
|
}
|
||||||
_ => Err(ParserError::Unimplemented(
|
self.primary()
|
||||||
*i,
|
|
||||||
t.len(),
|
|
||||||
format!("Expected either a comma or a right parenthesis here. Got {t:?}"),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if t != TokenType::RParen {
|
fn primary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||||
// Err(ParserError::UnexpectedToken(i, t.len(), format!("")))
|
let (i, token) = if let Some(Token(i, token)) = self.tokens.next() {
|
||||||
// } else {
|
(i, token)
|
||||||
// Ok(expr)
|
} else {
|
||||||
// }
|
return Err(ParserError::UnexpectedEndOfTokens(
|
||||||
|
"Expected a Primary here".into(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr = match token {
|
||||||
|
TokenType::Number(value) => {
|
||||||
|
let value = if let Ok(incomplete) = Float::parse(&value) {
|
||||||
|
Float::with_val_64(self.environment.get_float_precision(), incomplete)
|
||||||
|
} 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(),
|
||||||
|
)),
|
||||||
|
TokenType::True => Ok(Rc::new(Bool::True.into())),
|
||||||
|
TokenType::False => Ok(Rc::new(Bool::False.into())),
|
||||||
|
|
||||||
|
TokenType::String(s) => Ok(StringNode::new(s)),
|
||||||
|
|
||||||
|
TokenType::LParen => {
|
||||||
|
let expr = self.expression()?;
|
||||||
|
|
||||||
|
if !self.match_type(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(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
_ => Err(ParserError::UnexpectedToken(
|
_ => Err(ParserError::UnexpectedToken(
|
||||||
i,
|
i,
|
||||||
token.len(),
|
token.len(),
|
||||||
format!("Unexpected token {token:?}"),
|
format!("Unexpected token {token:?}"),
|
||||||
)),
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implicit multiplication
|
||||||
|
if !self.is_at_end() {
|
||||||
|
match self.tokens.peek().unwrap() {
|
||||||
|
Token(_, TokenType::Identifier(_)) | Token(_, TokenType::Number(_)) => {
|
||||||
|
return Ok(Multiply::new_rc(expr?, self.primary()?));
|
||||||
}
|
}
|
||||||
|
Token(_, TokenType::LParen) if expr.is_ok() => {
|
||||||
|
if let NodeEnum::Symbol(_) = expr.as_ref().unwrap().as_ref() {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Multiply::new_rc(expr?, self.primary()?));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,133 +1,135 @@
|
||||||
mod arithmetic {
|
// TODO: Test case this: `x*0*5*y*z`
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crate::{environment, node::{
|
// mod arithmetic {
|
||||||
add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
|
// use std::rc::Rc;
|
||||||
}};
|
//
|
||||||
use environment::Environment;
|
// use crate::{environment, node::{
|
||||||
|
// add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
|
||||||
use test_case::test_case;
|
// }};
|
||||||
|
// use environment::Environment;
|
||||||
#[test_case(69.0, 420.0, 489.0 ; "when both are positive")]
|
//
|
||||||
#[test_case(-2.0, -4.0, -6.0 ; "when both are negative")]
|
// use test_case::test_case;
|
||||||
#[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
|
//
|
||||||
#[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
|
// #[test_case(69.0, 420.0, 489.0 ; "when both are positive")]
|
||||||
// #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
// #[test_case(-2.0, -4.0, -6.0 ; "when both are negative")]
|
||||||
fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
|
||||||
let mut env = Environment::new();
|
// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
|
||||||
|
// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
||||||
let a = Rc::new(Constant::new(a).into());
|
// fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
||||||
let b = Rc::new(Constant::new(b).into());
|
// let mut env = Environment::new();
|
||||||
let d: NodeEnum = Add::new(a, b).into();
|
//
|
||||||
|
// let a = Rc::new(Constant::new(a).into());
|
||||||
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
// let b = Rc::new(Constant::new(b).into());
|
||||||
|
// let d: NodeEnum = Add::new(a, b).into();
|
||||||
assert!(
|
//
|
||||||
value == Constant::new(e).into(),
|
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
"Expected {} got {}",
|
//
|
||||||
e,
|
// assert!(
|
||||||
value
|
// value == Constant::new(e).into(),
|
||||||
)
|
// "Expected {} got {}",
|
||||||
}
|
// e,
|
||||||
|
// value
|
||||||
#[test_case(69.0, 420.0, -351.0 ; "when both are positive")]
|
// )
|
||||||
#[test_case(-2.0, -4.0, 2.0 ; "when both are negative")]
|
// }
|
||||||
#[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
|
//
|
||||||
#[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
|
// #[test_case(69.0, 420.0, -351.0 ; "when both are positive")]
|
||||||
// #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
// #[test_case(-2.0, -4.0, 2.0 ; "when both are negative")]
|
||||||
fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) {
|
// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
|
||||||
let mut env = Environment::new();
|
// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
|
||||||
|
// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
||||||
let a = Rc::new(Constant::new(0.0).into());
|
// fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) {
|
||||||
let b = Rc::new(Constant::new(aa).into());
|
// let mut env = Environment::new();
|
||||||
let c = Rc::new(Constant::new(bb).into());
|
//
|
||||||
let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into();
|
// let a = Rc::new(Constant::new(0.0).into());
|
||||||
|
// let b = Rc::new(Constant::new(aa).into());
|
||||||
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
// let c = Rc::new(Constant::new(bb).into());
|
||||||
|
// let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into();
|
||||||
assert!(
|
//
|
||||||
value == Constant::new(e).into(),
|
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
"Expected {} got {}",
|
//
|
||||||
e,
|
// assert!(
|
||||||
value
|
// value == Constant::new(e).into(),
|
||||||
)
|
// "Expected {} got {}",
|
||||||
}
|
// e,
|
||||||
|
// value
|
||||||
#[test_case(5.0, 10.0, 50.0 ; "when both are positive")]
|
// )
|
||||||
#[test_case(-5.0, 10.0, -50.0 ; "when left is negative")]
|
// }
|
||||||
#[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
|
//
|
||||||
#[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
|
// #[test_case(5.0, 10.0, 50.0 ; "when both are positive")]
|
||||||
#[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
|
// #[test_case(-5.0, 10.0, -50.0 ; "when left is negative")]
|
||||||
fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
// #[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
|
||||||
let mut env = Environment::new();
|
// #[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
|
||||||
|
// #[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
|
||||||
let a = Rc::new(Constant::new(a).into());
|
// fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
||||||
let b = Rc::new(Constant::new(b).into());
|
// let mut env = Environment::new();
|
||||||
let d: NodeEnum = Multiply::new(a, b).into();
|
//
|
||||||
|
// let a = Rc::new(Constant::new(a).into());
|
||||||
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
// let b = Rc::new(Constant::new(b).into());
|
||||||
|
// let d: NodeEnum = Multiply::new(a, b).into();
|
||||||
assert!(
|
//
|
||||||
value == Constant::new(e).into(),
|
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
"Expected {} got {}",
|
//
|
||||||
e,
|
// assert!(
|
||||||
value
|
// value == Constant::new(e).into(),
|
||||||
)
|
// "Expected {} got {}",
|
||||||
}
|
// e,
|
||||||
|
// value
|
||||||
#[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
|
// )
|
||||||
#[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
|
// }
|
||||||
#[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
|
//
|
||||||
#[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
|
// #[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
|
||||||
fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
// #[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
|
||||||
let mut env = Environment::new();
|
// #[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
|
||||||
|
// #[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
|
||||||
let a = Rc::new(Constant::new(a).into());
|
// fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
|
||||||
let b = Rc::new(Constant::new(b).into());
|
// let mut env = Environment::new();
|
||||||
let d: NodeEnum = Divide::new(a, b).into();
|
//
|
||||||
|
// let a = Rc::new(Constant::new(a).into());
|
||||||
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
// let b = Rc::new(Constant::new(b).into());
|
||||||
|
// let d: NodeEnum = Divide::new(a, b).into();
|
||||||
assert!(
|
//
|
||||||
value == Constant::new(e).into(),
|
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
|
||||||
"Expected {} got {}",
|
//
|
||||||
e,
|
// assert!(
|
||||||
value
|
// value == Constant::new(e).into(),
|
||||||
)
|
// "Expected {} got {}",
|
||||||
}
|
// e,
|
||||||
}
|
// value
|
||||||
|
// )
|
||||||
mod functions {
|
// }
|
||||||
#[test]
|
// }
|
||||||
fn stringification() {
|
//
|
||||||
// let f = Function
|
// mod functions {
|
||||||
}
|
// #[test]
|
||||||
}
|
// fn stringification() {
|
||||||
|
// // let f = Function
|
||||||
mod expected_errors {
|
// }
|
||||||
use test_case::test_case;
|
// }
|
||||||
|
//
|
||||||
use crate::node::constant::ConstantValue;
|
// mod expected_errors {
|
||||||
|
// use test_case::test_case;
|
||||||
#[test_case(5.0, 0.0 ; "divide by zero")]
|
//
|
||||||
fn division(a: ConstantValue, b: ConstantValue) {
|
// use crate::node::constant::ConstantValue;
|
||||||
let _ = a+b;
|
//
|
||||||
}
|
// #[test_case(5.0, 0.0 ; "divide by zero")]
|
||||||
}
|
// fn division(a: ConstantValue, b: ConstantValue) {
|
||||||
|
// let _ = a+b;
|
||||||
mod misc {
|
// }
|
||||||
use test_case::test_case;
|
// }
|
||||||
|
//
|
||||||
use crate::node::constant::ConstantValue;
|
// mod misc {
|
||||||
|
// use test_case::test_case;
|
||||||
#[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
|
//
|
||||||
fn convert_to_string(
|
// use crate::node::constant::ConstantValue;
|
||||||
a: impl Into<ConstantValue>,
|
//
|
||||||
op1: char,
|
// #[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
|
||||||
b: impl Into<ConstantValue>,
|
// fn convert_to_string(
|
||||||
op2: char,
|
// a: impl Into<ConstantValue>,
|
||||||
c: impl Into<ConstantValue>,
|
// op1: char,
|
||||||
e: &'static str,
|
// b: impl Into<ConstantValue>,
|
||||||
) {
|
// op2: char,
|
||||||
}
|
// c: impl Into<ConstantValue>,
|
||||||
}
|
// e: &'static str,
|
||||||
|
// ) {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
Loading…
Reference in a new issue