Added --debug flag, eval returns Result

This commit is contained in:
Kasper 2020-08-20 06:49:10 +02:00
parent 55e7ef0a04
commit 5830904a45
3 changed files with 59 additions and 17 deletions

View File

@ -1,8 +1,11 @@
[package] [package]
name = "cpc" name = "cpc"
version = "0.1.0" version = "0.1.0"
authors = ["Kasper <kasperkh.kh@gmail.com>"] description = "evaluates math expressions, with support for units and conversion between units"
readme = 'README.md'
authors = ["Kasper Henningsen"]
edition = "2018" edition = "2018"
repository = "https://github.com/probablykasper/cpc"
[dependencies] [dependencies]
decimal = "2.0.4" decimal = "2.0.4"

View File

@ -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 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 ## Examples
``` ```
3 + 4 * 2 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 round(sqrt(2)^4)! liters
10% of abs(sin(pi)) horsepower to watts 10% of abs(sin(pi)) horsepower to watts
``` ```
## Supported unit types ## 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. 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 ## 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 # Dev Instructions
@ -54,6 +73,11 @@ Run cpc with a CLI argument as input:
cargo run -- '100ms to s' cargo run -- '100ms to s'
``` ```
Run with debugging, which shows some extra logs:
```
cargo run -- '100ms to s' --debug
```
Run tests: Run tests:
``` ```
cargo test cargo test
@ -99,7 +123,7 @@ match string {
// ... // ...
``` ```
# Potential Improvements ## Potential Improvements
### General ### General
- Support for math in `6'4"` syntax, like `3'+2'4"`. Currently needs to be written like `3'+3'+4"` - 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. - The functions in units.rs have a lot of manual if statements. This could probably be replaced with a pretty advanced macro.

View File

@ -91,14 +91,27 @@ mod lookup;
fn main() { fn main() {
use std::env; use std::env;
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let mut debug = false;
if args.iter().any(|i| i=="--debug") {
debug = true;
}
if args.len() >= 2 { 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 { } else {
println!("No argument supplied"); 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<units::Number, String> {
let lex_start = Instant::now(); let lex_start = Instant::now();
@ -116,24 +129,26 @@ pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit) {
Ok(answer) => { Ok(answer) => {
let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32; let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32;
println!("Lexed TokenVector: {:?}", tokens); if debug == true {
println!("Parsed AstNode: {:#?}", ast); println!("Lexed TokenVector: {:?}", tokens);
println!("Parsed AstNode: {:#?}", ast);
println!("Evaluated value: {} {:?}", answer.value, answer.unit); println!("Evaluated value: {} {:?}", answer.value, answer.unit);
println!("\u{23f1} {:.3}ms lexing", lex_time/1000.0/1000.0);
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 parsing", parse_time/1000.0/1000.0); println!("\u{23f1} {:.3}ms evaluation", eval_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)),
} }
} }