1 /* 2 This file is part of libeval, a simple math expression evaluator 3 4 Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 /* 21 An evaluator object is used to replace an input string that represents 22 a mathematical expression by its result. 23 24 Example: Consider the input "3+4". The result of this expression is "7". 25 The NumericEvaluator can be used like this: 26 27 NumericEvaluator eval; 28 eval.process("3+4"); 29 printf("3+4", eval.result()); 30 31 The same example with error checking. Please note that even a valid input string may result 32 in an empty output string or "NaN". 33 34 NumericEvaluator eval; 35 bool ret = eval.process("3+4"); 36 assert(ret == eval.isValid()); // isValid() reflects return value of process(). 37 if (eval.isValid()) printf("3+4=%s\n", eval.result()); 38 39 Using variables 40 Expressions can refer to variables if they were defined by previous expressions. 41 A variable can be defined by an expression or by the setVar() method. 42 Expressions that define/set variables do not have a result. 43 44 eval.process("x=1; y=2"); // Result is NaN 45 eval.setVar("z", 3); 46 eval.process("x+y+z"); 47 printf("x+y+z=%s\n", eval.result()); 48 49 Input string storage 50 An evaluator object can store and retrieve the original input string using a pointer 51 as key. This can be used to restore the input string of a text entry field. 52 53 eval.process("25.4-0.7", &eval); 54 printf("%s = %s\n", eval.textInput(&eval), eval.result()); 55 56 Unit conversion 57 The evaluator uses a default unit and constants can be specified with a unit. 58 As long as no units are used the default unit is not relevant. 59 Supported units are millimeters (mm), Mil (mil) and inch (") 60 61 eval.process("1\""); 62 printf("1\" = %s\n", eval.result()); 63 eval.process("12.7 - 0.1\" - 50mil"); 64 printf("12.7 - 0.1\" - 50mil = %s\n", eval.result()); 65 */ 66 67 #ifndef NUMERIC_EVALUATOR_H_ 68 #define NUMERIC_EVALUATOR_H_ 69 70 #include <cstddef> 71 #include <map> 72 #include <string> 73 74 #include <base_units.h> 75 76 // This namespace is used for the lemon parser 77 namespace numEval 78 { 79 80 struct TokenType 81 { 82 union 83 { 84 double dValue; 85 int iValue; 86 }; 87 88 bool valid; 89 char text[32]; 90 }; 91 92 } // namespace numEval 93 94 class NUMERIC_EVALUATOR 95 { 96 enum class Unit { Invalid, MM, CM, Inch, Mil }; 97 98 public: 99 NUMERIC_EVALUATOR( EDA_UNITS aUnits ); 100 ~NUMERIC_EVALUATOR(); 101 102 /* clear() should be invoked by the client if a new input string is to be processed. It 103 * will reset the parser. User defined variables are retained. 104 */ 105 void Clear(); 106 107 /* Used by the lemon parser */ 108 void parseError(const char* s); 109 void parseOk(); 110 void parseSetResult(double); 111 112 /* Check if previous invocation of process() was successful */ IsValid()113 inline bool IsValid() const { return !m_parseError; } 114 115 /* Result of string processing. Undefined if !isValid() */ Result()116 inline wxString Result() const { return wxString::FromUTF8( m_token.token ); } 117 118 /* Evaluate input string. 119 * Result can be retrieved by result(). 120 * Returns true if input string could be evaluated, otherwise false. 121 */ 122 bool Process( const wxString& aString ); 123 124 /* Retrieve the original text before evaluation. */ 125 wxString OriginalText() const; 126 127 /* Add/set variable with value */ 128 void SetVar( const wxString& aString, double aValue ); 129 130 /* Get value of variable. Returns 0.0 if not defined. */ 131 double GetVar( const wxString& aString ); 132 133 /* Remove single variable */ RemoveVar(const wxString & aString)134 void RemoveVar( const wxString& aString ) { m_varMap.erase( aString ); } 135 136 /* Remove all variables */ ClearVar()137 void ClearVar() { m_varMap.clear(); } 138 139 protected: 140 /* Token type used by the tokenizer */ 141 struct Token 142 { 143 int token; 144 numEval::TokenType value; 145 }; 146 147 /* Begin processing of a new input string */ 148 void newString( const wxString& aString ); 149 150 /* Tokenizer: Next token/value taken from input string. */ 151 Token getToken(); 152 153 /* Used by processing loop */ 154 void parse( int token, numEval::TokenType value ); 155 156 private: 157 void* m_parser; // the current lemon parser state machine 158 159 /* Token state for input string. */ 160 struct TokenStat 161 { 162 enum { OutLen = 32 }; TokenStatTokenStat163 TokenStat() : input( nullptr ), token( nullptr ), inputLen( 0 ), pos( 0 ) { /* empty */ } 164 const char* input; // current input string ("var=4") 165 char* token; // output token ("var", type:VAR; "4", type:VALUE) 166 size_t inputLen; // strlen(input) 167 size_t pos; // current index 168 } 169 m_token; 170 171 char m_localeDecimalSeparator; 172 173 /* Parse progress. Set by parser actions. */ 174 bool m_parseError; 175 bool m_parseFinished; 176 177 Unit m_defaultUnits; // Default unit for values 178 179 wxString m_originalText; 180 181 std::map<wxString, double> m_varMap; 182 }; 183 184 185 #endif /* NUMERIC_EVALUATOR_H_ */ 186