Skip to content

Commit

Permalink
reorder files and work on executer
Browse files Browse the repository at this point in the history
  • Loading branch information
urisinger committed Oct 12, 2024
1 parent 125294a commit b607c2f
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 74 deletions.
183 changes: 183 additions & 0 deletions libraries/math-parser/src/ast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
use crate::value::Value;

#[derive(Debug, PartialEq)]
pub enum Literal {
Int(u64),
Float(f64),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Pow,
}

#[derive(Debug, PartialEq)]
pub enum UnaryOp {
Neg,
Sqrt,
Sin,
Cos,
Tan,
Csc,
Sec,
Cot,
InvSin,
InvCos,
InvTan,
InvCsc,
InvSec,
InvCot,
Fac,
}

#[derive(Debug, PartialEq)]
pub enum Node {
Lit(Literal),
Var(String),
FnCall { name: String, expr: Box<Node> },
GlobalVar(String),
BinOp { lhs: Box<Node>, op: BinaryOp, rhs: Box<Node> },
UnaryOp { expr: Box<Node>, op: UnaryOp },
}

impl Node {
pub fn eval(&self) -> Value {
match self {
Node::Lit(lit) => match lit {
Literal::Int(i) => Value::from_f64(*i as f64),
Literal::Float(f) => Value::from_f64(*f),
},

Node::BinOp { lhs, op, rhs } => {
let left = lhs.eval();
let right = rhs.eval();

// Ensure we can handle complex numbers
let (left_real, left_imag) = match left {
Value::Complex(real, imag) => (real, imag),
};
let (right_real, right_imag) = match right {
Value::Complex(real, imag) => (real, imag),
};

let result = match op {
BinaryOp::Add => Value::Complex(left_real + right_real, left_imag + right_imag),
BinaryOp::Sub => Value::Complex(left_real - right_real, left_imag - right_imag),
BinaryOp::Mul => Value::Complex(left_real * right_real - left_imag * right_imag, left_real * right_imag + left_imag * right_real),
BinaryOp::Div => {
let denom = right_real.powi(2) + right_imag.powi(2);
Value::Complex((left_real * right_real + left_imag * right_imag) / denom, (left_imag * right_real - left_real * right_imag) / denom)
}
BinaryOp::Pow => {

panic!("Power operation for complex numbers is not implemented");
}
};
result
}

Node::UnaryOp { expr, op } => {
let value = expr.eval();
let (real, imag) = match value {
Value::Complex(real, imag) => (real, imag),
};

let result = match op {
UnaryOp::Neg => Value::Complex(-real, -imag),
UnaryOp::Sqrt => {
let r = (real.powi(2) + imag.powi(2)).sqrt();
let theta = (imag / real).atan();
let sqrt_r = r.sqrt();
Value::Complex(sqrt_r * theta.cos(), sqrt_r * theta.sin())
}
UnaryOp::Sin => Value::Complex(real.sin() * imag.cosh(), real.cos() * imag.sinh()),
UnaryOp::Cos => Value::Complex(real.cos() * imag.cosh(), -real.sin() * imag.sinh()),
UnaryOp::Tan => {
let denom = (2.0 * real.cos()).cosh();
Value::Complex((2.0 * real.sin() * imag.cosh()) / denom, (2.0 * imag.sin() * real.cos()) / denom)
}
_ => panic!("Unary operation not implemented for complex numbers"),
};
result
}
Node::Var(_) => unimplemented!("Variable accses not implemented"),
Node::GlobalVar(_) => unimplemented!("Global variable accses not implemented")
Node::FnCall{..} => unimplemented!("Function calls not implemented")
}
}
}

fn factorial(value: f64) -> f64 {
if value < 0.0 || value.fract() != 0.0 {
panic!("Factorial is not defined for negative or non-integer values");
}
(1..=(value as u64)).fold(1u64, |acc, x| acc * x) as f64
}

#[cfg(test)]
mod tests {
use crate::{
ast::{BinaryOp, Literal, Node, UnaryOp},
value::Value,
};

macro_rules! eval_tests {
($($name:ident: $expected:expr => $expr:expr),* $(,)?) => {
$(
#[test]
fn $name() {
let result = $expr.eval();
assert_eq!(result, $expected);
}
)*
};
}

eval_tests! {
test_addition: Value::from_f64(7.0) => Node::BinOp {
lhs: Box::new(Node::Lit(Literal::Int(3))),
op: BinaryOp::Add,
rhs: Box::new(Node::Lit(Literal::Int(4))),
},
test_subtraction: Value::from_f64(1.0) => Node::BinOp {
lhs: Box::new(Node::Lit(Literal::Int(5))),
op: BinaryOp::Sub,
rhs: Box::new(Node::Lit(Literal::Int(4))),
},
test_multiplication: Value::from_f64(12.0) => Node::BinOp {
lhs: Box::new(Node::Lit(Literal::Int(3))),
op: BinaryOp::Mul,
rhs: Box::new(Node::Lit(Literal::Int(4))),
},
test_division: Value::from_f64(2.5) => Node::BinOp {
lhs: Box::new(Node::Lit(Literal::Float(5.0))),
op: BinaryOp::Div,
rhs: Box::new(Node::Lit(Literal::Int(2))),
},
test_negation: Value::from_f64(-3.0) => Node::UnaryOp {
val: Box::new(Node::Lit(Literal::Int(3))),
op: UnaryOp::Neg,
},
test_sqrt: Value::from_f64(2.0) => Node::UnaryOp {
val: Box::new(Node::Lit(Literal::Int(4))),
op: UnaryOp::Sqrt,
},
test_sine: Value::from_f64(0.0) => Node::UnaryOp {
val: Box::new(Node::Lit(Literal::Float(0.0))),
op: UnaryOp::Sin,
},
test_cosine: Value::from_f64(1.0) => Node::UnaryOp {
val: Box::new(Node::Lit(Literal::Float(0.0))),
op: UnaryOp::Cos,
},
test_power: Value::from_f64(8.0) => Node::BinOp {
lhs: Box::new(Node::Lit(Literal::Int(2))),
op: BinaryOp::Pow,
rhs: Box::new(Node::Lit(Literal::Int(3))),
},
}
}
40 changes: 19 additions & 21 deletions libraries/math-parser/src/grammer.pest
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,32 @@ program = _{ SOI ~ expr ~ EOI }
sub = { "-" } // Subtraction
mul = { "*" } // Multiplication
div = { "/" } // Division
mod = { "%" } // Modulu
pow = { "^" } // Exponantiation
paren = { "" } // Nothing operator, useally Multiplication
prefix = _{ neg | sqrt | sin | cos | tan | csc | sec | cot | invsin | invcos | invtan | invcsc | invsec| invcot }
neg = { "-" } // Negation
sqrt = {"sqrt"}
sin = {"sin"}
cos = {"cos"}
tan = {"tan"}
csc = {"csc"}
sec = {"sec"}
cot = {"cot"}
invsin = {"asin"}
invcos = {"cos"}
invtan = {"tan"}
invcsc = {"csc"}
invsec = {"sec"}
invcot = {"cot"}
sqrt = {"sqrt"}
sin = {"sin"}
cos = {"cos"}
tan = {"tan"}
csc = {"csc"}
sec = {"sec"}
cot = {"cot"}
invsin = {"asin"}
invcos = {"cos"}
invtan = {"tan"}
invcsc = {"csc"}
invsec = {"sec"}
invcot = {"cot"}
postfix = _{ fac }
fac = { "!" } // Factorial
primary = _{( "(" ~ expr ~ ")" ) | lit | var | global_var}
var = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
primary = _{ fn_call | ( "(" ~ expr ~ ")" ) | lit | var | global_var}
fn_call = { var ~ "(" ~ expr ~ ")"}
var = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
global_var= @{ "$" ~ var }
lit = _{ float | int }
float = @{ int ~ "." ~ int? ~ exp? | int ~ exp }
exp = _{ ^"e" ~ ("+" | "-")? ~ int }
exp = _{ ^"e" ~ ("+" | "-")? ~ int }
int = @{ ASCII_DIGIT+ }
constant = {pi | inf}
pi = { "pi" | "PI" | "π" }
i = {"i" | "I" }
inf = { "inf" | "INF" | "infinity" | "INFINITY" | "∞" }
tau = { "tau" | "TAU" | "τ"}

6 changes: 2 additions & 4 deletions libraries/math-parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod ast;
mod parser;

fn main() {
println!("h");
}
mod value;
64 changes: 15 additions & 49 deletions libraries/math-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use pest::{
use pest_derive::Parser;
use thiserror::Error;

use crate::ast::{BinaryOp, Literal, Node, UnaryOp};

#[derive(Parser)]
#[grammar = "./grammer.pest"] // Point to the grammar file
struct ExprParser;
Expand Down Expand Up @@ -39,49 +41,6 @@ lazy_static! {
};
}

#[derive(Debug, PartialEq)]
pub enum Literal {
Int(u64),
Float(f64),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Pow,
}

#[derive(Debug, PartialEq)]
pub enum UnaryOp {
Neg,
Sqrt,
Sin,
Cos,
Tan,
Csc,
Sec,
Cot,
InvSin,
InvCos,
InvTan,
InvCsc,
InvSec,
InvCot,
Fac,
}

#[derive(Debug, PartialEq)]
pub enum Node {
Lit(Literal),
Var(String),
GlobalVar(String),
BinOp { lhs: Box<Node>, op: BinaryOp, rhs: Box<Node> },
UnaryOp { val: Box<Node>, op: UnaryOp },
}

#[derive(Error, Debug)]
pub enum ParseError {
#[error("ParseIntError: {0}")]
Expand Down Expand Up @@ -110,14 +69,22 @@ fn parse_expr(pairs: Pairs<Rule>) -> Result<Node, ParseError> {

Node::Var(name)
}
Rule::fn_call => {
let mut pairs = primary.into_inner();
let name = pairs.next().expect("fn_call always has 2 children").as_str().to_string();

Node::FnCall {
name,
expr: Box::new(parse_expr(pairs.next().expect("fn_call always has two children").into_inner())?),
}
}
Rule::global_var => {
let name = primary.as_str().split_at(1).1.to_string();

Node::GlobalVar(name)
}
Rule::expr => parse_expr(primary.into_inner())?,
Rule::float => Node::Lit(Literal::Float(primary.as_str().parse::<f64>()?)),

rule => unreachable!("Expr::parse expected int, expr, ident, found {:?}", rule),
})
})
Expand Down Expand Up @@ -148,6 +115,7 @@ fn parse_expr(pairs: Pairs<Rule>) -> Result<Node, ParseError> {
val: Box::new(lhs?),
op: match op.as_rule() {
Rule::fac => UnaryOp::Fac,

_ => unreachable!(),
},
})
Expand Down Expand Up @@ -215,11 +183,9 @@ mod tests {
val: Box::new(Node::Lit(Literal::Int(16))),
op: UnaryOp::Sqrt,
},
test_parse_sqr_ident: "sqr(16)" => Node::BinOp {

lhs: Box::new(Node::Var("sqr".to_string())),
op: BinaryOp::Mul,
rhs: Box::new(Node::Lit(Literal::Int(16)) )
test_parse_sqr_ident: "sqr(16)" => Node::FnCall {
name:"sqr".to_string(),
expr: Box::new(Node::Lit(Literal::Int(16)))
},
test_parse_global_var: "$variable_one1 - 11" => Node::BinOp {

Expand Down
10 changes: 10 additions & 0 deletions libraries/math-parser/src/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[derive(Debug, PartialEq)]
pub enum Value {
Complex(f64, f64),
}

impl Value {
pub fn from_f64(x: f64) -> Self {
Self::Complex(x, 0.0)
}
}

0 comments on commit b607c2f

Please sign in to comment.