Compare commits
4 changed files with 16 additions and 42 deletions
|
|
@ -2,7 +2,9 @@ use rug::Float;
|
||||||
|
|
||||||
use crate::nodes::{self, Error};
|
use crate::nodes::{self, Error};
|
||||||
|
|
||||||
pub fn pretty_eval(mut input: String, max_digits: usize) -> Result<String, Error> {
|
pub const MAX_DIGITS: usize = 10;
|
||||||
|
|
||||||
|
pub fn pretty_eval(mut input: String) -> Result<String, Error> {
|
||||||
// no need to remove the characters here,
|
// no need to remove the characters here,
|
||||||
// as we ignore the them in all other logic already
|
// as we ignore the them in all other logic already
|
||||||
let should_add_back_alignment = input.contains('&');
|
let should_add_back_alignment = input.contains('&');
|
||||||
|
|
@ -19,7 +21,7 @@ pub fn pretty_eval(mut input: String, max_digits: usize) -> Result<String, Error
|
||||||
let result = nodes::eval(&input)?;
|
let result = nodes::eval(&input)?;
|
||||||
let display_digits =
|
let display_digits =
|
||||||
get_significant_digits(&result)
|
get_significant_digits(&result)
|
||||||
.min(max_digits)
|
.min(MAX_DIGITS)
|
||||||
.max(if result >= 1000 || result < 1 {
|
.max(if result >= 1000 || result < 1 {
|
||||||
1
|
1
|
||||||
} else if result >= 100 {
|
} else if result >= 100 {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match formatting::pretty_eval(input, 10) {
|
match formatting::pretty_eval(input) {
|
||||||
Ok(s) => println!("{s}"),
|
Ok(s) => println!("{s}"),
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
use rug::{Assign, Float};
|
use rug::Float;
|
||||||
|
|
||||||
mod eval;
|
mod eval;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
@ -53,14 +53,6 @@ impl Display for Node {
|
||||||
Debug::fmt(&self, f)
|
Debug::fmt(&self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> From<T> for Node
|
|
||||||
where
|
|
||||||
Float: Assign<T>,
|
|
||||||
{
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self::Number(Float::with_val(PREC, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
||||||
pub enum Operator {
|
pub enum Operator {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,11 @@ use crate::nodes::{Function, Node, Nodes, Operator, PREC};
|
||||||
impl Node {
|
impl Node {
|
||||||
fn from_name(s: &str) -> Result<Self, String> {
|
fn from_name(s: &str) -> Result<Self, String> {
|
||||||
Ok(match s {
|
Ok(match s {
|
||||||
"c" => Self::from(299_792_458),
|
"c" => Self::Number(Float::with_val(PREC, 299_792_458)),
|
||||||
"A" => Self::from(6.02214076e23),
|
"A" => Self::Number(Float::with_val(PREC, 6.02214076e23)),
|
||||||
"pi" => Self::from(f64::consts::PI),
|
"pi" => Self::Number(Float::with_val(PREC, f64::consts::PI)),
|
||||||
// Planck's constant, in J*s (joule-seconds)
|
// Planck's constant, in J*s (joule-seconds)
|
||||||
"h" => Self::from(6.62607015e-34),
|
"h" => Self::Number(Float::with_val(PREC, 6.62607015e-34)),
|
||||||
"e" => Self::from(f64::consts::E),
|
|
||||||
_ => Self::Function(s.parse()?),
|
_ => Self::Function(s.parse()?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -71,14 +70,10 @@ impl FromStr for Nodes {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
/// A superset of `char::is_numeric`
|
/// A superset of `char::is_numeric`
|
||||||
/// that includes other characters parseable into numbers,
|
/// that includes other characters parseable into numbers,
|
||||||
/// now only `.` (for decimals)
|
/// such as `.` (for decimal) and `e` for scientific notation.
|
||||||
///
|
|
||||||
/// since including `e` makes it not parsable as a constant,
|
|
||||||
/// this function effectively only checks if the character can *start* a number
|
|
||||||
/// (since a number cannot start with `e`)
|
|
||||||
fn is_number_char(c: char) -> bool {
|
fn is_number_char(c: char) -> bool {
|
||||||
match c {
|
match c {
|
||||||
'.' => true,
|
'.' | 'e' => true,
|
||||||
c => c.is_numeric(),
|
c => c.is_numeric(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,10 +114,8 @@ impl FromStr for Nodes {
|
||||||
}
|
}
|
||||||
ParsingState::ReadingNumber(start_index) => {
|
ParsingState::ReadingNumber(start_index) => {
|
||||||
if !is_number_char(c)
|
if !is_number_char(c)
|
||||||
&& !(c == 'e' && filtered_string.chars().nth(i + 1).is_some_and(char::is_numeric))
|
// don't stop reading number on a special case, eg "1e-1"
|
||||||
// don't stop reading number on special case eg "1e-1"
|
&& !(filtered_string.chars().nth(i - 1) == Some('e') && c == '-')
|
||||||
&& !(c == 'e' && filtered_string.chars().nth(i + 1).is_some_and(|c| c == '-'))
|
|
||||||
&& !(c == '-' && filtered_string.chars().nth(i - 1).is_some_and(|c| c == 'e'))
|
|
||||||
{
|
{
|
||||||
nodes.push(Node::Number(
|
nodes.push(Node::Number(
|
||||||
Float::parse(&filtered_string[start_index..i])
|
Float::parse(&filtered_string[start_index..i])
|
||||||
|
|
@ -203,23 +196,10 @@ fn test() {
|
||||||
&[
|
&[
|
||||||
Node::Function(Function::Sine),
|
Node::Function(Function::Sine),
|
||||||
Node::ParenthesisStart,
|
Node::ParenthesisStart,
|
||||||
Node::from(2),
|
Node::Number(Float::with_val(PREC, 2)),
|
||||||
Node::ParenthesisEnd,
|
Node::ParenthesisEnd,
|
||||||
Node::Operator(Operator::Plus),
|
Node::Operator(Operator::Plus),
|
||||||
Node::from(1),
|
Node::Number(Float::with_val(PREC, 1))
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_e() {
|
|
||||||
assert_eq!(
|
|
||||||
Nodes::from_str("5e * 5").unwrap().as_ref(),
|
|
||||||
&[
|
|
||||||
Node::from(5),
|
|
||||||
Node::Operator(Operator::Mult),
|
|
||||||
Node::from(f64::consts::E),
|
|
||||||
Node::Operator(Operator::Mult),
|
|
||||||
Node::from(5),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue