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.

239 lines
7.7 KiB

  1. /**
  2. * @file evaluate.cpp
  3. */
  4. /*
  5. * This program source code file is part of KiCad, a free EDA CAD application.
  6. *
  7. * Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
  8. * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. /* How to evaluate an arithmetic expression like those used in Aperture Macro Definition in Gerber?
  28. *
  29. * See http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence
  30. *
  31. * The shunting yard algorithm is the right tool for this.
  32. * Wikipedia is really confusing about this, but basically the algorithm works like this:
  33. *
  34. * Say, you want to evaluate 1 + 2 * 3 + 4. Intuitively, you "know" you have to do the 2 * 3 first,
  35. * but how do you get this result?
  36. * The key is to realize that when you're scanning the string from left to right, you will evaluate
  37. * an operator when the operator that follows it has a lower (or equal to) precedence.
  38. *
  39. * In the context of the example, here's what you want to do:
  40. *
  41. * Look at: 1 + 2, don't do anything.
  42. * Now look at 1 + 2 * 3, still don't do anything.
  43. * Now look at 1 + 2 * 3 + 4, now you know that 2 * 3 has to to be evaluated because
  44. * the next operator has lower precedence.
  45. *
  46. * How do you implement this?
  47. *
  48. * You want to have two stacks, one for numbers, and another for operators.
  49. * You push numbers onto the stack all the time.
  50. * You compare each new operator with the one at the top of the stack,
  51. * if the one on top of the stack has higher priority, you pop it off the operator stack,
  52. * pop the operands off the number stack, apply the operator and push the result onto the number stack.
  53. *
  54. * Now you repeat the comparison with the top of stack operator.
  55. *
  56. * Coming back to the example, it works like this:
  57. *
  58. * N = [ ] Ops = [ ]
  59. *
  60. * Read 1. N = [1], Ops = [ ]
  61. * Read +. N = [1], Ops = [+]
  62. * Read 2. N = [1 2], Ops = [+]
  63. * Read *. N = [1 2], Ops = [+ *]
  64. * Read 3. N = [1 2 3], Ops = [+ *]
  65. * Read +. N = [1 2 3], Ops = [+ *]
  66. * Pop 3, 2 and execute 2*3, and push result onto N. N = [1 6], Ops = [+]
  67. * is left associative, so you want to pop 1, 6 off as well and execute the +. N = [7], Ops = [].
  68. * Finally push the [+] onto the operator stack. N = [7], Ops = [+].
  69. * Read 4. N = [7 4]. Ops = [+].
  70. *
  71. * You're run out off input, so you want to empty the stacks now.
  72. * Upon which you will get the result 11.
  73. */
  74. #include <am_param.h>
  75. /**
  76. * Evaluate an basic arithmetic expression (infix notation) with precedence
  77. * The expression is a sequence of numbers (double) and arith operators:
  78. * operators are + - x / ( and )
  79. * the expression is stored in a std::vector
  80. * each item is a AM_PARAM_EVAL (each item is an operator or a double)
  81. * @param aExp = the arithmetic expression to evaluate
  82. * @return the value
  83. */
  84. /*
  85. The instructions ( subset of parm_item_type)
  86. ----------------
  87. NOP : The no operation. the AM_PARAM_EVAL item stores a value.
  88. ADD
  89. SUB
  90. MUL
  91. DIV
  92. OPEN_PAR : Opening parenthesis: modify the precedence of operators inside ( and )
  93. CLOSE_PAR : Closing parenthesis: modify the precedence of operators by closing the local block.
  94. POPVALUE : used to initialize a sequence
  95. */
  96. double Evaluate( AM_PARAM_EVAL_STACK& aExp )
  97. {
  98. class OP_CODE // A small class to store a operator and its priority
  99. {
  100. public:
  101. parm_item_type m_Optype;
  102. int m_Priority;
  103. OP_CODE( AM_PARAM_EVAL& aAmPrmEval )
  104. : m_Optype( aAmPrmEval.GetOperator() ),
  105. m_Priority( aAmPrmEval.GetPriority() )
  106. {}
  107. OP_CODE( parm_item_type aOptype )
  108. : m_Optype( aOptype ), m_Priority( 0 )
  109. {}
  110. };
  111. double result = 0.0;
  112. std::vector<double> values; // the current list of values
  113. std::vector<OP_CODE> optype; // the list of arith operators
  114. double curr_value = 0.0;
  115. int extra_priority = 0;
  116. for( unsigned ii = 0; ii < aExp.size(); ii++ )
  117. {
  118. AM_PARAM_EVAL& prm = aExp[ii];
  119. if( prm.IsOperator() )
  120. {
  121. if( prm.GetOperator() == OPEN_PAR )
  122. {
  123. extra_priority += AM_PARAM_EVAL::GetPriority( OPEN_PAR );
  124. }
  125. else if( prm.GetOperator() == CLOSE_PAR )
  126. {
  127. extra_priority -= AM_PARAM_EVAL::GetPriority( CLOSE_PAR );
  128. }
  129. else
  130. {
  131. optype.emplace_back( prm );
  132. optype.back().m_Priority += extra_priority;
  133. }
  134. }
  135. else // we have a value:
  136. {
  137. values.push_back( prm.GetValue() );
  138. if( optype.size() < 2 )
  139. continue;
  140. OP_CODE& previous_optype = optype[optype.size() - 2];
  141. if( optype.back().m_Priority > previous_optype.m_Priority )
  142. {
  143. double op1 = 0.0;
  144. double op2 = values.back();
  145. values.pop_back();
  146. if( values.size() )
  147. {
  148. op1 = values.back();
  149. values.pop_back();
  150. }
  151. switch( optype.back().m_Optype )
  152. {
  153. case ADD:
  154. values.push_back( op1+op2 );
  155. break;
  156. case SUB:
  157. values.push_back( op1-op2 );
  158. break;
  159. case MUL:
  160. values.push_back( op1*op2 );
  161. break;
  162. case DIV:
  163. values.push_back( op1/op2 );
  164. break;
  165. default:
  166. break;
  167. }
  168. optype.pop_back();
  169. }
  170. }
  171. }
  172. // Now all operators have the same priority, or those having the higher priority
  173. // are before others, calculate the final result by combining initial values and/or
  174. // replaced values.
  175. if( values.size() > optype.size() )
  176. // If there are n values, the number of operator is n-1 or n if the first
  177. // item of the expression to evaluate is + or - (like -$1/2)
  178. // If the number of operator is n-1 the first value is just copied to result
  179. optype.insert( optype.begin(), OP_CODE( POPVALUE ) );
  180. wxASSERT( values.size() == optype.size() );
  181. for( unsigned idx = 0; idx < values.size(); idx++ )
  182. {
  183. curr_value = values[idx];
  184. switch( optype[idx].m_Optype )
  185. {
  186. case POPVALUE:
  187. result = curr_value;
  188. break;
  189. case ADD:
  190. result += curr_value;
  191. break;
  192. case SUB:
  193. result -= curr_value;
  194. break;
  195. case MUL:
  196. result *= curr_value;
  197. break;
  198. case DIV:
  199. result /= curr_value;
  200. break;
  201. default:
  202. break;
  203. }
  204. }
  205. return result;
  206. }