You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
5.7 KiB

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