diff --git a/Cargo.toml b/Cargo.toml index 7f1b8eb..2c67606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,11 @@ [package] name = "cpc" version = "0.1.0" -authors = ["Kasper "] +description = "evaluates math expressions, with support for units and conversion between units" +readme = 'README.md' +authors = ["Kasper Henningsen"] edition = "2018" +repository = "https://github.com/probablykasper/cpc" [dependencies] decimal = "2.0.4" diff --git a/README.md b/README.md index 4ff5b5f..045ea95 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,23 @@ calculation + conversion cpc parses and evaluates strings of math, with support for units and conversion. 128-bit decimal floating points are used for high accuracy. +cpc lets you mix units, so for example `1 km - 1m` results in `Number { value: 999, unit: Meter }`. + +## Usage +```rs +use cpc::{eval, Unit::*} + +match eval("3m + 1cm", true, Celcius) { + Ok(answer) => { + // answer: Number { value: 301, unit: Unit::cm } + println!("Evaluated value: {} {:?}", answer.value, answer.unit) + }, + Err(e) => { + println!(e) + } +} +``` + ## Examples ``` 3 + 4 * 2 @@ -20,7 +37,6 @@ cpc parses and evaluates strings of math, with support for units and conversion. round(sqrt(2)^4)! liters 10% of abs(sin(pi)) horsepower to watts - ``` ## Supported unit types @@ -41,7 +57,10 @@ round(sqrt(2)^4)! liters cpc Uses 128-bit Decimal Floating Point (d128) numbers instead of Binary Coded Decimals for better accuracy. The result cpc gives will still not always be 100% accurate. I would recommend rounding the result to 20 decimals or less. ## Performance -In my case, I can expect `eval()` to take 100-200ms, and this scales pretty alright. However, putting numbers with a lot of digits into functions result in pretty poor performance. `log(e)` is one of the worst, and takes 500ms. +In my case, I can expect `eval()` to take 100-200ms, and this scales pretty alright. However, putting numbers with a lot of digits into functions result in pretty poor performance. `log(e)` is one of the worst, and takes 500ms for me. + +## Errors +cpc returns `Result`s with basic strings as errors. Just to be safe, you may want to handle panics (You can do that using `std::panic::catch_unwind`). # Dev Instructions @@ -54,6 +73,11 @@ Run cpc with a CLI argument as input: cargo run -- '100ms to s' ``` +Run with debugging, which shows some extra logs: +``` +cargo run -- '100ms to s' --debug +``` + Run tests: ``` cargo test @@ -99,7 +123,7 @@ match string { // ... ``` -# Potential Improvements +## Potential Improvements ### General - Support for math in `6'4"` syntax, like `3'+2'4"`. Currently needs to be written like `3'+3'+4"` - The functions in units.rs have a lot of manual if statements. This could probably be replaced with a pretty advanced macro. diff --git a/src/main.rs b/src/main.rs index afd54d1..90ae6d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,14 +91,27 @@ mod lookup; fn main() { use std::env; let args: Vec = env::args().collect(); + let mut debug = false; + if args.iter().any(|i| i=="--debug") { + debug = true; + } if args.len() >= 2 { - eval(&args[1], true, Unit::Celcius); + match eval(&args[1], true, Unit::Celcius, debug) { + Ok(answer) => { + if !debug { + println!("Evaluated value: {} {:?}", answer.value, answer.unit) + } + }, + Err(e) => { + println!("{}", e) + }, + } } else { println!("No argument supplied"); } } -pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit) { +pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, debug: bool) -> Result { let lex_start = Instant::now(); @@ -116,24 +129,26 @@ pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit) { Ok(answer) => { let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32; - println!("Lexed TokenVector: {:?}", tokens); - println!("Parsed AstNode: {:#?}", ast); - - println!("Evaluated value: {} {:?}", answer.value, answer.unit); - - println!("\u{23f1} {:.3}ms lexing", lex_time/1000.0/1000.0); - println!("\u{23f1} {:.3}ms parsing", parse_time/1000.0/1000.0); - println!("\u{23f1} {:.3}ms evaluation", eval_time/1000.0/1000.0); + if debug == true { + println!("Lexed TokenVector: {:?}", tokens); + println!("Parsed AstNode: {:#?}", ast); + println!("Evaluated value: {} {:?}", answer.value, answer.unit); + println!("\u{23f1} {:.3}ms lexing", lex_time/1000.0/1000.0); + println!("\u{23f1} {:.3}ms parsing", parse_time/1000.0/1000.0); + println!("\u{23f1} {:.3}ms evaluation", eval_time/1000.0/1000.0); + } + + return Ok(answer) }, - Err(e) => println!("Eval error: {}", e), + Err(e) => Err(format!("Eval error: {}", e)), } }, - Err(e) => println!("Parsing error: {}", e), + Err(e) => Err(format!("Parsing error: {}", e)), } }, - Err(e) => println!("Lexing error: {}", e), + Err(e) => Err(format!("Lexing error: {}", e)), } }