initial commit
This commit is contained in:
commit
84a4689a76
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
103
Cargo.lock
generated
Normal file
103
Cargo.lock
generated
Normal file
|
@ -0,0 +1,103 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "openbirch-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"enum_dispatch",
|
||||
"test-case",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
|
||||
dependencies = [
|
||||
"test-case-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case-core"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-case-macros"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "openbirch-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
enum_dispatch = "0.3.13"
|
||||
test-case = "3.3.1"
|
48
flake.lock
Normal file
48
flake.lock
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1736012469,
|
||||
"narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1736216977,
|
||||
"narHash": "sha256-EMueGrzBpryM8mgOyoyJ7DdNRRk09ug1ggcLLp0WrCQ=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "bbe7e4e7a70d235db4bbdcabbf8a2f6671881dd7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
34
flake.nix
Normal file
34
flake.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
description = "Openbirch rust flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, rust-overlay }:
|
||||
let
|
||||
# rust-overlay = import rust-overlay;
|
||||
pkgs = import nixpkgs
|
||||
{
|
||||
system = "x86_64-linux";
|
||||
overlays = [ rust-overlay.overlays.default ];
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs;
|
||||
[
|
||||
rust-bin.nightly.latest.default
|
||||
rust-analyzer
|
||||
];
|
||||
|
||||
shell = ''
|
||||
echo "Hello from nix dev shell"
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
18
src/main.rs
Normal file
18
src/main.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use node::{add::Add, constant::Constant, subtract::Subtract, Environment, Node, NodeEnum};
|
||||
|
||||
mod node;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
fn main() {
|
||||
let mut env = Environment::new();
|
||||
|
||||
let a = Constant::new(69.0).into();
|
||||
let b = Constant::new(420.0).into();
|
||||
let c = Constant::new(360.0).into();
|
||||
let d: NodeEnum = Subtract::new(Add::new(a,b).into(), c).into();
|
||||
|
||||
println!("d_0: {}", d);
|
||||
println!("d_1: {}", d.evaluate(&mut env).unwrap());
|
||||
}
|
48
src/node/add.rs
Normal file
48
src/node/add.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Add {
|
||||
left: Box<NodeEnum>,
|
||||
right: Box<NodeEnum>,
|
||||
}
|
||||
|
||||
impl Node for Add {
|
||||
fn evaluate(&self, env: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
||||
let evaluated_left = self.left.evaluate(env)?;
|
||||
let evaluated_right = self.right.evaluate(env)?;
|
||||
|
||||
match (evaluated_left, evaluated_right) {
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Constant::new(a.get_value() + b.get_value()).into())
|
||||
}
|
||||
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
let left_string = if self.left.precedence() <= self.precedence() {
|
||||
format!("({})", self.left.to_string())
|
||||
} else {
|
||||
self.left.to_string()
|
||||
};
|
||||
let right_string = if self.right.precedence() <= self.precedence() {
|
||||
format!("({})", self.right.to_string())
|
||||
} else {
|
||||
self.right.to_string()
|
||||
};
|
||||
format!("{}+{}", left_string, right_string)
|
||||
}
|
||||
|
||||
fn precedence(&self) -> Precedence {
|
||||
Precedence::Term
|
||||
}
|
||||
}
|
||||
|
||||
impl Add {
|
||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||
Self {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
}
|
36
src/node/constant.rs
Normal file
36
src/node/constant.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::{Node, NodeEnum, Precedence};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Constant {
|
||||
value: f64,
|
||||
}
|
||||
|
||||
impl Node for Constant {
|
||||
fn evaluate(&self, _: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
||||
Ok(self.clone().into())
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
self.value.to_string()
|
||||
}
|
||||
|
||||
fn precedence(&self) -> Precedence {
|
||||
Precedence::Primary
|
||||
}
|
||||
}
|
||||
|
||||
impl Constant {
|
||||
pub fn new(value: f64) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, value: f64) {
|
||||
self.value = value;
|
||||
}
|
||||
}
|
41
src/node/mod.rs
Normal file
41
src/node/mod.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use std::{cmp::Ordering, collections::HashMap, fmt::Display};
|
||||
|
||||
use add::Add;
|
||||
use constant::Constant;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use subtract::Subtract;
|
||||
|
||||
pub mod constant;
|
||||
pub mod add;
|
||||
pub mod subtract;
|
||||
|
||||
#[enum_dispatch]
|
||||
#[enum_dispatch(Debug, Clone, PartialEq, PartialOrd, ToString)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum NodeEnum {
|
||||
Constant(Constant),
|
||||
Add(Add),
|
||||
Subtract(Subtract)
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Precedence {
|
||||
Term,
|
||||
Factor,
|
||||
Primary,
|
||||
}
|
||||
|
||||
pub type Environment = HashMap<u16, NodeEnum>;
|
||||
|
||||
#[enum_dispatch(NodeEnum)]
|
||||
pub trait Node {
|
||||
fn evaluate(&self, _: &mut Environment) -> Result<NodeEnum, String>;
|
||||
fn to_string(&self) -> String;
|
||||
fn precedence(&self) -> Precedence;
|
||||
}
|
||||
|
||||
impl Display for NodeEnum {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", Node::to_string(self))
|
||||
}
|
||||
}
|
48
src/node/subtract.rs
Normal file
48
src/node/subtract.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use super::{Node, NodeEnum, Precedence, constant::Constant};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Subtract {
|
||||
left: Box<NodeEnum>,
|
||||
right: Box<NodeEnum>,
|
||||
}
|
||||
|
||||
impl Node for Subtract {
|
||||
fn evaluate(&self, env: &mut super::Environment) -> Result<super::NodeEnum, String> {
|
||||
let evaluated_left = self.left.evaluate(env)?;
|
||||
let evaluated_right = self.right.evaluate(env)?;
|
||||
|
||||
match (evaluated_left, evaluated_right) {
|
||||
(NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
|
||||
Ok(Constant::new(a.get_value() - b.get_value()).into())
|
||||
}
|
||||
_ => Err(format!("Invalid Add operation: {:?}", self)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
let left_string = if self.left.precedence() <= self.precedence() {
|
||||
format!("({})", self.left.to_string())
|
||||
} else {
|
||||
self.left.to_string()
|
||||
};
|
||||
let right_string = if self.right.precedence() <= self.precedence() {
|
||||
format!("({})", self.right.to_string())
|
||||
} else {
|
||||
self.right.to_string()
|
||||
};
|
||||
format!("{}-{}", left_string, right_string)
|
||||
}
|
||||
|
||||
fn precedence(&self) -> Precedence {
|
||||
Precedence::Term
|
||||
}
|
||||
}
|
||||
|
||||
impl Subtract {
|
||||
pub fn new(left: NodeEnum, right: NodeEnum) -> Self {
|
||||
Self {
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
}
|
||||
}
|
||||
}
|
82
src/tests/mod.rs
Normal file
82
src/tests/mod.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
mod arithmetic {
|
||||
use crate::node::{
|
||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
||||
};
|
||||
|
||||
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(f64::INFINITY, 0.0, f64::INFINITY ; "infinity")]
|
||||
// #[test_case(f64::NAN, 0.0, f64::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
||||
fn addition(a: f64, b: f64, e: f64) {
|
||||
let mut env = Environment::new();
|
||||
|
||||
let a = Constant::new(a).into();
|
||||
let b = Constant::new(b).into();
|
||||
let d: NodeEnum = Add::new(a, b).into();
|
||||
|
||||
let value = d.evaluate(&mut env).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(f64::INFINITY, 0.0, f64::INFINITY ; "infinity")]
|
||||
// #[test_case(f64::NAN, 0.0, f64::NAN ; "NaN")] // cant test NaN because NaN != NaN
|
||||
fn subtraction(aa: f64, bb: f64, e: f64) {
|
||||
let mut env = Environment::new();
|
||||
|
||||
let a = Constant::new(0.0).into();
|
||||
let b = Constant::new(aa).into();
|
||||
let c = Constant::new(bb).into();
|
||||
let d: NodeEnum = Subtract::new(Add::new(a, b).into(), c).into();
|
||||
|
||||
let value = d.evaluate(&mut env).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: f64, b: f64, e: f64) {}
|
||||
|
||||
#[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: f64, b: f64, e: f64) {}
|
||||
}
|
||||
|
||||
mod expected_errors {
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case(5.0, 0.0 ; "divide by zero")]
|
||||
fn division(a: f64, b: f64) {}
|
||||
}
|
||||
|
||||
mod misc {
|
||||
use crate::node::{
|
||||
Environment, Node, NodeEnum, add::Add, constant::Constant, subtract::Subtract,
|
||||
};
|
||||
|
||||
#[test_case(30, '+', 60, '-', 20, "(30+60)-20", "add and subtract")]
|
||||
fn convert_to_string(a: into<f64>, op1: char, b: Into<f64>, op2: char, c: Into<f64>, e: String) {
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue