initial commit

This commit is contained in:
Snorre 2025-02-11 19:27:56 +01:00
commit 84a4689a76
11 changed files with 467 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

103
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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) {
}
}