1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
//! 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}; //! use cpc::units::Unit; //! //! match eval("3m + 1cm", true, Unit::Celcius, false) { //! Ok(answer) => { //! // answer: Number { value: 301, unit: Unit::Centimeter } //! 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; mod lookup; #[derive(Clone, Debug)] /// A number with a `Unit`. /// /// Example: /// ```rust /// use cpc::{eval,Number}; /// use cpc::units::Unit; /// use decimal::d128; /// /// let x = Number { /// value: d128!(100), /// unit: Unit::Meter, /// }; /// ``` pub struct Number { /// The number part of a `Number` struct pub value: d128, /// The unit of a `Number` struct. This can be `NoType` pub unit: Unit, } impl Number { pub fn new(value: d128, unit: Unit) -> Number { Number { value: value, unit: unit, } } } #[derive(Clone, Debug)] /// Math operators like [`Multiply`](enum.Operator.html#variant.Multiply), parentheses, etc. pub enum Operator { Plus, Minus, Multiply, Divide, Modulo, Caret, LeftParen, // lexer only RightParen, // lexer only } #[derive(Clone, Debug)] /// Unary operators like [`Percent`](enum.UnaryOperator.html#variant.Percent) and [`Factorial`](enum.UnaryOperator.html#variant.Factorial). pub enum UnaryOperator { Percent, Factorial, } #[derive(Clone, Debug)] /// A Text operator like [`To`](enum.TextOperator.html#variant.To) or [`From`](enum.TextOperator.html#variant.From). pub enum TextOperator { To, Of, } #[derive(Clone, Debug)] /// A constants like [`Pi`](enum.Constant.html#variant.Pi) or [`E`](enum.Constant.html#variant.E). pub enum Constant { Pi, E, } #[derive(Clone, Debug)] /// Functions identifiers like [`Sqrt`](enum.FunctionIdentifier.html#variant.Sqrt), [`Sin`](enum.FunctionIdentifier.html#variant.Sin), [`Round`](enum.FunctionIdentifier.html#variant.Round), etc. pub enum FunctionIdentifier { Sqrt, Cbrt, Log, Ln, Exp, Round, Ceil, Floor, Abs, Sin, Cos, Tan, } #[derive(Clone, Debug)] /// A temporary enum used by the [`lexer`](lexer/index.html) to later determine what [`Token`](enum.Token.html) it is. /// /// For example, when a symbol like `%` is found, the lexer turns it into a /// the [`PercentChar`](enum.LexerKeyword.html#variant.PercentChar) variant /// and then later it checks the surrounding [`Token`](enum.Token.html)s and, /// dependingon them, turns it into a [`Percent`](enum.UnaryOperator.html) or /// [`Modulo`](enum.Operator.html) [`Token`](enum.Token.html). pub enum LexerKeyword { Per, PercentChar, In, DoubleQuotes, Mercury, Hg, PoundForce, PoundWord, Force, } #[derive(Clone, Debug)] /// A token like a [`Number`](enum.Token.html#variant.Number), [`Operator`](enum.Token.html#variant.Operator), [`Unit`](enum.Token.html#variant.Unit) etc. /// /// Strings can be divided up into these tokens by the [`lexer`](lexer/index.html), and then put into the [`parser`](parser/index.html). pub enum Token { Operator(Operator), UnaryOperator(UnaryOperator), Number(d128), FunctionIdentifier(FunctionIdentifier), Constant(Constant), /// Used by the parser only Paren, /// Used by the lexer only Per, /// Used by the parser only LexerKeyword(LexerKeyword), TextOperator(TextOperator), /// The `-` symbol, specifically when used as `-5` and not `5-5`. Used by the parser only Negative, Unit(units::Unit), } /// A vector of [`Token`](enum.Token.html) pub type TokenVector = Vec<Token>; /// Evaluates a string into a resulting [`Number`](). /// /// Example: /// ```rust /// use cpc::{eval}; /// use cpc::units::Unit; /// /// match eval("3m + 1cm", true, Unit::Celcius, false) { /// Ok(answer) => { /// // answer: Number { value: 301, unit: Unit::Centimeter } /// println!("Evaluated value: {} {:?}", answer.value, answer.unit) /// }, /// Err(e) => { /// println!("{}", e) /// } /// } /// ``` pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, debug: bool) -> Result<Number, String> { let lex_start = Instant::now(); match lexer::lex(input, allow_trailing_operators, default_degree) { Ok(tokens) => { let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32; let parse_start = Instant::now(); match parser::parse(&tokens) { Ok(ast) => { let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32; let eval_start = Instant::now(); match evaluator::evaluate(&ast) { Ok(answer) => { let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32; 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) => Err(format!("Eval error: {}", e)), } }, Err(e) => Err(format!("Parsing error: {}", e)), } }, Err(e) => Err(format!("Lexing error: {}", e)), } }