feat: nodes to num
co-authored by electria
This commit is contained in:
parent
e27438a244
commit
06aaf8cb8a
1 changed files with 182 additions and 1 deletions
183
src/parse.rs
183
src/parse.rs
|
|
@ -1,7 +1,11 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rug::{Float, float::ParseFloatError, ops::CompleteRound};
|
use rug::{
|
||||||
|
Float,
|
||||||
|
float::ParseFloatError,
|
||||||
|
ops::{CompleteRound, Pow},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
enum Error {
|
enum Error {
|
||||||
|
|
@ -28,6 +32,9 @@ enum Node {
|
||||||
enum Operator {
|
enum Operator {
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
|
Mult,
|
||||||
|
Div,
|
||||||
|
Exp,
|
||||||
}
|
}
|
||||||
impl TryFrom<char> for Operator {
|
impl TryFrom<char> for Operator {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
@ -35,6 +42,9 @@ impl TryFrom<char> for Operator {
|
||||||
match value {
|
match value {
|
||||||
'+' => Ok(Self::Plus),
|
'+' => Ok(Self::Plus),
|
||||||
'-' => Ok(Self::Minus),
|
'-' => Ok(Self::Minus),
|
||||||
|
'*' => Ok(Self::Mult),
|
||||||
|
'/' => Ok(Self::Div),
|
||||||
|
'^' => Ok(Self::Exp),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,6 +158,177 @@ fn nodes_from_str(s: &str) -> Result<Vec<Node>, Error> {
|
||||||
Ok(nodes)
|
Ok(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collapse_toplevel(nodes: &mut Vec<Node>, mut end: usize) {
|
||||||
|
let start = nodes[0..end]
|
||||||
|
.iter()
|
||||||
|
.rposition(|n| *n == Node::ParenthesisStart)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Function(Function::Sine))
|
||||||
|
{
|
||||||
|
match &mut nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n.sin_mut(),
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.remove(operator);
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Function(Function::Cosine))
|
||||||
|
{
|
||||||
|
match &mut nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n.cos_mut(),
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.remove(operator);
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Operator(Operator::Exp))
|
||||||
|
{
|
||||||
|
let lhs = match &nodes[start..end][operator - 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
let rhs = match &nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = lhs.pow(rhs).complete(PREC);
|
||||||
|
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
end -= 2;
|
||||||
|
nodes[operator - 1] = Node::Number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Operator(Operator::Mult))
|
||||||
|
{
|
||||||
|
let lhs = match &nodes[start..end][operator - 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
let rhs = match &nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = (lhs * rhs).complete(PREC);
|
||||||
|
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
end -= 2;
|
||||||
|
nodes[operator - 1] = Node::Number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Operator(Operator::Div))
|
||||||
|
{
|
||||||
|
let lhs = match &nodes[start..end][operator - 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
let rhs = match &nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = (lhs / rhs).complete(PREC);
|
||||||
|
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
end -= 2;
|
||||||
|
nodes[operator - 1] = Node::Number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Operator(Operator::Plus))
|
||||||
|
{
|
||||||
|
let lhs = match &nodes[start..end][operator - 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
let rhs = match &nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = (lhs + rhs).complete(PREC);
|
||||||
|
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
end -= 2;
|
||||||
|
nodes[operator - 1] = Node::Number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(operator) = nodes[start..end]
|
||||||
|
.iter()
|
||||||
|
.position(|n| *n == Node::Operator(Operator::Minus))
|
||||||
|
{
|
||||||
|
let lhs = match &nodes[start..end][operator - 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
let rhs = match &nodes[start..end][operator + 1] {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("operator is missing an operand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = (lhs - rhs).complete(PREC);
|
||||||
|
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
nodes.remove(operator - 1);
|
||||||
|
end -= 2;
|
||||||
|
nodes[operator - 1] = Node::Number(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodes.len() > 1 {
|
||||||
|
nodes.remove(end);
|
||||||
|
nodes.remove(start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nodes_to_num(mut nodes: Vec<Node>) -> Float {
|
||||||
|
while let Some(end) = nodes.iter().position(|n| *n == Node::ParenthesisEnd) {
|
||||||
|
collapse_toplevel(&mut nodes, end);
|
||||||
|
}
|
||||||
|
let len = nodes.len();
|
||||||
|
collapse_toplevel(&mut nodes, len);
|
||||||
|
|
||||||
|
match nodes.pop().unwrap() {
|
||||||
|
Node::Number(n) => n,
|
||||||
|
_ => panic!("failed to collapse nodes: {nodes:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nodes_to_num() {
|
||||||
|
assert_eq!(
|
||||||
|
nodes_to_num(vec![
|
||||||
|
Node::Function(Function::Sine),
|
||||||
|
Node::ParenthesisStart,
|
||||||
|
Node::Number(Float::with_val(PREC, 2)),
|
||||||
|
Node::ParenthesisEnd,
|
||||||
|
Node::Operator(Operator::Plus),
|
||||||
|
Node::Number(Float::with_val(PREC, 1))
|
||||||
|
])
|
||||||
|
.to_f64_round(rug::float::Round::Nearest),
|
||||||
|
1.9092974268256817,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nodes_from_str() {
|
fn test_nodes_from_str() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue