mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-06 13:39:17 +01:00
Created KalkNum and moved output formatting from kalk_cli to kalk
This commit is contained in:
parent
402c3e327c
commit
0284475b2c
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kalk"
|
||||
version = "0.2.3"
|
||||
version = "1.2.3"
|
||||
authors = ["PaddiM8"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
@ -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<Stmt>,
|
||||
) -> Result<Option<(Float, String)>, CalcError> {
|
||||
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<KalkNum>, 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<Option<(Float, String)>, CalcError> {
|
||||
fn interpret_with_unit(stmt: Stmt) -> Result<Option<KalkNum>, 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<Option<Float>, CalcError> {
|
||||
if let Some((result, _)) = interpret_with_unit(stmt)? {
|
||||
fn interpret(stmt: Stmt) -> Result<Option<KalkNum>, 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]
|
||||
|
100
kalk/src/kalk_num.rs
Normal file
100
kalk/src/kalk_num.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
pub mod ast;
|
||||
mod interpreter;
|
||||
mod inverter;
|
||||
pub mod kalk_num;
|
||||
mod lexer;
|
||||
pub mod parser;
|
||||
mod prelude;
|
||||
|
@ -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<Option<(Float, String)>, CalcError> {
|
||||
) -> Result<Option<KalkNum>, CalcError> {
|
||||
let statements = parse(context, input)?;
|
||||
|
||||
let mut interpreter =
|
||||
|
@ -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]
|
||||
|
@ -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<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);
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user