From 0284475b2cec4e68e01608a8eeaea2027deba40f Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 13 Dec 2020 01:27:55 +0100 Subject: [PATCH] Created KalkNum and moved output formatting from kalk_cli to kalk --- Cargo.lock | 5 +- kalk/Cargo.toml | 2 +- kalk/src/interpreter.rs | 25 +++++----- kalk/src/kalk_num.rs | 100 ++++++++++++++++++++++++++++++++++++++++ kalk/src/lib.rs | 1 + kalk/src/parser.rs | 4 +- kalk_cli/Cargo.toml | 5 +- kalk_cli/src/output.rs | 47 +++---------------- 8 files changed, 126 insertions(+), 63 deletions(-) create mode 100644 kalk/src/kalk_num.rs diff --git a/Cargo.lock b/Cargo.lock index 5037ff9..73b74b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "kalk" -version = "0.2.3" +version = "1.2.3" dependencies = [ "lazy_static", "regex", @@ -163,13 +163,12 @@ dependencies = [ [[package]] name = "kalk_cli" -version = "0.3.4" +version = "0.3.5" dependencies = [ "ansi_term", "kalk", "lazy_static", "regex", - "rug", "rustyline", "winres", ] diff --git a/kalk/Cargo.toml b/kalk/Cargo.toml index 1dc63e9..218887a 100644 --- a/kalk/Cargo.toml +++ b/kalk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kalk" -version = "0.2.3" +version = "1.2.3" authors = ["PaddiM8"] edition = "2018" readme = "README.md" diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 37256d2..e704036 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -1,4 +1,5 @@ use crate::ast::{Expr, Stmt}; +use crate::kalk_num::KalkNum; use crate::lexer::TokenKind; use crate::parser::CalcError; use crate::parser::DECL_UNIT; @@ -22,10 +23,7 @@ impl<'a> Context<'a> { } } - pub fn interpret( - &mut self, - statements: Vec, - ) -> Result, CalcError> { + pub fn interpret(&mut self, statements: Vec) -> Result, CalcError> { for (i, stmt) in statements.iter().enumerate() { let (value, unit) = eval_stmt(self, stmt)?; @@ -47,7 +45,7 @@ impl<'a> Context<'a> { if i == statements.len() - 1 { if let Stmt::Expr(_) = stmt { - return Ok(Some((value, unit))); + return Ok(Some(KalkNum::new(value, &unit))); } } } @@ -371,7 +369,7 @@ mod tests { ); } - fn interpret_with_unit(stmt: Stmt) -> Result, CalcError> { + fn interpret_with_unit(stmt: Stmt) -> Result, CalcError> { let mut symbol_table = SymbolTable::new(); symbol_table .insert(DEG_RAD_UNIT.clone()) @@ -381,15 +379,15 @@ mod tests { context.interpret(vec![stmt]) } - fn interpret(stmt: Stmt) -> Result, CalcError> { - if let Some((result, _)) = interpret_with_unit(stmt)? { + fn interpret(stmt: Stmt) -> Result, CalcError> { + if let Some(result) = interpret_with_unit(stmt)? { Ok(Some(result)) } else { Ok(None) } } - fn cmp(x: Float, y: f64) -> bool { + fn cmp(x: KalkNum, y: f64) -> bool { println!("{} = {}", x.to_f64(), y); (x.to_f64() - y).abs() < 0.0001 } @@ -463,12 +461,11 @@ mod tests { rad_context .interpret(vec![implicit.clone()]) .unwrap() - .unwrap() - .0, + .unwrap(), 0.84147098 )); assert!(cmp( - deg_context.interpret(vec![implicit]).unwrap().unwrap().0, + deg_context.interpret(vec![implicit]).unwrap().unwrap(), 0.01745240 )); } @@ -482,7 +479,7 @@ mod tests { symbol_table.insert(var_decl("x", literal("1"))); let mut context = Context::new(&mut symbol_table, "rad", PRECISION); - assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap().0, 1); + assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1); } #[test] @@ -519,7 +516,7 @@ mod tests { )); let mut context = Context::new(&mut symbol_table, "rad", PRECISION); - assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap().0, 3); + assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3); } #[test] diff --git a/kalk/src/kalk_num.rs b/kalk/src/kalk_num.rs new file mode 100644 index 0000000..44394d0 --- /dev/null +++ b/kalk/src/kalk_num.rs @@ -0,0 +1,100 @@ +use rug::Float; + +pub struct KalkNum { + value: Float, + unit: String, +} + +pub struct ScientificNotation { + pub negative: bool, + pub digits: String, + pub exponent: i32, +} + +impl ScientificNotation { + pub fn to_string(&self) -> String { + let sign = if self.negative { "-" } else { "" }; + let mut digits_and_mul = if self.digits == "1" { + String::new() + } else { + format!("{}*", &self.digits) + }; + + if self.digits.len() > 1 { + digits_and_mul.insert(1usize, '.'); + } + + format!("{}{}10^{}", sign, digits_and_mul, self.exponent.to_string()) + } +} + +impl KalkNum { + pub fn new(value: Float, unit: &str) -> Self { + Self { + value, + unit: unit.to_string(), + } + } + + pub fn to_f64(&self) -> f64 { + self.value.to_f64_round(rug::float::Round::Nearest) + } + + pub fn to_string(&self) -> String { + let as_str = self.to_f64().to_string(); + + if as_str.contains(".") { + as_str + .trim_end_matches('0') + .trim_end_matches('.') + .to_string() + } else { + as_str + } + } + + pub fn is_too_big(&self) -> bool { + self.value.is_infinite() + } + + pub fn to_string_with_unit(&self) -> String { + format!("{} {}", self.to_string(), self.unit) + } + + pub fn get_unit(self) -> String { + self.unit + } + + pub fn to_scientific_notation(&self) -> ScientificNotation { + let (neg, digits, exp_option) = + self.value + .to_sign_string_exp_round(10, None, rug::float::Round::Up); + + ScientificNotation { + negative: neg, + digits: digits + .trim_end_matches('0') + .trim_end_matches('.') + .to_string(), + exponent: if let Some(exp) = exp_option { exp } else { 0 }, + } + } +} + +impl Into for ScientificNotation { + fn into(self) -> String { + self.to_string() + } +} + +impl Into for KalkNum { + fn into(self) -> f64 { + self.to_f64() + } +} + +impl Into for KalkNum { + fn into(self) -> String { + self.to_string() + } +} diff --git a/kalk/src/lib.rs b/kalk/src/lib.rs index 79e3497..f100d58 100644 --- a/kalk/src/lib.rs +++ b/kalk/src/lib.rs @@ -1,6 +1,7 @@ pub mod ast; mod interpreter; mod inverter; +pub mod kalk_num; mod lexer; pub mod parser; mod prelude; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index f897ce2..37929fb 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -1,10 +1,10 @@ +use crate::kalk_num::KalkNum; use crate::{ ast::{Expr, Stmt}, interpreter, lexer::{Lexer, Token, TokenKind}, symbol_table::SymbolTable, }; -use rug::Float; pub const DECL_UNIT: &'static str = ".u"; pub const DEFAULT_ANGLE_UNIT: &'static str = "rad"; @@ -83,7 +83,7 @@ pub fn eval( context: &mut Context, input: &str, precision: u32, -) -> Result, CalcError> { +) -> Result, CalcError> { let statements = parse(context, input)?; let mut interpreter = diff --git a/kalk_cli/Cargo.toml b/kalk_cli/Cargo.toml index b3b1934..6017b1f 100644 --- a/kalk_cli/Cargo.toml +++ b/kalk_cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kalk_cli" -version = "0.3.4" +version = "0.3.5" authors = ["PaddiM8"] edition = "2018" readme = "../README.md" @@ -16,11 +16,10 @@ path = "src/main.rs" name = "kalk" [dependencies] -kalk = { path = "../kalk", version = "^0.2.3" } +kalk = { path = "../kalk", version = "^1.2.3" } rustyline = "7.0.0" ansi_term = "0.12" regex = "1" -rug = { version = "1.11.0", features = ["float"] } lazy_static = "1.4.0" [target.'cfg(windows)'.build-dependencies] diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index e9d91da..52dda88 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -3,41 +3,15 @@ use kalk::parser::{self, CalcError, CalcError::*}; pub fn eval(parser: &mut parser::Context, input: &str) { match parser::eval(parser, input, 53) { - Ok(Some((result, unit))) => { - let (_, digits, exp_option) = result.to_sign_string_exp(10, None); - let exp = if let Some(exp) = exp_option { exp } else { 0 }; - - if result.is_infinite() { - print_err("Too big to process."); + Ok(Some(result)) => { + let sci_notation = result.to_scientific_notation(); + let result_str = if sci_notation.exponent > 8 || sci_notation.exponent < -6 { + sci_notation.to_string() } else { - let use_sci_notation = exp > 8 || exp < -6; + result.to_string() + }; - if !use_sci_notation { - println!("{}", round_value(result)); - return; - } - - let comma_pos = if use_sci_notation { 1 } else { exp as usize }; - let sign = if result >= 0 { "" } else { "-" }; - - let num = if exp <= 0 { - // 0 < x < 1 - format!("0.{}{}", "0".repeat(exp.abs() as usize), digits) - .trim_end_matches('0') - .to_string() - } else { - // Insert the comma if there are supposed to be decimals. - let mut chars: Vec = digits - .trim_end_matches('0') - .trim_end_matches('.') - .chars() - .collect(); - chars.insert(comma_pos, '.'); - chars.into_iter().collect::() - }; - - println!("{}{}*10^{} {}", sign, num, exp - 1, unit); - } + println!("{} {}", result_str, result.get_unit()); } Ok(None) => print!(""), Err(err) => print_calc_err(err), @@ -70,10 +44,3 @@ fn print_calc_err(err: CalcError) { Unknown => format!("Unknown error."), }); } - -fn round_value(value: rug::Float) -> String { - format!("{:.10}", value.to_f64_round(rug::float::Round::Down)) - .trim_end_matches('0') - .trim_end_matches('.') - .to_string() -}