diff --git a/README.md b/README.md index 86613c0..eee9f77 100644 --- a/README.md +++ b/README.md @@ -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) => { diff --git a/src/evaluator.rs b/src/evaluator.rs index ec8237c..16c7377 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -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 { 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 { +/// Evaluate an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html) +fn evaluate_node(ast_node: &AstNode) -> Result { let token = &ast_node.token; let children = &ast_node.children; match token { diff --git a/src/lib.rs b/src/lib.rs index 459c3e1..2aa8f6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; /// /// 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) => { diff --git a/src/lookup.rs b/src/lookup.rs index 3012246..b0a2ab3 100644 --- a/src/lookup.rs +++ b/src/lookup.rs @@ -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), diff --git a/src/main.rs b/src/main.rs index 0bf39cf..0c37be1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use cpc::eval; use cpc::units::Unit; +/// cpc CLI interface fn main() { use std::env; let args: Vec = env::args().collect(); diff --git a/src/parser.rs b/src/parser.rs index 9364e33..822f4fc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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, + /// 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 { 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 { } // 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 { diff --git a/src/units.rs b/src/units.rs index f486efa..fb16ae8 100644 --- a/src/units.rs +++ b/src/units.rs @@ -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 { 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 { } } -/// 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 { 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 { } } -/// 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 { 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 { } } -/// 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 { let lcat = left.unit.category(); let rcat = right.unit.category(); @@ -401,7 +401,7 @@ pub fn multiply(left: Number, right: Number) -> Result { } } -/// 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 { 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 { @@ -465,7 +467,7 @@ pub fn modulo(left: Number, right: Number) -> Result { } } -/// 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`