This commit is contained in:
Snorre Ettrup Altschul 2025-02-21 20:42:56 +01:00
parent b8f2963769
commit fa8aae88b4
21 changed files with 991 additions and 310 deletions

122
Cargo.lock generated
View file

@ -8,6 +8,18 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -38,7 +50,7 @@ dependencies = [
"bitflags 2.8.0",
"cexpr",
"clang-sys",
"itertools",
"itertools 0.12.1",
"lazy_static",
"lazycell",
"log",
@ -369,10 +381,19 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
dependencies = [
"ahash",
"ahash 0.3.8",
"autocfg",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash 0.8.11",
]
[[package]]
name = "hibitset"
version = "0.6.4"
@ -388,6 +409,15 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
@ -425,6 +455,12 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "libm"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libredox"
version = "0.1.3"
@ -458,6 +494,51 @@ version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "malachite"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa282d5c3914eba1e50fd81012e04cfd5a79a08c29ece0fa20ef73afcbd7804"
dependencies = [
"malachite-base",
"malachite-nz",
"malachite-q",
]
[[package]]
name = "malachite-base"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef157484a1d7246fbff1a4fd2c148b83ac7324161963af61001ea43dc9086076"
dependencies = [
"hashbrown 0.14.5",
"itertools 0.11.0",
"libm",
"ryu",
]
[[package]]
name = "malachite-nz"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de4451b2e0e0ec6e4f39ee311b72154d291b185f5450474ace5663e37d7f8360"
dependencies = [
"itertools 0.11.0",
"libm",
"malachite-base",
]
[[package]]
name = "malachite-q"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa9bd8eccda7ff0959d48e00e03ee3626817414442725b2cc1f1b2f215ceee9"
dependencies = [
"itertools 0.11.0",
"malachite-base",
"malachite-nz",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
@ -511,6 +592,7 @@ dependencies = [
"clay-layout",
"enum_dispatch",
"font-kit",
"malachite",
"raylib",
"termion",
"test-case",
@ -708,6 +790,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "ryu"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
[[package]]
name = "same-file"
version = "1.0.6"
@ -742,7 +830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d"
dependencies = [
"arrayvec",
"hashbrown",
"hashbrown 0.7.2",
"mopa",
"smallvec",
"tynm",
@ -767,7 +855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240"
dependencies = [
"crossbeam-queue",
"hashbrown",
"hashbrown 0.7.2",
"hibitset",
"log",
"shred",
@ -894,6 +982,12 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
@ -1111,3 +1205,23 @@ dependencies = [
"once_cell",
"pkg-config",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]

View file

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

View file

@ -1,8 +1,13 @@
use std::io::{self, StdoutLock, Write, stdout};
use std::rc::Rc;
use libopenbirch::environment::Environment;
use libopenbirch::node::Node;
use libopenbirch::node::constant::Constant;
use libopenbirch::node::empty::Empty;
use libopenbirch::node::{Node, NodeEnum};
use libopenbirch::parser::{Lexer, Parser, ParserError};
use malachite::base::num::basic::traits::Zero;
use malachite::rational::Rational;
#[cfg(feature = "async")]
use termion::AsyncReader;
use termion::color;
@ -148,12 +153,24 @@ fn print_err(i: usize, len: usize, exp: String) {
);
}
fn print(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
for expr in args {
println!("\r{}", expr.as_string(Some(env)));
}
Ok(Empty::new(""))
}
fn main() -> Result<(), io::Error> {
let mut input = Input::new();
let mut env = Environment::new();
env.define_native_function("print", print);
while let Some(source) = input.get()? {
input.disable_raw()?;
let mut lexer = Lexer::new(&source);
let tokens_result = lexer.lex();
@ -164,9 +181,6 @@ fn main() -> Result<(), io::Error> {
continue;
}
#[cfg(debug_assertions)]
input.disable_raw()?;
let tokens = tokens_result.unwrap();
let mut parser = Parser::new(tokens, &mut env);
@ -193,7 +207,6 @@ fn main() -> Result<(), io::Error> {
}
}
#[cfg(debug_assertions)]
input.enable_raw()?;
print!("{}", color::Fg(color::Reset));

View file

@ -1,16 +1,28 @@
use std::{collections::HashMap, rc::Rc};
use std::{collections::HashMap, rc::Rc, thread::current};
use crate::node::NodeEnum;
use crate::node::{
NodeEnum,
function::{self, Function, FunctionType, NativeFunctionType},
};
pub type EnvironmentInternalSymbolKey = u16;
// pub type Environment = HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>;
struct Scope {
pub stack_len: usize,
pub shadow_len: usize,
}
pub struct Environment {
map: HashMap<EnvironmentInternalSymbolKey, Rc<NodeEnum>>,
stack_depth: usize,
max_stack_depth: usize,
stack: Vec<Vec<EnvironmentInternalSymbolKey>>,
stack_sizes: Vec<Scope>,
stack: Vec<EnvironmentInternalSymbolKey>,
stack_shadows: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
// stack: Vec<Vec<EnvironmentInternalSymbolKey>>,
symbol_to_id: HashMap<String, EnvironmentInternalSymbolKey>,
id_to_symbol: HashMap<EnvironmentInternalSymbolKey, String>,
unique_keys: EnvironmentInternalSymbolKey,
@ -22,7 +34,10 @@ impl Environment {
map: HashMap::new(),
stack_depth: 0,
max_stack_depth: 5000,
stack: vec![vec![]],
stack_sizes: vec![],
stack: vec![],
stack_shadows: vec![],
symbol_to_id: HashMap::new(),
id_to_symbol: HashMap::new(),
@ -31,61 +46,101 @@ impl Environment {
}
pub fn push_stack(&mut self) -> Result<(), String> {
debug_assert!(self.stack_depth == self.stack.len() - 1);
if self.stack_depth == self.max_stack_depth {
self.unwind_stack();
return Err("Max Stack depth exceeded".to_owned());
}
self.stack_depth += 1;
self.stack.push(vec![]);
self.stack_sizes.push(Scope {
stack_len: 0,
shadow_len: 0,
});
debug_assert!(self.stack_depth == self.stack.len() - 1);
// debug_assert!(self.stack_depth == self.stack.len() - 1);
Ok(())
}
pub fn pop_stack(&mut self) -> Result<(), String> {
debug_assert!(
self.stack_depth == self.stack.len() - 1,
"Expected stack len: {}, stack_depth: {}",
self.stack.len(),
self.stack_depth
);
if self.stack_depth == 0 {
return Err("Trying to pop empty stack".to_owned());
}
self.stack_depth -= 1;
for item in self.stack.pop().unwrap() {
self.map.remove(&item);
let scope = self.stack_sizes.pop().unwrap();
for _ in 0..scope.stack_len {
self.map.remove(&self.stack.pop().unwrap());
}
for _ in 0..scope.shadow_len {
let (id, value) = self.stack_shadows.pop().unwrap();
self.map.insert(id, value);
}
debug_assert!(self.stack_depth == self.stack.len() - 1);
Ok(())
}
fn unwind_stack(&mut self) {
for stack in &self.stack {
for item in stack {
self.map.remove(&item);
for scope in &self.stack_sizes {
for _ in 0..scope.stack_len {
self.map.remove(&self.stack.pop().unwrap());
}
}
self.stack = vec![vec![]];
self.stack_sizes = vec![];
self.stack_shadows = vec![];
self.stack_depth = 0;
}
pub fn define(&mut self, name: String, value: Rc<NodeEnum>) {
let id = if let Some(value) = self.str_to_id(&name) {
*value
} else {
self.get_new_id()
};
self.map.insert(id, value);
self.insert_id_to_str(id, name);
}
pub fn define_native_function(&mut self, name: &'static str, func: NativeFunctionType) {
let f = Function::new(FunctionType::Native(name, func));
self.define(name.to_owned(), f);
}
pub fn get(&self, key: &EnvironmentInternalSymbolKey) -> Option<&Rc<NodeEnum>> {
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>) {
if self.stack_depth != 0 {
let mut shadow = false;
if let Some(existing) = self.map.get(&key) {
if !self.get_current_scope().contains(&key) {
// We need to shadow this variable
self.stack_shadows
.push((key, self.map.insert(key, value.clone()).unwrap()));
self.stack.push(key);
shadow = true;
}
}
let scope = unsafe { self.stack_sizes.get_unchecked_mut(self.stack_depth - 1) };
scope.stack_len += 1;
self.stack.push(key);
if shadow {
scope.shadow_len += 1;
return;
}
}
self.map.insert(key, value);
unsafe { self.stack.get_unchecked_mut(self.stack_depth).push(key) }
}
pub fn str_to_id(&self, value: &String) -> Option<&EnvironmentInternalSymbolKey> {

View file

@ -1,5 +1,7 @@
use std::rc::Rc;
use malachite::{base::num::basic::traits::Zero, rational::Rational};
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
@ -15,20 +17,16 @@ impl Node for Add {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == 0. => Ok(evaluated_right),
(_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => Ok(evaluated_left),
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right),
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
// Constant + Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into()))
}
// Symbol + Constant we just return the same
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => {
Ok(Rc::new(Add::new(evaluated_left, evaluated_right).into()))
}
// Constant + Symbol we switch them around so the constant is last
// Move constants after symbols in add
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
Ok(Rc::new(Add::new(evaluated_right, evaluated_left).into()))
}
@ -61,6 +59,10 @@ impl Add {
Self { left, right }
}
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
Rc::new(Self { left, right }.into())
}
pub fn get_left(&self) -> Rc<NodeEnum> {
self.left.clone()
}

View file

@ -35,27 +35,24 @@ impl Node for Call {
);
};
// Check if argument counts match
let fargs = func.get_arguments();
if fargs.len() != arguments.len() {
return Err(format!(
"Error calling function. Expected {} arguments, but got {}",
func.get_arguments().len(),
arguments.len()
));
}
// Call function body with arguments
match func.get_body() {
// Pass arguments to native function
FunctionType::Native(_name, native_function) => native_function(&arguments),
FunctionType::UserFunction(node_enum) => {
FunctionType::Native(_name, native_function) => native_function(&arguments, env),
FunctionType::UserFunction(body, fargs) => {
if fargs.len() != arguments.len() {
return Err(format!(
"Error calling function. Expected {} arguments, but got {}",
fargs.len(),
arguments.len()
));
}
env.push_stack()?;
// Define variables
fargs.iter().zip(arguments).for_each(|(symbol, value)| {
env.insert(symbol.get_value(), value.clone());
});
let ev = node_enum.evaluate(env)?;
let ev = body.evaluate(env)?;
env.pop_stack()?;
// Return evaluated return value for function
Ok(ev)

237
src/lib/node/comparison.rs Normal file
View 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))
}
}

View file

@ -2,7 +2,7 @@ use std::rc::Rc;
use super::{Environment, Node, Precedence};
pub type ConstantValue = f64;
pub type ConstantValue = malachite::rational::Rational;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Constant {
@ -34,8 +34,8 @@ impl Constant {
Self { value }
}
pub fn get_value(&self) -> ConstantValue {
self.value
pub fn get_value(&self) -> &ConstantValue {
&self.value
}
pub fn set_value(&mut self, value: ConstantValue) {

View file

@ -1,6 +1,8 @@
use std::rc::Rc;
use super::{constant::Constant, set::Set, Node, NodeEnum, Precedence};
use malachite::{base::num::basic::traits::Zero, rational::Rational};
use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Divide {
@ -15,27 +17,20 @@ impl Node for Divide {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Error if dividing by zero
(_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => {
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => {
Err("Division by Zero".into())
}
(_, _) if evaluated_left == evaluated_right => Ok(Rc::new(Constant::new(Rational::ZERO).into())),
// Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == 0. => {
Ok(evaluated_left)
}
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
// Constant + Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
Ok(Rc::new(Constant::new(a.get_value() / b.get_value()).into()))
}
// Symbol + Constant we just return the same
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => {
Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into()))
}
// Constant + Symbol we switch them around so the constant is last
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
Ok(Rc::new(Divide::new(evaluated_right, evaluated_left).into()))
}
// Divide a set with a constant
(NodeEnum::Set(s), NodeEnum::Constant(c)) => {
let old_values = s.get_values();
@ -90,6 +85,10 @@ impl Divide {
Self { left, right }
}
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
Rc::new(Self::new(left, right).into())
}
pub fn get_left(&self) -> Rc<NodeEnum> {
self.left.clone()
}

View file

@ -2,11 +2,18 @@ use std::{rc::Rc, sync::LazyLock};
use super::{Environment, Node, NodeEnum, Precedence};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[derive(Debug, Clone, PartialOrd)]
pub struct Empty {
message: Option<String>,
}
impl PartialEq for Empty {
fn eq(&self, other: &Self) -> bool {
true
}
}
impl Eq for Empty {}
impl Node for Empty {
fn evaluate(&self, _: &mut Environment) -> Result<Rc<NodeEnum>, String> {
Ok(Empty::EMPTY.clone())

View file

@ -5,25 +5,31 @@ use super::{Environment, Node, NodeEnum, Precedence, if_else::Bool};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Equals {
left: Rc<NodeEnum>,
right: Rc<NodeEnum>,
right: Vec<Rc<NodeEnum>>,
}
impl Node for Equals {
fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
let left = self.left.evaluate(env)?;
let right = self.right.evaluate(env)?;
match left == right {
true => Ok(Rc::new(Bool::True.into())),
false => Ok(Rc::new(Bool::False.into())),
for expr in &self.right {
if left != expr.evaluate(env)? {
return Ok(Rc::new(Bool::False.into()));
}
}
return Ok(Rc::new(Bool::True.into()));
}
fn as_string(&self, env: Option<&Environment>) -> String {
format!(
"({}={})",
"{} = {}",
self.left.as_string(env),
self.right.as_string(env)
self.right
.iter()
.map(|x| x.as_string(env))
.reduce(|a, b| a + " = " + &b)
.unwrap()
)
}
@ -33,7 +39,7 @@ impl Node for Equals {
}
impl Equals {
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
pub fn new(left: Rc<NodeEnum>, right: Vec<Rc<NodeEnum>>) -> Self {
Self { left, right }
}
}

68
src/lib/node/exponent.rs Normal file
View file

@ -0,0 +1,68 @@
use std::rc::Rc;
use malachite::{Natural, rational::Rational, rational::arithmetic::pow};
use malachite::base::num::arithmetic::traits::Pow;
use malachite::rational::conversion::integer_from_rational;
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Exponent {
left: Rc<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)) => {
let v = b.get_value();
let exp: u64 = if let Ok(u) = v.try_into() {
u
} else {
return Err("Exponent was too large".into());
};
Ok(Rc::new(Constant::new(a.get_value().pow(exp)).into()))
}
_ => Ok(Self::new(evaluated_left, evaluated_right)),
}
}
fn as_string(&self, env: Option<&Environment>) -> String {
let left_string = if self.left.precedence() <= self.precedence() {
format!("({})", self.left.as_string(env))
} else {
self.left.as_string(env)
};
let right_string = if self.right.precedence() <= self.precedence() {
format!("({})", self.right.as_string(env))
} else {
self.right.as_string(env)
};
format!("{}^{}", left_string, right_string)
}
fn precedence(&self) -> Precedence {
Precedence::Exponent
}
}
impl Exponent {
pub fn new(left: Rc<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
}
}

View file

@ -4,21 +4,17 @@ use crate::environment::Environment;
use super::{Node, NodeEnum, Precedence, symbol::Symbol};
pub type NativeFunctionType = fn(&Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, String>;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum FunctionType {
Native(
&'static str,
fn(&Vec<Rc<NodeEnum>>) -> Result<Rc<NodeEnum>, String>,
),
UserFunction(Rc<NodeEnum>),
Native(&'static str, NativeFunctionType),
UserFunction(Rc<NodeEnum>, Vec<Symbol>),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Function {
function: FunctionType,
// TODO: Finish reasoning about whether or not this is
// the right way to implement functions
arguments: Vec<Symbol>,
}
impl Node for Function {
@ -27,16 +23,16 @@ impl Node for Function {
}
fn as_string(&self, env: Option<&Environment>) -> String {
let args = self
.arguments
.iter()
.map(|x| Node::as_string(x, env))
.reduce(|acc, e| format!("{acc}, {e}"))
.unwrap_or("()".to_owned());
match &self.function {
FunctionType::Native(name, _) => name.to_owned().to_owned(),
FunctionType::UserFunction(body) => format!("({args} -> {})", body.as_string(env)),
FunctionType::Native(name, _) => format!("(Native Function `{name}`)"),
FunctionType::UserFunction(body, args) => format!(
"({} -> {})",
args.iter()
.map(|x| x.as_string(env))
.reduce(|acc, e| format!("{acc}, {e}"))
.unwrap_or("()".to_owned()),
body.as_string(env)
),
}
}
@ -46,21 +42,11 @@ impl Node for Function {
}
impl Function {
pub fn new(t: FunctionType, args: Vec<Symbol>) -> Rc<NodeEnum> {
Rc::new(
Self {
function: t,
arguments: args,
}
.into(),
)
pub fn new(t: FunctionType) -> Rc<NodeEnum> {
Rc::new(Self { function: t }.into())
}
pub fn get_body(&self) -> &FunctionType {
&self.function
}
pub fn get_arguments(&self) -> &Vec<Symbol> {
&self.arguments
}
}

View file

@ -64,12 +64,12 @@ impl Node for IfElse {
for expr in to_evaluate {
expr.evaluate(env)?;
}
last.evaluate(env)
last.evaluate(env)?
} else {
Ok(Empty::EMPTY.clone())
Empty::EMPTY.clone()
};
env.pop_stack()?;
ret
Ok(ret)
}
match condition {

View file

@ -3,38 +3,42 @@ use std::{fmt::Display, rc::Rc};
use add::Add;
use assign::Assign;
use call::Call;
use comparison::{Greater, GreaterEquals, Less, LessEquals};
use constant::Constant;
use divide::Divide;
use empty::Empty;
use enum_dispatch::enum_dispatch;
use equals::Equals;
use exponent::Exponent;
use function::Function;
use if_else::{Bool, IfElse};
use multiply::Multiply;
use set::Set;
use subtract::Subtract;
use symbol::Symbol;
use equals::Equals;
use set::Set;
use crate::environment::Environment;
pub mod add;
pub mod assign;
pub mod call;
pub mod comparison;
pub mod constant;
pub mod divide;
pub mod empty;
pub mod equals;
pub mod exponent;
pub mod function;
pub mod if_else;
pub mod multiply;
pub mod node_ref;
pub mod set;
pub mod subtract;
pub mod symbol;
pub mod node_ref;
pub mod assign;
pub mod empty;
pub mod function;
pub mod call;
pub mod if_else;
pub mod equals;
pub mod set;
#[enum_dispatch]
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[derive(Debug, Clone, PartialEq)]
pub enum NodeEnum {
Constant,
@ -43,6 +47,7 @@ pub enum NodeEnum {
Subtract,
Multiply,
Divide,
Exponent,
Symbol,
// NodeRef, // DEPRECATED, use Symbol
@ -57,17 +62,15 @@ pub enum NodeEnum {
Set,
Equals,
// Greater,
// GreaterEquals,
// Less,
// LessEquals
// Logical operators
// In,
// Where,
// Not,
// Or,
// And
Greater,
GreaterEquals,
Less,
LessEquals, // Logical operators
// In,
// Where,
// Not,
// Or,
// And
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
@ -79,6 +82,7 @@ pub enum Precedence {
Term,
Factor,
Unary,
Exponent,
Call,
Primary,
}
@ -95,3 +99,55 @@ impl Display for NodeEnum {
write!(f, "{}", Node::as_string(self, None))
}
}
impl PartialOrd for NodeEnum {
fn partial_cmp(&self, other: &Self) -> Option<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,
}
}
}

View file

@ -1,8 +1,13 @@
use std::rc::Rc;
use malachite::{base::num::basic::traits::{One, Zero}, rational::Rational};
use crate::environment::Environment;
use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set};
use super::{
Node, NodeEnum, Precedence, add::Add, constant::Constant, divide::Divide, set::Set,
subtract::Subtract,
};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Multiply {
@ -18,26 +23,57 @@ impl Node for Multiply {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule
(NodeEnum::Constant(zero), _) | (_, NodeEnum::Constant(zero))
if zero.get_value() == 0. =>
if zero.get_value() == &Rational::ZERO =>
{
Ok(Rc::new(Constant::new(0.).into()))
Ok(Rc::new(Constant::new(Rational::ZERO).into()))
}
// Identity rule
(NodeEnum::Constant(one), _) if one.get_value() == &Rational::ONE => Ok(evaluated_right),
(_, NodeEnum::Constant(one)) if one.get_value() == &Rational::ONE => Ok(evaluated_left),
// Multiply into parenthesis (add)
(_, NodeEnum::Add(add)) => Ok(Add::new_rc(
Self::new_rc(evaluated_left.clone(), add.get_left()).evaluate(env)?,
Self::new_rc(evaluated_left, add.get_right()).evaluate(env)?,
)),
(NodeEnum::Add(add), _) => Ok(Add::new_rc(
Self::new_rc(evaluated_right.clone(), add.get_left()).evaluate(env)?,
Self::new_rc(evaluated_right, add.get_right()).evaluate(env)?,
)),
// Multiply into parenthesis (sub)
(_, NodeEnum::Subtract(sub)) => Ok(Subtract::new_rc(
Self::new_rc(evaluated_left.clone(), sub.get_left()).evaluate(env)?,
Self::new_rc(evaluated_left, sub.get_right()).evaluate(env)?,
)),
(NodeEnum::Subtract(sub), _) => Ok(Subtract::new_rc(
Self::new_rc(evaluated_right.clone(), sub.get_left()).evaluate(env)?,
Self::new_rc(evaluated_right, sub.get_right()).evaluate(env)?,
)),
// Multiply fraction
(NodeEnum::Divide(div), _) => Ok(Divide::new_rc(
Self::new_rc(evaluated_right, div.get_left()).evaluate(env)?,
div.get_right(),
)),
(_, NodeEnum::Divide(div)) => Ok(Divide::new_rc(
Self::new_rc(evaluated_left, div.get_left()).evaluate(env)?,
div.get_right(),
)),
// Constant + Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
Ok(Rc::new(Constant::new(a.get_value() * b.get_value()).into()))
}
// Symbol + Constant we just return the same
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
// Move constant infront of symbols
(NodeEnum::Symbol(_), NodeEnum::Constant(_))
| (NodeEnum::Exponent(_), NodeEnum::Constant(_)) => Ok(Rc::new(
Multiply::new(evaluated_right, evaluated_left).into(),
)),
// Constant + Symbol we switch them around so the constant is last
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
Multiply::new(evaluated_left, evaluated_right).into(),
)),
// Multiply a set with a constant
(NodeEnum::Set(s), NodeEnum::Constant(c))
| (NodeEnum::Constant(c), NodeEnum::Set(s)) => {

View file

@ -25,7 +25,7 @@ impl Node for Set {
.iter()
.map(|x| x.as_string(env))
.reduce(|a, b| a + ", " + &b)
.unwrap()
.unwrap_or("".to_owned())
)
}

View file

@ -1,5 +1,7 @@
use std::rc::Rc;
use malachite::{base::num::basic::traits::Zero, rational::Rational};
use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};
#[derive(Clone, Debug, PartialEq, PartialOrd)]
@ -15,24 +17,24 @@ impl Node for Subtract {
match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
// Zero rule
(NodeEnum::Constant(zero), _) if zero.get_value() == 0. => Ok(evaluated_right),
(_, NodeEnum::Constant(zero)) if zero.get_value() == 0. => Ok(evaluated_left),
(NodeEnum::Constant(zero), _) if zero.get_value() == &Rational::ZERO => Ok(evaluated_right),
(_, NodeEnum::Constant(zero)) if zero.get_value() == &Rational::ZERO => Ok(evaluated_left),
// Constant + Constant = Constant
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
Ok(Rc::new(Constant::new(a.get_value() - b.get_value()).into()))
}
// Symbol + Constant we just return the same
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => {
Ok(Rc::new(Subtract::new(evaluated_left, evaluated_right).into()))
}
(NodeEnum::Symbol(_), NodeEnum::Constant(_)) => Ok(Rc::new(
Subtract::new(evaluated_left, evaluated_right).into(),
)),
// Constant + Symbol we switch them around so the constant is last
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => {
Ok(Rc::new(Subtract::new(evaluated_right, evaluated_left).into()))
}
_ => {
Ok(Rc::new(Subtract::new(evaluated_left, evaluated_right).into()))
}
(NodeEnum::Constant(_), NodeEnum::Symbol(_)) => Ok(Rc::new(
Subtract::new(evaluated_right, evaluated_left).into(),
)),
_ => Ok(Rc::new(
Subtract::new(evaluated_left, evaluated_right).into(),
)),
}
}
@ -57,10 +59,11 @@ impl Node for Subtract {
impl Subtract {
pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
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> {

View file

@ -3,7 +3,7 @@ use std::rc::Rc;
use super::{Environment, Node, NodeEnum, Precedence};
use crate::environment::EnvironmentInternalSymbolKey;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[derive(Debug, Clone, PartialEq)]
pub struct Symbol {
value: EnvironmentInternalSymbolKey,
}
@ -26,10 +26,10 @@ impl Node for Symbol {
} else {
env.id_to_str(&self.value)
.cloned()
.unwrap_or(format!("{{#SYMBOL {}}}", self.value))
.unwrap_or(format!("{{#SYMBOL {:?}}}", self.value))
}
} else {
format!("{{#SYMBOL {}}}", self.value)
format!("{{#SYMBOL {:?}}}", self.value)
}
}
@ -58,3 +58,9 @@ impl Symbol {
self.value
}
}
impl PartialOrd for Symbol {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
None
}
}

View file

@ -3,7 +3,21 @@ use std::{iter::Peekable, rc::Rc, slice::Iter, str::Chars, vec::IntoIter};
use crate::{
environment::Environment,
node::{
add::Add, assign::Assign, call::Call, constant::{Constant, ConstantValue}, divide::Divide, equals::Equals, function::{Function, FunctionType}, if_else::{Bool, ElseBranchEnum, IfElse}, multiply::Multiply, set::Set, subtract::Subtract, symbol::Symbol, NodeEnum
NodeEnum,
add::Add,
assign::Assign,
call::Call,
comparison::{Greater, GreaterEquals, Less, LessEquals},
constant::{Constant, ConstantValue},
divide::Divide,
equals::Equals,
exponent::Exponent,
function::{Function, FunctionType},
if_else::{Bool, ElseBranchEnum, IfElse},
multiply::Multiply,
set::Set,
subtract::Subtract,
symbol::Symbol,
},
};
@ -20,8 +34,13 @@ pub enum TokenType {
Minus,
Star,
Slash,
Hat,
Equals,
Greater,
Less,
GreaterEquals,
LessEquals,
ColonEquals,
LeftArrow,
@ -36,7 +55,7 @@ pub enum TokenType {
End,
True,
False
False,
}
impl TokenType {
@ -49,7 +68,13 @@ impl TokenType {
TokenType::Minus => 1,
TokenType::Star => 1,
TokenType::Slash => 1,
TokenType::Hat => 1,
TokenType::Equals => 1,
TokenType::Greater => 1,
TokenType::Less => 1,
TokenType::GreaterEquals => 2,
TokenType::LessEquals => 2,
TokenType::ColonEquals => 2,
TokenType::LeftArrow => 2,
@ -108,26 +133,26 @@ impl<'a> Lexer<'a> {
// Numbers with decimal points
'0'..='9' | '.' => {
let mut digit = String::from(c);
let mut has_decimal = c == '.';
loop {
let d = self.source.peek();
let mut has_decimal = c == '.';
match d {
Some('0'..='9') => {
digit.push(*d.unwrap());
self.source.next();
i += 1;
}
#[allow(unused_assignments)] // For some reason it thinks has_decimal
// is never read
Some('.') => {
if has_decimal {
return Err(LexerError::UnexpectedChar(
i,
i + 1,
"Invalid digit with multiple decimal points".into(),
));
}
digit.push(*d.unwrap());
self.source.next();
i += 1;
has_decimal = true;
}
_ => {
@ -147,12 +172,26 @@ impl<'a> Lexer<'a> {
tokens.push(Token(i, TokenType::LeftArrow));
}
'<' if self.source.peek() == Some(&'=') => {
self.source.next();
i += 1;
tokens.push(Token(i, TokenType::LessEquals));
}
'>' if self.source.peek() == Some(&'=') => {
self.source.next();
i += 1;
tokens.push(Token(i, TokenType::GreaterEquals));
}
'<' => tokens.push(Token(i, TokenType::Less)),
'>' => tokens.push(Token(i, TokenType::Greater)),
'+' => tokens.push(Token(i, TokenType::Plus)),
'-' => tokens.push(Token(i, TokenType::Minus)),
'*' => tokens.push(Token(i, TokenType::Star)),
'/' => tokens.push(Token(i, TokenType::Slash)),
'=' => tokens.push(Token(i, TokenType::Equals)),
',' => tokens.push(Token(i, TokenType::Comma)),
'^' => tokens.push(Token(i, TokenType::Hat)),
':' if self.source.peek() == Some(&'=') => {
self.source.next();
@ -314,16 +353,56 @@ impl<'a> Parser<'a> {
fn equality(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
// TODO: Implement equality
let expr = self.comparison();
let expr = self.comparison()?;
if self.matchType(TokenType::Equals) {
return Ok(Rc::new(Equals::new(expr?, self.equality()?).into()));
let mut expressions = vec![];
loop {
expressions.push(self.comparison()?);
if !self.matchType(TokenType::Equals) {
break;
}
}
return Ok(Rc::new(Equals::new(expr, expressions).into()));
}
expr
Ok(expr)
}
fn comparison(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
// TODO: Implement comparison
self.term()
let expr = self.term()?;
let t = if let Some(Token(i, t)) = self.tokens.peek() {
t.clone()
} else {
return Ok(expr);
};
if match t {
TokenType::Greater => true,
TokenType::Less => true,
TokenType::GreaterEquals => true,
TokenType::LessEquals => true,
_ => false,
} {
self.consume();
let mut expressions = vec![];
loop {
expressions.push(self.term()?);
if !self.matchType(t.clone()) {
break;
}
}
return match t {
TokenType::Greater => Ok(Greater::new(expr, expressions)),
TokenType::Less => Ok(Less::new(expr, expressions)),
TokenType::GreaterEquals => Ok(GreaterEquals::new(expr, expressions)),
TokenType::LessEquals => Ok(LessEquals::new(expr, expressions)),
_ => panic!(),
};
}
Ok(expr)
}
fn term(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
@ -356,7 +435,14 @@ impl<'a> Parser<'a> {
}
fn exponent(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
self.call()
let expr = self.call();
if self.matchType(TokenType::Hat) {
let right = self.unary()?;
return Ok(Exponent::new(expr?, right));
}
expr
}
fn call(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
@ -403,10 +489,10 @@ impl<'a> Parser<'a> {
let right = self.equality()?;
match expr.clone().as_ref() {
NodeEnum::Symbol(symbol) => {
return Ok(Function::new(
FunctionType::UserFunction(right),
return Ok(Function::new(FunctionType::UserFunction(
right,
vec![symbol.clone()],
));
)));
}
NodeEnum::Set(set) => {
let mut symbols = vec![];
@ -425,7 +511,9 @@ impl<'a> Parser<'a> {
}
}
return Ok(Function::new(FunctionType::UserFunction(right), symbols));
return Ok(Function::new(
FunctionType::UserFunction(right, symbols),
));
}
_ => {
return Err(ParserError::UnexpectedNode(
@ -505,6 +593,11 @@ impl<'a> Parser<'a> {
TokenType::False => Ok(Rc::new(Bool::False.into())),
TokenType::LParen => {
// Empty set
if self.matchType(TokenType::RParen) {
return Ok(Set::new(vec![]));
}
let expr = self.expression()?;
let (i, t) = if let Some(Token(i, x)) = self.tokens.peek() {
(i, x)

View file

@ -1,133 +1,135 @@
mod arithmetic {
use std::rc::Rc;
// TODO: Test case this: `x*0*5*y*z`
use crate::{environment, node::{
add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
}};
use environment::Environment;
use test_case::test_case;
#[test_case(69.0, 420.0, 489.0 ; "when both are positive")]
#[test_case(-2.0, -4.0, -6.0 ; "when both are negative")]
#[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
#[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
// #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
let mut env = Environment::new();
let a = Rc::new(Constant::new(a).into());
let b = Rc::new(Constant::new(b).into());
let d: NodeEnum = Add::new(a, b).into();
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
assert!(
value == Constant::new(e).into(),
"Expected {} got {}",
e,
value
)
}
#[test_case(69.0, 420.0, -351.0 ; "when both are positive")]
#[test_case(-2.0, -4.0, 2.0 ; "when both are negative")]
#[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
#[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
// #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) {
let mut env = Environment::new();
let a = Rc::new(Constant::new(0.0).into());
let b = Rc::new(Constant::new(aa).into());
let c = Rc::new(Constant::new(bb).into());
let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into();
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
assert!(
value == Constant::new(e).into(),
"Expected {} got {}",
e,
value
)
}
#[test_case(5.0, 10.0, 50.0 ; "when both are positive")]
#[test_case(-5.0, 10.0, -50.0 ; "when left is negative")]
#[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
#[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
#[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
let mut env = Environment::new();
let a = Rc::new(Constant::new(a).into());
let b = Rc::new(Constant::new(b).into());
let d: NodeEnum = Multiply::new(a, b).into();
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
assert!(
value == Constant::new(e).into(),
"Expected {} got {}",
e,
value
)
}
#[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
#[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
#[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
#[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
let mut env = Environment::new();
let a = Rc::new(Constant::new(a).into());
let b = Rc::new(Constant::new(b).into());
let d: NodeEnum = Divide::new(a, b).into();
let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
assert!(
value == Constant::new(e).into(),
"Expected {} got {}",
e,
value
)
}
}
mod functions {
#[test]
fn stringification() {
// let f = Function
}
}
mod expected_errors {
use test_case::test_case;
use crate::node::constant::ConstantValue;
#[test_case(5.0, 0.0 ; "divide by zero")]
fn division(a: ConstantValue, b: ConstantValue) {
let _ = a+b;
}
}
mod misc {
use test_case::test_case;
use crate::node::constant::ConstantValue;
#[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
fn convert_to_string(
a: impl Into<ConstantValue>,
op1: char,
b: impl Into<ConstantValue>,
op2: char,
c: impl Into<ConstantValue>,
e: &'static str,
) {
}
}
// mod arithmetic {
// use std::rc::Rc;
//
// use crate::{environment, node::{
// add::Add, constant::{Constant, ConstantValue}, divide::Divide, multiply::Multiply, subtract::Subtract, Node, NodeEnum
// }};
// use environment::Environment;
//
// use test_case::test_case;
//
// #[test_case(69.0, 420.0, 489.0 ; "when both are positive")]
// #[test_case(-2.0, -4.0, -6.0 ; "when both are negative")]
// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
// fn addition(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
// let mut env = Environment::new();
//
// let a = Rc::new(Constant::new(a).into());
// let b = Rc::new(Constant::new(b).into());
// let d: NodeEnum = Add::new(a, b).into();
//
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
//
// assert!(
// value == Constant::new(e).into(),
// "Expected {} got {}",
// e,
// value
// )
// }
//
// #[test_case(69.0, 420.0, -351.0 ; "when both are positive")]
// #[test_case(-2.0, -4.0, 2.0 ; "when both are negative")]
// #[test_case(0.0, 0.0, 0.0 ; "when both are zero")]
// #[test_case(ConstantValue::INFINITY, 0.0, ConstantValue::INFINITY ; "infinity")]
// // #[test_case(ConstantValue::NAN, 0.0, ConstantValue::NAN ; "NaN")] // cant test NaN because NaN != NaN
// fn subtraction(aa: ConstantValue, bb: ConstantValue, e: ConstantValue) {
// let mut env = Environment::new();
//
// let a = Rc::new(Constant::new(0.0).into());
// let b = Rc::new(Constant::new(aa).into());
// let c = Rc::new(Constant::new(bb).into());
// let d: NodeEnum = Subtract::new(Rc::new(Add::new(a, b).into()), c).into();
//
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
//
// assert!(
// value == Constant::new(e).into(),
// "Expected {} got {}",
// e,
// value
// )
// }
//
// #[test_case(5.0, 10.0, 50.0 ; "when both are positive")]
// #[test_case(-5.0, 10.0, -50.0 ; "when left is negative")]
// #[test_case(5.0, -10.0, -50.0 ; "when right is negative")]
// #[test_case(-5.0, -10.0, 50.0 ; "when both are negative")]
// #[test_case(2734589235234.23, 0.0, 0.0 ; "when 0 is involved")]
// fn multiplication(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
// let mut env = Environment::new();
//
// let a = Rc::new(Constant::new(a).into());
// let b = Rc::new(Constant::new(b).into());
// let d: NodeEnum = Multiply::new(a, b).into();
//
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
//
// assert!(
// value == Constant::new(e).into(),
// "Expected {} got {}",
// e,
// value
// )
// }
//
// #[test_case(5.0, 10.0, 0.5 ; "when both are positive")]
// #[test_case(-5.0, 10.0, -0.5 ; "when left is negative")]
// #[test_case(5.0, -10.0, -0.5 ; "when right is negative")]
// #[test_case(-5.0, -10.0, 0.5 ; "when both are negative")]
// fn division(a: ConstantValue, b: ConstantValue, e: ConstantValue) {
// let mut env = Environment::new();
//
// let a = Rc::new(Constant::new(a).into());
// let b = Rc::new(Constant::new(b).into());
// let d: NodeEnum = Divide::new(a, b).into();
//
// let value = Rc::<NodeEnum>::try_unwrap(d.evaluate(&mut env).unwrap()).unwrap();
//
// assert!(
// value == Constant::new(e).into(),
// "Expected {} got {}",
// e,
// value
// )
// }
// }
//
// mod functions {
// #[test]
// fn stringification() {
// // let f = Function
// }
// }
//
// mod expected_errors {
// use test_case::test_case;
//
// use crate::node::constant::ConstantValue;
//
// #[test_case(5.0, 0.0 ; "divide by zero")]
// fn division(a: ConstantValue, b: ConstantValue) {
// let _ = a+b;
// }
// }
//
// mod misc {
// use test_case::test_case;
//
// use crate::node::constant::ConstantValue;
//
// #[test_case(30, '+', 60, '-', 20, "(30+60)-20" ; "add and subtract")]
// fn convert_to_string(
// a: impl Into<ConstantValue>,
// op1: char,
// b: impl Into<ConstantValue>,
// op2: char,
// c: impl Into<ConstantValue>,
// e: &'static str,
// ) {
// }
// }