Documented most things, small code improvements

This commit is contained in:
Kasper 2020-08-20 21:59:44 +02:00
parent 56ea8380b7
commit a5aa85c1c0
7 changed files with 83 additions and 24 deletions

View File

@ -35,13 +35,13 @@ cpc = "1.*"
## API Usage
[docs.rs documentation](https://docs.rs/cpc)
```rs
use cpc::{eval, Unit};
```rust
use cpc::{eval};
use cpc::units::Unit;
match eval("3m + 1cm", true, Unit::Celcius, false) {
Ok(answer) => {
// answer: Number { value: 301, unit: Unit::cm }
// answer: Number { value: 301, unit: Unit::Centimeter }
println!("Evaluated value: {} {:?}", answer.value, answer.unit)
},
Err(e) => {

View File

@ -9,15 +9,22 @@ use crate::TextOperator::{To, Of};
use crate::FunctionIdentifier::*;
use crate::lookup::lookup_factorial;
/// Evaluate an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html)
pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
let answer = evaluate_node(ast)?;
Ok(answer)
}
/// Returns the factorial of a [`d128`](../decimal/struct.d128.html) up to `1000!` without doing any math
///
/// Factorials do not work with decimal numbers.
///
/// All return values of this function are hard-coded.
pub fn factorial(input: d128) -> d128 {
return lookup_factorial(input.into());
}
/// Returns the square root of a [`d128`](../decimal/struct.d128.html)
pub fn sqrt(input: d128) -> d128 {
let mut n = d128!(1);
let half = d128!(0.5);
@ -27,6 +34,7 @@ pub fn sqrt(input: d128) -> d128 {
return n
}
/// Returns the cube root of a [`d128`](../decimal/struct.d128.html)
pub fn cbrt(input: d128) -> d128 {
let mut n: d128 = input;
// hope that 20 iterations makes it accurate enough
@ -38,6 +46,7 @@ pub fn cbrt(input: d128) -> d128 {
return n
}
/// Returns the sine of a [`d128`](../decimal/struct.d128.html)
pub fn sin(mut input: d128) -> d128 {
let pi = d128!(3.141592653589793238462643383279503);
let pi2 = d128!(6.283185307179586476925286766559006);
@ -67,16 +76,19 @@ pub fn sin(mut input: d128) -> d128 {
}
/// Returns the cosine of a [`d128`](../decimal/struct.d128.html)
pub fn cos(input: d128) -> d128 {
let half_pi = d128!(1.570796326794896619231321691639751);
return sin(half_pi - input);
}
/// Returns the tangent of a [`d128`](../decimal/struct.d128.html)
pub fn tan(input: d128) -> d128 {
return sin(input) / cos(input);
}
pub fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
/// Evaluate an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html)
fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
let token = &ast_node.token;
let children = &ast_node.children;
match token {

View File

@ -1,11 +1,39 @@
use crate::units::Unit;
pub mod units;
//! 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 }.
//!
//! Check out the [list of supported units](units/enum.Unit.html)
//!
//! # Example usage
//! ```rust
//! 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)
//! }
//! }
//! ```
use std::time::{Instant};
use decimal::d128;
use crate::units::Unit;
/// Units, and functions you can use with them
pub mod units;
/// Turns a string into a [`TokenVector`](type.TokenVector.html)
pub mod lexer;
/// Turns a [`TokenVector`](type.TokenVector.html) into an [`AstNode`](struct.AstNode.html)
pub mod parser;
/// Turns an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html)
pub mod evaluator;
pub mod lookup;
mod lookup;
#[derive(Clone, Debug)]
/// A number with a `Unit`.
@ -128,7 +156,7 @@ pub enum Token {
/// Used by the parser only
LexerKeyword(LexerKeyword),
TextOperator(TextOperator),
/// Used by the parser only
/// The `-` symbol, specifically when used as `-5` and not `5-5`. Used by the parser only
Negative,
Unit(units::Unit),
}
@ -145,7 +173,7 @@ pub type TokenVector = Vec<Token>;
///
/// match eval("3m + 1cm", true, Unit::Celcius, false) {
/// Ok(answer) => {
/// // answer: Number { value: 301, unit: Unit::cm }
/// // answer: Number { value: 301, unit: Unit::Centimeter }
/// println!("Evaluated value: {} {:?}", answer.value, answer.unit)
/// },
/// Err(e) => {

View File

@ -1,5 +1,6 @@
use decimal::d128;
/// Returns the factorial of an `i32` as a [`d128`](decimal/struct.d128.html)
pub fn lookup_factorial(n: i32) -> d128 {
return match n {
0 => d128!(1),

View File

@ -1,6 +1,7 @@
use cpc::eval;
use cpc::units::Unit;
/// cpc CLI interface
fn main() {
use std::env;
let args: Vec<String> = env::args().collect();

View File

@ -5,8 +5,11 @@ use crate::TextOperator::{To, Of};
use crate::units::Unit::{Foot, Inch};
#[derive(Debug)]
/// A struct with a [`Token`](struct.AstNode.html#structfield.token) and `AstNode` [`children`](struct.AstNode.html#structfield.children)
pub struct AstNode {
/// The children of the `AstNode`
pub children: Vec<AstNode>,
/// The token of the `AstNode`
pub token: Token,
}
@ -19,6 +22,7 @@ impl AstNode {
}
}
/// Parse [`TokenVector`](type.TokenVector.html) into an Abstract Syntax Tree ([`AstNode`](struct.AstNode.html))
pub fn parse(tokens: &TokenVector) -> Result<AstNode, String> {
parse_level_1(tokens, 0).and_then(|(ast, next_pos)| if next_pos == tokens.len() {
Ok(ast)
@ -28,6 +32,7 @@ pub fn parse(tokens: &TokenVector) -> Result<AstNode, String> {
}
// level 1 precedence (lowest): to, of
/// Parse [`to`](../enum.TextOperator.html#variant.To) and [`of`](../enum.TextOperator.html#variant.Of)
pub fn parse_level_1(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
// do higher precedences first, then come back down
let (mut node, mut pos) = parse_level_2(tokens, pos)?;
@ -55,6 +60,7 @@ pub fn parse_level_1(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
// level 2 precedence: +, -
/// Parse [`Plus`](../enum.Operator.html#variant.Plus) and [`Minus`](../enum.Operator.html#variant.Minus)
pub fn parse_level_2(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
let (mut node, mut pos) = parse_level_3(tokens, pos)?;
loop {
@ -89,7 +95,8 @@ pub fn parse_level_2(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
}
// level 3 precedence: *, /, modulo
// level 3 precedence: *, /, modulo, implicative multiplication
/// Parse [`Multiply`](../enum.Operator.html#variant.Multiply), [`Divide`](../enum.Operator.html#variant.Divide), [`Modulo`](../enum.Operator.html#variant.Modulo) and implicative multiplication (for example`2pi`)
pub fn parse_level_3(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
let (mut node, mut pos) = parse_level_4(tokens, pos)?;
@ -188,6 +195,7 @@ pub fn parse_level_3(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
// level 4 precedence: ^
/// Parse [`Caret`](../enum.Operator.html#variant.Caret)
pub fn parse_level_4(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
let (mut node, mut pos) = parse_level_5(tokens, pos)?;
loop {
@ -209,6 +217,7 @@ pub fn parse_level_4(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
// level 5 precedence: - (as in -5, but not 4-5)
/// Parse [`Negative`](../enum.Token.html#variant.Negative)
pub fn parse_level_5(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
// Here we parse the negative unary operator. If the current token
// is a minus, we wrap the right_node inside a Negative AstNode.
@ -235,6 +244,7 @@ pub fn parse_level_5(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
// level 6 precedence: !, percent
/// Parse [`Factorial`](../enum.UnaryOperator.html#variant.Factorial) and [`Percent`](../enum.UnaryOperator.html#variant.Percent)
pub fn parse_level_6(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
let (mut node, mut pos) = parse_level_7(tokens, pos)?;
loop {
@ -265,7 +275,12 @@ pub fn parse_level_6(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize
}
}
// level 7 precedence: numbers, parens
// level 7 precedence: numbers, units, constants, functions, parens
/// Parse [`Number`](../enum.Token.html#variant.Number),
/// [`Unit`](../units/enum.Unit.html),
/// [`Constant`](../enum.Constant.html),
/// [`FunctionIdentifier`](../enum.FunctionIdentifier.html),
/// [`Paren`](../enum.Token.html#variant.Paren)
pub fn parse_level_7(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), String> {
let token: &Token = tokens.get(pos).ok_or(format!("Unexpected end of input at {}", pos))?;
match token {

View File

@ -238,7 +238,7 @@ pub fn get_conversion_factor(unit: Unit, to_unit: Unit) -> d128 {
return unit.weight() / to_unit.weight();
}
/// Convert a `Number` to `to_unit`.
/// Convert a [`Number`](struct.Number.html) to a specified [`Unit`](enum.Unit.html).
pub fn convert(number: Number, to_unit: Unit) -> Result<Number, String> {
if number.unit.category() != to_unit.category() {
return Err(format!("Cannot convert from {:?} to {:?}", number.unit, to_unit));
@ -266,7 +266,7 @@ pub fn convert(number: Number, to_unit: Unit) -> Result<Number, String> {
}
}
/// If one of two provided `Number`s has a larger unit than the other, convert
/// If one of two provided [`Number`](struct.Number.html)s has a larger [`Unit`](enum.Unit.html) than the other, convert
/// the large one to the unit of the small one.
pub fn convert_to_lowest(left: Number, right: Number) -> Result<(Number, Number), String> {
if left.unit.weight() == right.unit.weight() {
@ -280,7 +280,7 @@ pub fn convert_to_lowest(left: Number, right: Number) -> Result<(Number, Number)
}
}
/// Return the sum of `left` and `right`
/// Return the sum of two [`Number`](enum.Number.html)s
pub fn add(left: Number, right: Number) -> Result<Number, String> {
if left.unit == right.unit {
Ok(Number::new(left.value + right.value, left.unit))
@ -292,7 +292,7 @@ pub fn add(left: Number, right: Number) -> Result<Number, String> {
}
}
/// Subtract a `left` from `right`
/// Subtract a [`Number`](enum.Number.html) from another [`Number`](enum.Number.html)
pub fn subtract(left: Number, right: Number) -> Result<Number, String> {
if left.unit == right.unit {
Ok(Number::new(left.value - right.value, left.unit))
@ -304,7 +304,7 @@ pub fn subtract(left: Number, right: Number) -> Result<Number, String> {
}
}
/// Convert `Number` to an ideal unit.
/// Convert [`Number`](enum.Number.html) to an ideal unit.
///
/// If you have 1,000,000 millimeters, this will return 1 kilometer.
///
@ -352,12 +352,12 @@ pub fn to_ideal_unit(number: Number) -> Number {
number
}
/// Multiply `left` with `right`
/// Multiply two [`Number`](enum.Number.html)s
///
/// - Temperatures don't work
/// - If you multiple `NoType` with any other unit, the result gets that other unit
/// - If you multiple `Length` with `Length`, the result has a unit of `Area`, etc.
/// - If you multiple `Speed` with `Time`, the result has a unit of `Length`
/// - If you multiply `NoType` with any other unit, the result gets that other unit
/// - If you multiply `Length` with `Length`, the result has a unit of `Area`, etc.
/// - If you multiply `Speed` with `Time`, the result has a unit of `Length`
pub fn multiply(left: Number, right: Number) -> Result<Number, String> {
let lcat = left.unit.category();
let rcat = right.unit.category();
@ -401,7 +401,7 @@ pub fn multiply(left: Number, right: Number) -> Result<Number, String> {
}
}
/// Divide `left` by `right`
/// Divide a [`Number`](enum.Number.html) by another [`Number`](enum.Number.html)
///
/// - Temperatures don't work
/// - If you divide a unit by that same unit, the result has a unit of `NoType`
@ -449,7 +449,9 @@ pub fn divide(left: Number, right: Number) -> Result<Number, String> {
Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit))
}
}
/// Modulo `left` by `right`.`left` and `right` need to have the same `UnitType`, and the result will have that same `UnitType`.
/// Modulo a [`Number`](enum.Number.html) by another [`Number`](enum.Number.html).
///
/// `left` and `right` need to have the same `UnitType`, and the result will have that same `UnitType`.
///
/// Temperatures don't work.
pub fn modulo(left: Number, right: Number) -> Result<Number, String> {
@ -465,7 +467,7 @@ pub fn modulo(left: Number, right: Number) -> Result<Number, String> {
}
}
/// Returns `left` to the power of `right`
/// Returns a [`Number`](enum.Number.html) to the power of another [`Number`](enum.Number.html)
///
/// - If you take `Length` to the power of `NoType`, the result has a unit of `Area`.
/// - If you take `Length` to the power of `Length`, the result has a unit of `Area`