cleaned up some code
This commit is contained in:
parent
04af1e291c
commit
2c38fb391b
154
Cargo.lock
generated
154
Cargo.lock
generated
|
@ -8,18 +8,6 @@ version = "0.3.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
|
@ -41,6 +29,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.69.5"
|
||||
|
@ -50,7 +44,7 @@ dependencies = [
|
|||
"bitflags 2.8.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
|
@ -375,25 +369,26 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "gmp-mpfr-sys"
|
||||
version = "1.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0205cd82059bc63b63cf516d714352a30c44f2c74da9961dfda2617ae6b5918"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
|
||||
dependencies = [
|
||||
"ahash 0.3.8",
|
||||
"ahash",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hibitset"
|
||||
version = "0.6.4"
|
||||
|
@ -409,15 +404,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -494,51 +480,6 @@ version = "0.4.25"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
|
||||
[[package]]
|
||||
name = "malachite"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aa282d5c3914eba1e50fd81012e04cfd5a79a08c29ece0fa20ef73afcbd7804"
|
||||
dependencies = [
|
||||
"malachite-base",
|
||||
"malachite-nz",
|
||||
"malachite-q",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malachite-base"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef157484a1d7246fbff1a4fd2c148b83ac7324161963af61001ea43dc9086076"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"itertools 0.11.0",
|
||||
"libm",
|
||||
"ryu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malachite-nz"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de4451b2e0e0ec6e4f39ee311b72154d291b185f5450474ace5663e37d7f8360"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"libm",
|
||||
"malachite-base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malachite-q"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fa9bd8eccda7ff0959d48e00e03ee3626817414442725b2cc1f1b2f215ceee9"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"malachite-base",
|
||||
"malachite-nz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
|
@ -592,8 +533,8 @@ dependencies = [
|
|||
"clay-layout",
|
||||
"enum_dispatch",
|
||||
"font-kit",
|
||||
"malachite",
|
||||
"raylib",
|
||||
"rug",
|
||||
"termion",
|
||||
"test-case",
|
||||
]
|
||||
|
@ -762,6 +703,18 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rug"
|
||||
version = "1.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3"
|
||||
dependencies = [
|
||||
"az",
|
||||
"gmp-mpfr-sys",
|
||||
"libc",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
@ -790,12 +743,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
|
@ -830,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"hashbrown 0.7.2",
|
||||
"hashbrown",
|
||||
"mopa",
|
||||
"smallvec",
|
||||
"tynm",
|
||||
|
@ -855,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240"
|
||||
dependencies = [
|
||||
"crossbeam-queue",
|
||||
"hashbrown 0.7.2",
|
||||
"hashbrown",
|
||||
"hibitset",
|
||||
"log",
|
||||
"shred",
|
||||
|
@ -982,12 +929,6 @@ version = "1.0.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
|
@ -1056,6 +997,15 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
@ -1205,23 +1155,3 @@ dependencies = [
|
|||
"once_cell",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
|
|
@ -8,8 +8,8 @@ edition = "2024"
|
|||
clay-layout = { path = "../clay-ui-rust" }
|
||||
enum_dispatch = "0.3.13"
|
||||
font-kit = "0.14.2"
|
||||
malachite = "0.5.0"
|
||||
raylib = { version = "5.0.2", features = ["wayland"] }
|
||||
rug = "1.27.0"
|
||||
termion = "4.0.3"
|
||||
test-case = "3.3.1"
|
||||
|
||||
|
|
210
src/cli-repl.rs
210
src/cli-repl.rs
|
@ -1,14 +1,16 @@
|
|||
use std::f32::consts;
|
||||
use std::f64;
|
||||
use std::io::{self, StdoutLock, Write, stdout};
|
||||
use std::rc::Rc;
|
||||
|
||||
use libopenbirch::environment::Environment;
|
||||
use libopenbirch::node::call::Call;
|
||||
use libopenbirch::node::closure::Closure;
|
||||
use libopenbirch::node::constant::Constant;
|
||||
use libopenbirch::node::empty::Empty;
|
||||
use libopenbirch::node::set::Set;
|
||||
use libopenbirch::node::{Node, NodeEnum};
|
||||
use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError};
|
||||
use malachite::base::num::basic::traits::Zero;
|
||||
use malachite::rational::Rational;
|
||||
#[cfg(feature = "async")]
|
||||
use termion::AsyncReader;
|
||||
use termion::color;
|
||||
|
@ -23,6 +25,9 @@ pub struct Input {
|
|||
stdout: RawTerminal<StdoutLock<'static>>,
|
||||
buffer: String,
|
||||
current_char: usize,
|
||||
|
||||
history: Vec<String>,
|
||||
history_idx: Option<usize>,
|
||||
}
|
||||
impl Input {
|
||||
pub fn new() -> Self {
|
||||
|
@ -35,6 +40,9 @@ impl Input {
|
|||
stdout,
|
||||
buffer: "".into(),
|
||||
current_char: 0,
|
||||
|
||||
history: vec![],
|
||||
history_idx: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,9 +76,11 @@ impl Input {
|
|||
return Err(io::Error::from(io::ErrorKind::Interrupted));
|
||||
}
|
||||
|
||||
self.history.insert(0, self.buffer.clone());
|
||||
let r = self.buffer.clone();
|
||||
self.buffer.clear();
|
||||
self.current_char = 0;
|
||||
self.history_idx = None;
|
||||
print!("\n\r");
|
||||
return Ok(Some(r));
|
||||
}
|
||||
|
@ -107,6 +117,45 @@ impl Input {
|
|||
print!("{}", termion::cursor::Right(1));
|
||||
let _ = self.stdout.flush();
|
||||
}
|
||||
Key::Up => {
|
||||
match self.history_idx {
|
||||
Some(i) => self.history_idx = Some((i + 1) % self.history.len()),
|
||||
None => {
|
||||
if self.history.len() > 0 {
|
||||
self.history_idx = Some(0)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.buffer =
|
||||
self.history.get(self.history_idx.unwrap()).unwrap().clone();
|
||||
self.current_char = self.buffer.len();
|
||||
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
||||
}
|
||||
Key::Down => {
|
||||
match self.history_idx {
|
||||
Some(i) => {
|
||||
let i = if i == 0 { self.history.len() } else { i };
|
||||
self.history_idx = Some((i - 1) % self.history.len())
|
||||
}
|
||||
None => {
|
||||
if self.history.len() > 0 {
|
||||
self.history_idx = Some(self.history.len() - 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.buffer =
|
||||
self.history.get(self.history_idx.unwrap()).unwrap().clone();
|
||||
self.current_char = self.buffer.len();
|
||||
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
||||
}
|
||||
Key::Home => self.current_char = 0,
|
||||
Key::End => self.current_char = self.buffer.len(),
|
||||
Key::Char(char) => {
|
||||
self.buffer.insert(self.current_char, char);
|
||||
self.current_char += 1;
|
||||
|
@ -169,23 +218,168 @@ fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>
|
|||
env.undefine(*id);
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Cannot undefine the expression {arg}"));
|
||||
return Err(format!(
|
||||
"Expected strings, but one of the arguments was a {}",
|
||||
arg.type_str()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Empty::new(format!("Undefined {} symbols", args.len())))
|
||||
}
|
||||
|
||||
fn get_float_precision(
|
||||
args: &Vec<Rc<NodeEnum>>,
|
||||
env: &mut Environment,
|
||||
) -> Result<Rc<NodeEnum>, String> {
|
||||
match args.len() {
|
||||
0 => Ok(Rc::new(
|
||||
Constant::new_from_float(env.get_float_precision() as f64, &env).into(),
|
||||
)),
|
||||
1 => {
|
||||
let arg = args.first().unwrap();
|
||||
if let NodeEnum::Constant(constant) = arg.as_ref() {
|
||||
let prec = constant.get_value().prec_64() as f64;
|
||||
|
||||
Ok(Rc::new(Constant::new_from_float(prec, &env).into()))
|
||||
} else {
|
||||
Err(format!(
|
||||
"Expected argument of type Constant, but got {}",
|
||||
arg.type_str()
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(format!("Expected 0 or 1 arguments, got {}", args.len()))?,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_float_precision(
|
||||
args: &Vec<Rc<NodeEnum>>,
|
||||
env: &mut Environment,
|
||||
) -> Result<Rc<NodeEnum>, String> {
|
||||
if args.len() != 1 {
|
||||
Err(format!("Expected 1 arguments, got {}", args.len()))?
|
||||
}
|
||||
|
||||
let arg = args.first().unwrap();
|
||||
|
||||
let precision: u64 = if let NodeEnum::Constant(value) = arg.as_ref() {
|
||||
value.get_value().to_u32_saturating().unwrap().into()
|
||||
} else {
|
||||
Err(format!(
|
||||
"Expected argument of type Constant, but got {}",
|
||||
arg.type_str()
|
||||
))?
|
||||
};
|
||||
|
||||
env.set_float_precision(precision);
|
||||
|
||||
Ok(Empty::new(format!("Set float precision to {precision}")))
|
||||
}
|
||||
|
||||
fn map(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||
if args.len() != 2 {
|
||||
Err(format!("Expected 2 argument but got {}", args.len()))?
|
||||
}
|
||||
|
||||
let arg = args.first().unwrap();
|
||||
let func = match arg.as_ref() {
|
||||
NodeEnum::Function(_) | NodeEnum::Closure(_) => arg,
|
||||
_ => Err(format!(
|
||||
"Argument 1 expected a Function but got {}",
|
||||
arg.type_str()
|
||||
))?,
|
||||
};
|
||||
|
||||
let arg = args.get(1).unwrap().as_ref();
|
||||
let set = if let NodeEnum::Set(set) = arg {
|
||||
set.get_values()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Argument 1 expected a Set but got {}",
|
||||
arg.type_str()
|
||||
))?;
|
||||
};
|
||||
|
||||
let mut out = Vec::with_capacity(set.len());
|
||||
|
||||
for expr in set.iter() {
|
||||
out.push(Call::new(func.clone(), vec![expr.clone()]).evaluate(env)?);
|
||||
}
|
||||
|
||||
Ok(Set::new(out))
|
||||
}
|
||||
|
||||
fn get(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||
if args.len() != 2 {
|
||||
Err(format!("Expected 2 argument but got {}", args.len()))?
|
||||
}
|
||||
|
||||
let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() {
|
||||
set.get_values()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Argument 1 expected Set but got {}",
|
||||
args.first().unwrap().type_str()
|
||||
));
|
||||
};
|
||||
|
||||
let idx = if let NodeEnum::Constant(c) = args.get(1).unwrap().as_ref() {
|
||||
if let Some(u) = c.get_value().to_u32_saturating() {
|
||||
u as usize
|
||||
} else {
|
||||
return Err(
|
||||
"Error occured while trying to convert second argument to integer".to_owned(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Err("Argument 1 is expected to be a Set".to_owned());
|
||||
};
|
||||
|
||||
if let Some(v) = set.get(idx) {
|
||||
return Ok(v.clone());
|
||||
}
|
||||
|
||||
return Err(format!(
|
||||
"Index was out of bounds for Set of length {}",
|
||||
set.len()
|
||||
));
|
||||
}
|
||||
|
||||
fn length(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||
if args.len() != 1 {
|
||||
Err(format!("Expected 1 argument but got {}", args.len()))?
|
||||
}
|
||||
|
||||
let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() {
|
||||
set.get_values()
|
||||
} else {
|
||||
return Err("Argument 1 is expected to be a Set".to_owned());
|
||||
};
|
||||
|
||||
Ok(Rc::new(
|
||||
Constant::new_from_float(set.len() as f32, &env).into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn main() -> Result<(), io::Error> {
|
||||
let mut input = Input::new();
|
||||
|
||||
let mut env = Environment::new();
|
||||
|
||||
env.set_float_precision(128);
|
||||
|
||||
env.define_native_function("print", print);
|
||||
env.define_native_function("unset", unset);
|
||||
env.define_native_function("get_float_precision", get_float_precision);
|
||||
env.define_native_function("set_float_precision", set_float_precision);
|
||||
env.define_native_function("map", map);
|
||||
env.define_native_function("get", get);
|
||||
env.define_native_function("length", length);
|
||||
|
||||
env.define(
|
||||
"pi",
|
||||
Rc::new(Constant::new(f64::consts::PI.try_into().unwrap()).into()),
|
||||
Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()),
|
||||
);
|
||||
|
||||
while let Some(source) = input.get()? {
|
||||
|
@ -196,9 +390,7 @@ fn main() -> Result<(), io::Error> {
|
|||
|
||||
if tokens_result.is_err() {
|
||||
match tokens_result.err().unwrap() {
|
||||
LexerError::UnexpectedChar(i, exp) | LexerError::NumberParse(i, exp) => {
|
||||
print_err(i, 1, exp)
|
||||
}
|
||||
LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -213,7 +405,9 @@ fn main() -> Result<(), io::Error> {
|
|||
ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp),
|
||||
ParserError::UnexpectedToken(i, len, exp)
|
||||
| ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp),
|
||||
ParserError::UnexpectedNode(i, exp) => print_err(i, 1, exp),
|
||||
ParserError::UnexpectedNode(i, exp) | ParserError::NumberParse(i, exp) => {
|
||||
print_err(i, 1, exp)
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ pub struct Environment {
|
|||
stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
|
||||
|
||||
hidden: Vec<EnvironmentInternalSymbolKey>,
|
||||
disable_calls: bool,
|
||||
enable_closures: bool,
|
||||
|
||||
float_precision: u64,
|
||||
|
||||
// stack: Vec<Vec<EnvironmentInternalSymbolKey>>,
|
||||
symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>,
|
||||
|
@ -42,7 +44,9 @@ impl Environment {
|
|||
stack: vec![],
|
||||
stack_shadows: vec![],
|
||||
hidden: vec![],
|
||||
disable_calls: false,
|
||||
enable_closures: false,
|
||||
|
||||
float_precision: 64,
|
||||
|
||||
symbol_to_id: HashMap::new(),
|
||||
id_to_symbol: HashMap::new(),
|
||||
|
@ -66,16 +70,24 @@ impl Environment {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn disable_calls(&mut self) {
|
||||
self.disable_calls = true;
|
||||
pub fn get_float_precision(&self) -> u64 {
|
||||
self.float_precision
|
||||
}
|
||||
|
||||
pub fn enable_calls(&mut self) {
|
||||
self.disable_calls = false;
|
||||
pub fn set_float_precision(&mut self, precision: u64) {
|
||||
self.float_precision = precision;
|
||||
}
|
||||
|
||||
pub fn calls_disabled(&self) -> bool {
|
||||
self.disable_calls
|
||||
pub fn disable_closures(&mut self) {
|
||||
self.enable_closures = true;
|
||||
}
|
||||
|
||||
pub fn enable_closures(&mut self) {
|
||||
self.enable_closures = false;
|
||||
}
|
||||
|
||||
pub fn closures_enabled(&self) -> bool {
|
||||
self.enable_closures
|
||||
}
|
||||
|
||||
pub fn push_stack(&mut self) -> Result<(), String> {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{base::num::basic::traits::Zero, rational::Rational};
|
||||
use rug::Float;
|
||||
|
||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant, set::Set};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct Add {
|
||||
|
@ -17,20 +17,33 @@ impl Node for Add {
|
|||
|
||||
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||
// Zero rule
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
|
||||
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => Ok(evaluated_left),
|
||||
|
||||
// Constant + Constant = Constant
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into()))
|
||||
}
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new(
|
||||
Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
a.get_value() + b.get_value(),
|
||||
))
|
||||
.into(),
|
||||
)),
|
||||
|
||||
// Move constants after symbols in add
|
||||
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
|
||||
Ok(Rc::new(Add::new(evaluated_right, evaluated_left).into()))
|
||||
}
|
||||
|
||||
// Concatenate sets
|
||||
(NodeEnum::Set(s1), NodeEnum::Set(s2)) => {
|
||||
let values = {
|
||||
let mut v = s1.get_values().clone();
|
||||
v.append(&mut s2.get_values().clone());
|
||||
v
|
||||
};
|
||||
Ok(Set::new(values))
|
||||
}
|
||||
|
||||
_ => Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +1,57 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::{environment::Environment, node::function::FunctionType};
|
||||
use crate::{
|
||||
environment::Environment,
|
||||
node::{closure::Closure, function::FunctionType},
|
||||
};
|
||||
|
||||
use super::{Node, NodeEnum, Precedence};
|
||||
use super::{Node, NodeEnum, Precedence, closure};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Call {
|
||||
function: Rc<NodeEnum>,
|
||||
arguments: Rc<NodeEnum>,
|
||||
arguments: Vec<Rc<NodeEnum>>,
|
||||
}
|
||||
|
||||
impl Node for Call {
|
||||
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
|
||||
if env.calls_disabled() {
|
||||
if env.closures_enabled() {
|
||||
return Ok(Rc::new(self.clone().into()));
|
||||
}
|
||||
|
||||
// Evaluate callee and error if its not a function
|
||||
let evaluated = self.function.evaluate(env)?;
|
||||
let func = if let NodeEnum::Function(func) = evaluated.as_ref() {
|
||||
func
|
||||
} else {
|
||||
// return Err(format!(
|
||||
// "Cannot call {} as a function",
|
||||
// evaluated.as_string(Some(env))
|
||||
// ));
|
||||
return Ok(Rc::new(self.clone().into()));
|
||||
// let func = if let NodeEnum::Function(func) = evaluated.as_ref() {
|
||||
// func
|
||||
// } else {
|
||||
// return Ok(Rc::new(self.clone().into()));
|
||||
// };
|
||||
|
||||
env.push_stack()?;
|
||||
|
||||
let func = match evaluated.as_ref() {
|
||||
NodeEnum::Function(func) => func,
|
||||
NodeEnum::Closure(closure) => {
|
||||
for (k, v) in closure.get_captured_variables() {
|
||||
env.insert(*k, v.clone());
|
||||
}
|
||||
closure.get_function()
|
||||
}
|
||||
_ => {
|
||||
return Ok(Rc::new(self.clone().into()));
|
||||
}
|
||||
};
|
||||
|
||||
let arguments = if let NodeEnum::Set(set) = self.arguments.as_ref() {
|
||||
let arguments = {
|
||||
let mut arguments = vec![];
|
||||
for value in set.get_values().iter() {
|
||||
for value in self.arguments.iter() {
|
||||
arguments.push(value.evaluate(env)?);
|
||||
}
|
||||
arguments
|
||||
} else {
|
||||
panic!(
|
||||
"This shoulnd not be possible, but call constructed with arguments of type that is not set"
|
||||
);
|
||||
};
|
||||
|
||||
// Call function body with arguments
|
||||
match func.get_body() {
|
||||
let ret = match func.get_body() {
|
||||
// Pass arguments to native function
|
||||
FunctionType::Native(_name, native_function) => native_function(&arguments, env),
|
||||
FunctionType::UserFunction(body, fargs) => {
|
||||
|
@ -52,21 +62,36 @@ impl Node for Call {
|
|||
arguments.len()
|
||||
));
|
||||
}
|
||||
env.push_stack()?;
|
||||
// Define variables
|
||||
fargs.iter().zip(arguments).for_each(|(symbol, value)| {
|
||||
fargs.iter().zip(&arguments).for_each(|(symbol, value)| {
|
||||
env.insert(symbol.get_value(), value.clone());
|
||||
});
|
||||
let ev = body.evaluate(env)?;
|
||||
env.pop_stack()?;
|
||||
|
||||
// Return evaluated return value for function
|
||||
Ok(ev)
|
||||
Ok(match ev.as_ref() {
|
||||
NodeEnum::Function(hof) => {
|
||||
let captures = fargs.iter().map(|a| a.get_value()).zip(arguments).collect();
|
||||
Closure::new(hof.clone(), captures)
|
||||
}
|
||||
_ => ev,
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
env.pop_stack()?;
|
||||
ret
|
||||
}
|
||||
|
||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||
let arguments = self.arguments.as_string(env);
|
||||
let arguments = format!(
|
||||
"{}",
|
||||
self.arguments
|
||||
.iter()
|
||||
.map(|x| x.as_string(env))
|
||||
.reduce(|a, b| a + ", " + &b)
|
||||
.unwrap_or("".to_owned())
|
||||
);
|
||||
format!("{}({})", self.function.as_string(env), arguments)
|
||||
}
|
||||
|
||||
|
@ -76,7 +101,7 @@ impl Node for Call {
|
|||
}
|
||||
|
||||
impl Call {
|
||||
pub fn new(function: Rc<NodeEnum>, arguments: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
||||
pub fn new(function: Rc<NodeEnum>, arguments: Vec<Rc<NodeEnum>>) -> Rc<NodeEnum> {
|
||||
Rc::new(
|
||||
Self {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{
|
||||
base::{num::basic::traits::Zero, rounding_modes::RoundingMode},
|
||||
rational::{Rational, conversion::primitive_float_from_rational::FloatConversionError},
|
||||
};
|
||||
use rug::Float;
|
||||
|
||||
use super::{Environment, Node, Precedence};
|
||||
|
||||
pub type ConstantValue = malachite::rational::Rational;
|
||||
pub type ConstantValue = Float;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct Constant {
|
||||
|
@ -26,29 +23,15 @@ impl Node for Constant {
|
|||
}
|
||||
|
||||
fn as_string(&self, _env: Option<&Environment>) -> String {
|
||||
if self.value == Rational::ZERO {
|
||||
return "0".to_owned();
|
||||
if self.value.is_zero() {
|
||||
"0".to_string();
|
||||
}
|
||||
|
||||
if self.value.approx_log() < 50. {
|
||||
let x: Result<f64, FloatConversionError> = self.value.clone().try_into();
|
||||
|
||||
match x {
|
||||
Ok(v) => {
|
||||
return v.to_string();
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((man, exp, _)) = self
|
||||
.value
|
||||
.sci_mantissa_and_exponent_round_ref::<f64>(RoundingMode::Nearest)
|
||||
{
|
||||
format!("{}*2^{}", man, exp)
|
||||
} else {
|
||||
self.value.to_string()
|
||||
}
|
||||
self.value
|
||||
.to_string_radix(10, Some(8))
|
||||
.trim_end_matches('0')
|
||||
.trim_end_matches('.')
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn precedence(&self) -> Precedence {
|
||||
|
@ -61,6 +44,12 @@ impl Constant {
|
|||
Self { value }
|
||||
}
|
||||
|
||||
pub fn new_from_float(value: impl Into<f64>, env: &Environment) -> Self {
|
||||
Self {
|
||||
value: Float::with_val_64(env.get_float_precision(), value.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> &ConstantValue {
|
||||
&self.value
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{base::num::basic::traits::Zero, rational::Rational};
|
||||
use rug::Float;
|
||||
|
||||
use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set};
|
||||
|
||||
|
@ -17,20 +17,28 @@ impl Node for Divide {
|
|||
|
||||
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||
// Error if dividing by zero
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => {
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => {
|
||||
Err("Division by Zero".into())
|
||||
}
|
||||
|
||||
(_, _) if evaluated_left == evaluated_right => Ok(Rc::new(Constant::new(Rational::ZERO).into())),
|
||||
(_, _) if evaluated_left == evaluated_right => {
|
||||
Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
|
||||
}
|
||||
|
||||
// Zero rule
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
|
||||
|
||||
// Constant + Constant = Constant
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Rc::new(Constant::new(a.get_value() / b.get_value()).into()))
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => {
|
||||
Ok(evaluated_left)
|
||||
}
|
||||
|
||||
// Constant / Constant = Constant
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new(
|
||||
Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
a.get_value() / b.get_value(),
|
||||
))
|
||||
.into(),
|
||||
)),
|
||||
|
||||
// Divide a set with a constant
|
||||
(NodeEnum::Set(s), NodeEnum::Constant(c)) => {
|
||||
let old_values = s.get_values();
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{Natural, rational::Rational, rational::arithmetic::pow};
|
||||
|
||||
use malachite::base::num::arithmetic::traits::Pow;
|
||||
|
||||
use malachite::rational::conversion::integer_from_rational;
|
||||
use rug::{Float, ops::Pow};
|
||||
|
||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
||||
|
||||
|
@ -20,15 +16,13 @@ impl Node for Exponent {
|
|||
let evaluated_right = self.right.evaluate(env)?;
|
||||
|
||||
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
let v = b.get_value();
|
||||
let exp: u64 = if let Ok(u) = v.try_into() {
|
||||
u
|
||||
} else {
|
||||
return Err("Exponent was too large".into());
|
||||
};
|
||||
Ok(Rc::new(Constant::new(a.get_value().pow(exp)).into()))
|
||||
}
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new(
|
||||
Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
a.get_value().pow(b.get_value()),
|
||||
))
|
||||
.into(),
|
||||
)),
|
||||
|
||||
_ => Ok(Self::new(evaluated_left, evaluated_right)),
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::rc::Rc;
|
|||
|
||||
use crate::environment::Environment;
|
||||
|
||||
use super::{Node, NodeEnum, Precedence, symbol::Symbol};
|
||||
use super::{closure::Closure, symbol::Symbol, Node, NodeEnum, Precedence};
|
||||
|
||||
pub type NativeFunctionType =
|
||||
fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>;
|
||||
|
@ -20,22 +20,22 @@ pub struct Function {
|
|||
|
||||
impl Node for Function {
|
||||
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(),
|
||||
))
|
||||
// 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 {
|
||||
|
|
|
@ -17,6 +17,7 @@ use set::Set;
|
|||
use subtract::Subtract;
|
||||
use symbol::Symbol;
|
||||
use string_node::StringNode;
|
||||
use closure::Closure;
|
||||
|
||||
use crate::environment::Environment;
|
||||
|
||||
|
@ -37,6 +38,7 @@ pub mod set;
|
|||
pub mod subtract;
|
||||
pub mod symbol;
|
||||
pub mod string_node;
|
||||
pub mod closure;
|
||||
|
||||
#[enum_dispatch]
|
||||
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
||||
|
@ -56,7 +58,7 @@ pub enum NodeEnum {
|
|||
Assign,
|
||||
Empty,
|
||||
Function,
|
||||
// Closure // IMPLEMENT THIS SO CURRYING WORKS
|
||||
Closure, // IMPLEMENT THIS SO CURRYING WORKS
|
||||
Call,
|
||||
|
||||
Bool,
|
||||
|
@ -155,3 +157,31 @@ impl PartialOrd for NodeEnum {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeEnum {
|
||||
pub fn type_str(&self) -> String {
|
||||
match self {
|
||||
NodeEnum::Constant(_) => "Constant",
|
||||
NodeEnum::StringNode(_) => "String",
|
||||
NodeEnum::Add(_) => "Add",
|
||||
NodeEnum::Subtract(_) => "Subtract",
|
||||
NodeEnum::Multiply(_) => "Multiply",
|
||||
NodeEnum::Divide(_) => "Divide",
|
||||
NodeEnum::Exponent(_) => "Exponent",
|
||||
NodeEnum::Symbol(_) => "Symbol",
|
||||
NodeEnum::Assign(_) => "Assign",
|
||||
NodeEnum::Empty(_) => "Empty",
|
||||
NodeEnum::Function(_) => "Function",
|
||||
NodeEnum::Closure(_) => "Closure",
|
||||
NodeEnum::Call(_) => "Call",
|
||||
NodeEnum::Bool(_) => "Bool",
|
||||
NodeEnum::IfElse(_) => "If",
|
||||
NodeEnum::Set(_) => "Set",
|
||||
NodeEnum::Equals(_) => "Equals",
|
||||
NodeEnum::Greater(_) => "Greater",
|
||||
NodeEnum::GreaterEquals(_) => "Greater Equals",
|
||||
NodeEnum::Less(_) => "Less",
|
||||
NodeEnum::LessEquals(_) => "Less Equals"
|
||||
}.to_owned()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{base::num::basic::traits::{One, Zero}, rational::Rational};
|
||||
use rug::Float;
|
||||
|
||||
use crate::environment::Environment;
|
||||
|
||||
|
@ -23,14 +23,14 @@ impl Node for Multiply {
|
|||
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||
// Zero rule
|
||||
(NodeEnum::Constant(zero), _) | (_, NodeEnum::Constant(zero))
|
||||
if zero.get_value() == &Rational::ZERO =>
|
||||
if zero.get_value() == &0.0 =>
|
||||
{
|
||||
Ok(Rc::new(Constant::new(Rational::ZERO).into()))
|
||||
Ok(Rc::new(Constant::new_from_float(0.0, &env).into()))
|
||||
}
|
||||
|
||||
// Identity rule
|
||||
(NodeEnum::Constant(one), _) if one.get_value() == &Rational::ONE => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(one)) if one.get_value() == &Rational::ONE => Ok(evaluated_left),
|
||||
(NodeEnum::Constant(one), _) if one.get_value() == &0.0 => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(one)) if one.get_value() == &0.0 => Ok(evaluated_left),
|
||||
|
||||
// Multiply into parenthesis (add)
|
||||
(_, NodeEnum::Add(add)) => Ok(Add::new_rc(
|
||||
|
@ -63,9 +63,12 @@ impl Node for Multiply {
|
|||
div.get_right(),
|
||||
)),
|
||||
|
||||
// Constant + Constant = Constant
|
||||
// Constant * Constant = Constant
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into()))
|
||||
Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
a.get_value() * b.get_value(),
|
||||
)))))
|
||||
}
|
||||
|
||||
// Move constant infront of symbols
|
||||
|
@ -146,14 +149,22 @@ impl Multiply {
|
|||
) -> Result<Rc<NodeEnum>, String> {
|
||||
match (multiply.left.as_ref(), multiply.right.as_ref()) {
|
||||
(NodeEnum::Constant(c2), _) => {
|
||||
let new_const = Constant::new(constant.get_value() * c2.get_value()).into();
|
||||
let new_const = Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
constant.get_value() * c2.get_value(),
|
||||
))
|
||||
.into();
|
||||
Ok(Rc::new(
|
||||
Multiply::new(Rc::new(new_const), multiply.right.clone()).into(),
|
||||
))
|
||||
}
|
||||
|
||||
(_, NodeEnum::Constant(c2)) => {
|
||||
let new_const = Constant::new(constant.get_value() * c2.get_value()).into();
|
||||
let new_const = Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
constant.get_value() * c2.get_value(),
|
||||
))
|
||||
.into();
|
||||
Ok(Rc::new(
|
||||
Multiply::new(Rc::new(new_const), multiply.right.clone()).into(),
|
||||
))
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Node for Set {
|
|||
|
||||
fn as_string(&self, env: Option<&Environment>) -> String {
|
||||
format!(
|
||||
"({})",
|
||||
"[{}]",
|
||||
self.values
|
||||
.iter()
|
||||
.map(|x| x.as_string(env))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use malachite::{base::num::basic::traits::Zero, rational::Rational};
|
||||
use rug::Float;
|
||||
|
||||
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
|
||||
|
||||
|
@ -17,18 +17,21 @@ impl Node for Subtract {
|
|||
|
||||
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
|
||||
// Zero rule
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
|
||||
(NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_right),
|
||||
(_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => Ok(evaluated_left),
|
||||
|
||||
// Constant + Constant = Constant
|
||||
// Constant - Constant = Constant
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Rc::new(Constant::new(a.get_value() - b.get_value()).into()))
|
||||
Ok(Rc::new(NodeEnum::from(Constant::new(Float::with_val_64(
|
||||
env.get_float_precision(),
|
||||
a.get_value() - b.get_value(),
|
||||
)))))
|
||||
}
|
||||
// Symbol + Constant we just return the same
|
||||
// Symbol - Constant we just return the same
|
||||
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
|
||||
Subtract::new(evaluated_left, evaluated_right).into(),
|
||||
)),
|
||||
// Constant + Symbol we switch them around so the constant is last
|
||||
// Constant - Symbol we switch them around so the constant is last
|
||||
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
|
||||
Subtract::new(evaluated_right, evaluated_left).into(),
|
||||
)),
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
use std::{iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter};
|
||||
use std::{env, iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter};
|
||||
|
||||
use rug::Float;
|
||||
|
||||
use crate::{
|
||||
environment::Environment,
|
||||
node::{
|
||||
add::Add, assign::Assign, call::Call, comparison::{Greater, GreaterEquals, Less, LessEquals}, constant::{Constant, ConstantValue}, divide::Divide, equals::Equals, exponent::Exponent, function::{Function, FunctionType}, if_else::{Bool, ElseBranchEnum, IfElse}, multiply::Multiply, set::Set, string_node::StringNode, subtract::Subtract, symbol::Symbol, NodeEnum
|
||||
NodeEnum,
|
||||
add::Add,
|
||||
assign::Assign,
|
||||
call::Call,
|
||||
comparison::{Greater, GreaterEquals, Less, LessEquals},
|
||||
constant::{Constant, ConstantValue},
|
||||
divide::Divide,
|
||||
equals::Equals,
|
||||
exponent::Exponent,
|
||||
function::{Function, FunctionType},
|
||||
if_else::{Bool, ElseBranchEnum, IfElse},
|
||||
multiply::Multiply,
|
||||
set::Set,
|
||||
string_node::StringNode,
|
||||
subtract::Subtract,
|
||||
symbol::Symbol,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -13,7 +30,7 @@ pub struct Token(usize, TokenType);
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TokenType {
|
||||
// Space,
|
||||
Number(ConstantValue),
|
||||
Number(String),
|
||||
Identifier(String),
|
||||
String(String),
|
||||
|
||||
|
@ -34,6 +51,9 @@ pub enum TokenType {
|
|||
|
||||
RParen,
|
||||
LParen,
|
||||
|
||||
RSquare,
|
||||
LSquare,
|
||||
Comma,
|
||||
|
||||
If,
|
||||
|
@ -68,6 +88,9 @@ impl TokenType {
|
|||
|
||||
TokenType::RParen => 1,
|
||||
TokenType::LParen => 1,
|
||||
|
||||
TokenType::RSquare => 1,
|
||||
TokenType::LSquare => 1,
|
||||
TokenType::Comma => 1,
|
||||
|
||||
TokenType::If => 2,
|
||||
|
@ -88,7 +111,6 @@ pub struct Lexer<'a> {
|
|||
#[derive(Debug)]
|
||||
pub enum LexerError {
|
||||
UnexpectedChar(usize, String),
|
||||
NumberParse(usize, String),
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
|
@ -125,7 +147,7 @@ impl<'a> Lexer<'a> {
|
|||
loop {
|
||||
let d = self.source.peek();
|
||||
match d {
|
||||
Some('0'..='9') | Some('e') | Some('E') => {
|
||||
Some('0'..='9') => {
|
||||
digit.push(*d.unwrap());
|
||||
self.source.next();
|
||||
i += 1;
|
||||
|
@ -148,41 +170,41 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
let number =
|
||||
if let Some(v) = ConstantValue::from_sci_string_simplest(digit.as_str()) {
|
||||
v
|
||||
} else {
|
||||
return Err(LexerError::NumberParse(
|
||||
i,
|
||||
format!("Failed to convert {digit} to a number"),
|
||||
));
|
||||
};
|
||||
// if let Some(v) = {
|
||||
// v
|
||||
// } else {
|
||||
// return Err(LexerError::NumberParse(
|
||||
// i,
|
||||
// format!("Failed to convert {digit} to a number"),
|
||||
// ));
|
||||
// };
|
||||
|
||||
tokens.push(Token(i, TokenType::Number(number)));
|
||||
tokens.push(Token(i, TokenType::Number(digit)));
|
||||
}
|
||||
|
||||
'"' => {
|
||||
let mut buffer = "".to_owned();
|
||||
loop {
|
||||
let next = self.source.peek();
|
||||
let next = self.source.peek();
|
||||
|
||||
match next {
|
||||
Some('"') => {
|
||||
tokens.push(Token(i, TokenType::String(buffer.clone())));
|
||||
self.source.next();
|
||||
break;
|
||||
}
|
||||
Some(_) => {
|
||||
buffer.push(self.source.next().unwrap());
|
||||
}
|
||||
None => {
|
||||
return Err(LexerError::UnexpectedChar(
|
||||
i,
|
||||
"Unexpected End of file".to_owned(),
|
||||
));
|
||||
match next {
|
||||
Some('"') => {
|
||||
tokens.push(Token(i, TokenType::String(buffer.clone())));
|
||||
self.source.next();
|
||||
break;
|
||||
}
|
||||
Some(_) => {
|
||||
buffer.push(self.source.next().unwrap());
|
||||
}
|
||||
None => {
|
||||
return Err(LexerError::UnexpectedChar(
|
||||
i,
|
||||
"Unexpected End of file".to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
}
|
||||
|
||||
// LeftArrow (->)
|
||||
'-' if self.source.peek() == Some(&'>') => {
|
||||
|
@ -221,6 +243,9 @@ impl<'a> Lexer<'a> {
|
|||
'(' => tokens.push(Token(i, TokenType::LParen)),
|
||||
')' => tokens.push(Token(i, TokenType::RParen)),
|
||||
|
||||
'[' => tokens.push(Token(i, TokenType::LSquare)),
|
||||
']' => tokens.push(Token(i, TokenType::RSquare)),
|
||||
|
||||
_ if c.is_alphabetic() || c == '_' => {
|
||||
tokens.push(self.lex_identifier(&mut i, c)?);
|
||||
}
|
||||
|
@ -273,6 +298,7 @@ pub enum ParserError {
|
|||
UnexpectedToken(usize, usize, String),
|
||||
Unimplemented(usize, usize, String),
|
||||
UnexpectedNode(usize, String),
|
||||
NumberParse(usize, String),
|
||||
}
|
||||
|
||||
/// Recursive descent parser
|
||||
|
@ -464,34 +490,95 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
fn call(&mut self) -> Result<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 {
|
||||
let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() {
|
||||
(*i, x.clone())
|
||||
} else {
|
||||
return expr;
|
||||
return Ok(expr);
|
||||
};
|
||||
|
||||
// If the next token is a parenthesis then we construct a call
|
||||
if t == TokenType::LParen {
|
||||
let potential_parameters = self.primary()?;
|
||||
let parameters = if let NodeEnum::Set(set) = potential_parameters.as_ref() {
|
||||
potential_parameters
|
||||
} else {
|
||||
// return Err(ParserError::UnexpectedNode(
|
||||
// i,
|
||||
// format!("Expected a Set here, but got a {potential_parameters:?}"),
|
||||
// ));
|
||||
Set::new(vec![potential_parameters])
|
||||
};
|
||||
self.consume();
|
||||
|
||||
expr = Ok(Call::new(expr?, parameters));
|
||||
// Calls can have 0 arguments, so check and return early
|
||||
if self.matchType(TokenType::RParen) {
|
||||
expr = Call::new(expr, vec![]);
|
||||
} else {
|
||||
// Parse expressions until a patching Right-Parenthesis is found
|
||||
let mut parameters = vec![self.equality()?];
|
||||
|
||||
while self.matchType(TokenType::Comma) {
|
||||
parameters.push(self.equality()?);
|
||||
}
|
||||
|
||||
if !self.matchType(TokenType::RParen) {
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
i,
|
||||
t.len(),
|
||||
"Unclosed right parenthesis".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
// If the next token is a ColonEquals (assignment) then
|
||||
// the user wants function assignment sugar
|
||||
//
|
||||
// Ie f(x) := x*5 => f := x -> x*5
|
||||
if self.matchType(TokenType::ColonEquals) {
|
||||
if let NodeEnum::Symbol(_) = expr.as_ref() {
|
||||
} else {
|
||||
let Token(i, token) = self.previous.as_ref().unwrap();
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
*i,
|
||||
token.len(),
|
||||
format!(
|
||||
"Expected an Identifier here but found a {}",
|
||||
expr.type_str()
|
||||
),
|
||||
));
|
||||
};
|
||||
|
||||
// Parse body
|
||||
let body = self.equality()?;
|
||||
|
||||
// Convert vector of expressions to vector of symbols
|
||||
let mut arguments = Vec::with_capacity(parameters.len());
|
||||
for param in parameters.into_iter() {
|
||||
if let NodeEnum::Symbol(symbol) =
|
||||
Rc::<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 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expr
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn function(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||
|
@ -588,6 +675,48 @@ impl<'a> Parser<'a> {
|
|||
return Ok(IfElse::new(condition, expressions, else_branch));
|
||||
}
|
||||
|
||||
self.set()
|
||||
}
|
||||
|
||||
fn set(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
|
||||
if self.matchType(TokenType::LSquare) {
|
||||
// Empty set
|
||||
if self.matchType(TokenType::RSquare) {
|
||||
return Ok(Set::new(vec![]));
|
||||
}
|
||||
|
||||
let mut values = vec![self.equality()?];
|
||||
while {
|
||||
if let Some(Token(_, TokenType::RSquare)) = self.tokens.peek() {
|
||||
self.consume();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} {
|
||||
let (i, token) = if let Some(Token(i, x)) = self.tokens.peek() {
|
||||
(i, x)
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedEndOfTokens(
|
||||
"Expected comma here".into(),
|
||||
));
|
||||
};
|
||||
|
||||
if *token == TokenType::Comma {
|
||||
self.consume();
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
*i,
|
||||
token.len(),
|
||||
format!("Expected comma here, but got {token:?}"),
|
||||
));
|
||||
}
|
||||
|
||||
values.push(self.equality()?);
|
||||
}
|
||||
|
||||
return Ok(Set::new(values));
|
||||
}
|
||||
self.primary()
|
||||
}
|
||||
|
||||
|
@ -601,7 +730,17 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
match token {
|
||||
TokenType::Number(value) => Ok(Rc::new(Constant::new(value).into())),
|
||||
TokenType::Number(value) => {
|
||||
let value = if let Ok(incomplete) = Float::parse(&value) {
|
||||
Float::with_val_64(self.environment.get_float_precision(), incomplete)
|
||||
} else {
|
||||
return Err(ParserError::NumberParse(
|
||||
i,
|
||||
format!("Failed to convert `{value}` to a number"),
|
||||
));
|
||||
};
|
||||
Ok(Rc::new(Constant::new(value).into()))
|
||||
}
|
||||
TokenType::Identifier(string) => Ok(Rc::new(
|
||||
Symbol::new_from_str(string, self.environment).into(),
|
||||
)),
|
||||
|
@ -611,70 +750,25 @@ impl<'a> Parser<'a> {
|
|||
TokenType::String(s) => Ok(StringNode::new(s)),
|
||||
|
||||
TokenType::LParen => {
|
||||
// Empty set
|
||||
if self.matchType(TokenType::RParen) {
|
||||
return Ok(Set::new(vec![]));
|
||||
}
|
||||
|
||||
let expr = self.expression()?;
|
||||
let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() {
|
||||
(i, x)
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedEndOfTokens(
|
||||
"Unclosed right parenthesis".into(),
|
||||
));
|
||||
};
|
||||
|
||||
match t {
|
||||
TokenType::RParen => {
|
||||
self.consume();
|
||||
Ok(expr)
|
||||
if !self.matchType(TokenType::RParen) {
|
||||
if let Some(Token(i, t)) = self.tokens.peek() {
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
*i,
|
||||
t.len(),
|
||||
format!("Expected right parenthesis here, but got {t:?}"),
|
||||
));
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
i,
|
||||
1,
|
||||
"Unclosed right parenthesis".to_owned(),
|
||||
));
|
||||
}
|
||||
TokenType::Comma => {
|
||||
let mut values = vec![expr];
|
||||
while {
|
||||
if let Some(Token(_, TokenType::RParen)) = self.tokens.peek() {
|
||||
self.consume();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} {
|
||||
let (i, token) = if let Some(Token(i, x)) = self.tokens.peek() {
|
||||
(i, x)
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedEndOfTokens(
|
||||
"Expected comma here".into(),
|
||||
));
|
||||
};
|
||||
|
||||
if *token == TokenType::Comma {
|
||||
self.consume();
|
||||
} else {
|
||||
return Err(ParserError::UnexpectedToken(
|
||||
*i,
|
||||
token.len(),
|
||||
format!("Expected comma here, but got {token:?}"),
|
||||
));
|
||||
}
|
||||
|
||||
values.push(self.equality()?);
|
||||
}
|
||||
|
||||
Ok(Set::new(values))
|
||||
}
|
||||
_ => Err(ParserError::Unimplemented(
|
||||
*i,
|
||||
t.len(),
|
||||
format!("Expected either a comma or a right parenthesis here. Got {t:?}"),
|
||||
)),
|
||||
}
|
||||
|
||||
// if t != TokenType::RParen {
|
||||
// Err(ParserError::UnexpectedToken(i, t.len(), format!("")))
|
||||
// } else {
|
||||
// Ok(expr)
|
||||
// }
|
||||
Ok(expr)
|
||||
}
|
||||
_ => Err(ParserError::UnexpectedToken(
|
||||
i,
|
||||
|
|
Loading…
Reference in a new issue