Created KalkNum and moved output formatting from kalk_cli to kalk

This commit is contained in:
PaddiM8 2020-12-13 01:27:55 +01:00
parent f8b86d31f7
commit f29104e489
8 changed files with 126 additions and 63 deletions

5
Cargo.lock generated
View File

@ -153,7 +153,7 @@ dependencies = [
[[package]] [[package]]
name = "kalk" name = "kalk"
version = "0.2.3" version = "1.2.3"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"regex", "regex",
@ -163,13 +163,12 @@ dependencies = [
[[package]] [[package]]
name = "kalk_cli" name = "kalk_cli"
version = "0.3.4" version = "0.3.5"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"kalk", "kalk",
"lazy_static", "lazy_static",
"regex", "regex",
"rug",
"rustyline", "rustyline",
"winres", "winres",
] ]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kalk" name = "kalk"
version = "0.2.3" version = "1.2.3"
authors = ["PaddiM8"] authors = ["PaddiM8"]
edition = "2018" edition = "2018"
readme = "README.md" readme = "README.md"

View File

@ -1,4 +1,5 @@
use crate::ast::{Expr, Stmt}; use crate::ast::{Expr, Stmt};
use crate::kalk_num::KalkNum;
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::CalcError; use crate::parser::CalcError;
use crate::parser::DECL_UNIT; use crate::parser::DECL_UNIT;
@ -22,10 +23,7 @@ impl<'a> Context<'a> {
} }
} }
pub fn interpret( pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<KalkNum>, CalcError> {
&mut self,
statements: Vec<Stmt>,
) -> Result<Option<(Float, String)>, CalcError> {
for (i, stmt) in statements.iter().enumerate() { for (i, stmt) in statements.iter().enumerate() {
let (value, unit) = eval_stmt(self, stmt)?; let (value, unit) = eval_stmt(self, stmt)?;
@ -47,7 +45,7 @@ impl<'a> Context<'a> {
if i == statements.len() - 1 { if i == statements.len() - 1 {
if let Stmt::Expr(_) = stmt { 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<Option<(Float, String)>, CalcError> { fn interpret_with_unit(stmt: Stmt) -> Result<Option<KalkNum>, CalcError> {
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table symbol_table
.insert(DEG_RAD_UNIT.clone()) .insert(DEG_RAD_UNIT.clone())
@ -381,15 +379,15 @@ mod tests {
context.interpret(vec![stmt]) context.interpret(vec![stmt])
} }
fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> { fn interpret(stmt: Stmt) -> Result<Option<KalkNum>, CalcError> {
if let Some((result, _)) = interpret_with_unit(stmt)? { if let Some(result) = interpret_with_unit(stmt)? {
Ok(Some(result)) Ok(Some(result))
} else { } else {
Ok(None) Ok(None)
} }
} }
fn cmp(x: Float, y: f64) -> bool { fn cmp(x: KalkNum, y: f64) -> bool {
println!("{} = {}", x.to_f64(), y); println!("{} = {}", x.to_f64(), y);
(x.to_f64() - y).abs() < 0.0001 (x.to_f64() - y).abs() < 0.0001
} }
@ -463,12 +461,11 @@ mod tests {
rad_context rad_context
.interpret(vec![implicit.clone()]) .interpret(vec![implicit.clone()])
.unwrap() .unwrap()
.unwrap() .unwrap(),
.0,
0.84147098 0.84147098
)); ));
assert!(cmp( assert!(cmp(
deg_context.interpret(vec![implicit]).unwrap().unwrap().0, deg_context.interpret(vec![implicit]).unwrap().unwrap(),
0.01745240 0.01745240
)); ));
} }
@ -482,7 +479,7 @@ mod tests {
symbol_table.insert(var_decl("x", literal("1"))); symbol_table.insert(var_decl("x", literal("1")));
let mut context = Context::new(&mut symbol_table, "rad", PRECISION); 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] #[test]
@ -519,7 +516,7 @@ mod tests {
)); ));
let mut context = Context::new(&mut symbol_table, "rad", PRECISION); 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] #[test]

100
kalk/src/kalk_num.rs Normal file
View File

@ -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<String> for ScientificNotation {
fn into(self) -> String {
self.to_string()
}
}
impl Into<f64> for KalkNum {
fn into(self) -> f64 {
self.to_f64()
}
}
impl Into<String> for KalkNum {
fn into(self) -> String {
self.to_string()
}
}

View File

@ -1,6 +1,7 @@
pub mod ast; pub mod ast;
mod interpreter; mod interpreter;
mod inverter; mod inverter;
pub mod kalk_num;
mod lexer; mod lexer;
pub mod parser; pub mod parser;
mod prelude; mod prelude;

View File

@ -1,10 +1,10 @@
use crate::kalk_num::KalkNum;
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
interpreter, interpreter,
lexer::{Lexer, Token, TokenKind}, lexer::{Lexer, Token, TokenKind},
symbol_table::SymbolTable, symbol_table::SymbolTable,
}; };
use rug::Float;
pub const DECL_UNIT: &'static str = ".u"; pub const DECL_UNIT: &'static str = ".u";
pub const DEFAULT_ANGLE_UNIT: &'static str = "rad"; pub const DEFAULT_ANGLE_UNIT: &'static str = "rad";
@ -83,7 +83,7 @@ pub fn eval(
context: &mut Context, context: &mut Context,
input: &str, input: &str,
precision: u32, precision: u32,
) -> Result<Option<(Float, String)>, CalcError> { ) -> Result<Option<KalkNum>, CalcError> {
let statements = parse(context, input)?; let statements = parse(context, input)?;
let mut interpreter = let mut interpreter =

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kalk_cli" name = "kalk_cli"
version = "0.3.4" version = "0.3.5"
authors = ["PaddiM8"] authors = ["PaddiM8"]
edition = "2018" edition = "2018"
readme = "../README.md" readme = "../README.md"
@ -16,11 +16,10 @@ path = "src/main.rs"
name = "kalk" name = "kalk"
[dependencies] [dependencies]
kalk = { path = "../kalk", version = "^0.2.3" } kalk = { path = "../kalk", version = "^1.2.3" }
rustyline = "7.0.0" rustyline = "7.0.0"
ansi_term = "0.12" ansi_term = "0.12"
regex = "1" regex = "1"
rug = { version = "1.11.0", features = ["float"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
[target.'cfg(windows)'.build-dependencies] [target.'cfg(windows)'.build-dependencies]

View File

@ -3,41 +3,15 @@ use kalk::parser::{self, CalcError, CalcError::*};
pub fn eval(parser: &mut parser::Context, input: &str) { pub fn eval(parser: &mut parser::Context, input: &str) {
match parser::eval(parser, input, 53) { match parser::eval(parser, input, 53) {
Ok(Some((result, unit))) => { Ok(Some(result)) => {
let (_, digits, exp_option) = result.to_sign_string_exp(10, None); let sci_notation = result.to_scientific_notation();
let exp = if let Some(exp) = exp_option { exp } else { 0 }; let result_str = if sci_notation.exponent > 8 || sci_notation.exponent < -6 {
sci_notation.to_string()
if result.is_infinite() {
print_err("Too big to process.");
} else { } else {
let use_sci_notation = exp > 8 || exp < -6; result.to_string()
};
if !use_sci_notation { println!("{} {}", result_str, result.get_unit());
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<char> = digits
.trim_end_matches('0')
.trim_end_matches('.')
.chars()
.collect();
chars.insert(comma_pos, '.');
chars.into_iter().collect::<String>()
};
println!("{}{}*10^{} {}", sign, num, exp - 1, unit);
}
} }
Ok(None) => print!(""), Ok(None) => print!(""),
Err(err) => print_calc_err(err), Err(err) => print_calc_err(err),
@ -70,10 +44,3 @@ fn print_calc_err(err: CalcError) {
Unknown => format!("Unknown error."), 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()
}