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.

272 lines
8.7 KiB

  1. /**
  2. * @file am_param.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) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  9. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. #include <am_param.h>
  29. #include <am_primitive.h>
  30. #include <macros.h>
  31. #include <wx/debug.h>
  32. extern int ReadInt( char*& text, bool aSkipSeparator = true );
  33. extern double ReadDouble( char*& text, bool aSkipSeparator = true );
  34. extern double Evaluate( AM_PARAM_EVAL_STACK& aExp );
  35. /* Class AM_PARAM
  36. * holds a parameter value for an "aperture macro" as defined within
  37. * standard RS274X. The parameter can be a constant, i.e. "immediate" parameter,
  38. * or depend on some defered values, defined in a D_CODE, by the ADD command.
  39. * Note the actual value could need an evaluation from an arithmetical expression
  40. * items in the expression are stored in .
  41. * A simple definition is just a value stored in one item in m_paramStack
  42. */
  43. AM_PARAM::AM_PARAM( )
  44. {
  45. m_index = -1;
  46. }
  47. /**
  48. * Function IsImmediate
  49. * tests if this AM_PARAM holds an immediate parameter or has parameter
  50. * held by an owning D_CODE.
  51. */
  52. bool AM_PARAM::IsImmediate() const
  53. {
  54. bool is_immediate = true;
  55. for( unsigned ii = 0; ii < m_paramStack.size(); ii++ )
  56. {
  57. if( m_paramStack[ii].IsDefered() )
  58. { // a defered value is found in operand list,
  59. // so the parameter is not immediate
  60. is_immediate = false;
  61. break;
  62. }
  63. }
  64. return is_immediate;
  65. }
  66. double AM_PARAM::GetValue( const D_CODE* aDcode ) const
  67. {
  68. // In macros, actual values are sometimes given by an expression like:
  69. // 0-$2/2-$4
  70. // Because arithmetic predence is used, the parameters (values (double) and operators)
  71. // are stored in a stack, with all numeric values converted to the actual values
  72. // when they are defered parameters
  73. // Each item is stored in a AM_PARAM_EVAL (a value or an operator)
  74. //
  75. // Then the stack with all values resolved is parsed and numeric values
  76. // calculated according to the precedence of operators
  77. double curr_value = 0.0;
  78. parm_item_type op_code;
  79. AM_PARAM_EVAL_STACK ops;
  80. for( unsigned ii = 0; ii < m_paramStack.size(); ii++ )
  81. {
  82. AM_PARAM_ITEM item = m_paramStack[ii];
  83. switch( item.GetType() )
  84. {
  85. case ADD:
  86. case SUB:
  87. case MUL:
  88. case DIV: // just an operator for next parameter value
  89. case OPEN_PAR:
  90. case CLOSE_PAR: // Priority modifiers: store in stack
  91. op_code = item.GetType();
  92. ops.push_back( AM_PARAM_EVAL( op_code ) );
  93. break;
  94. case PUSHPARM:
  95. // a defered value: get the actual parameter from the aDcode
  96. if( aDcode ) // should be always true here
  97. {
  98. if( item.GetIndex() <= aDcode->GetParamCount() )
  99. {
  100. curr_value = aDcode->GetParam( item.GetIndex() );
  101. }
  102. else // Get parameter from local param definition
  103. {
  104. const APERTURE_MACRO * am_parent = aDcode->GetMacro();
  105. curr_value = am_parent->GetLocalParam( aDcode, item.GetIndex() );
  106. }
  107. }
  108. else
  109. {
  110. wxFAIL_MSG( "AM_PARAM::GetValue(): NULL param aDcode" );
  111. }
  112. ops.push_back( AM_PARAM_EVAL( curr_value ) );
  113. break;
  114. case PUSHVALUE: // a value is on the stack:
  115. curr_value = item.GetValue();
  116. ops.push_back( AM_PARAM_EVAL( curr_value ) );
  117. break;
  118. default:
  119. wxFAIL_MSG( wxString::Format( "AM_PARAM::GetValue(): dcode %d prm %d/%d: "
  120. "unexpected type %d",
  121. aDcode ? aDcode->m_Num_Dcode : -1, ii,
  122. m_paramStack.size(), item.GetType() ) );
  123. break;
  124. }
  125. }
  126. double result = Evaluate( ops );
  127. return result;
  128. }
  129. /**
  130. * add an operator/operand to the current stack
  131. * aType = NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, EQUATE
  132. * aValue required only for PUSHVALUE (double) or PUSHPARM (int) aType.
  133. */
  134. void AM_PARAM::PushOperator( parm_item_type aType, double aValue )
  135. {
  136. AM_PARAM_ITEM item( aType, aValue);
  137. m_paramStack.push_back( item );
  138. }
  139. void AM_PARAM::PushOperator( parm_item_type aType, int aValue )
  140. {
  141. AM_PARAM_ITEM item( aType, aValue);
  142. m_paramStack.push_back( item );
  143. }
  144. /**
  145. * Function ReadParam
  146. * Read one aperture macro parameter
  147. * a parameter can be:
  148. * a number
  149. * a reference to an aperture definition parameter value: $1 ot $3 ...
  150. * a parameter definition can be complex and have operators between numbers and/or other parameter
  151. * like $1+3 or $2x2..
  152. * Note minus sign is not always an operator. It can be the sign of a value.
  153. * Parameters are separated by a comma ( of finish by *)
  154. * @param aText = pointer to the parameter to read. Will be modified to point to the next field
  155. * @return true if a param is read, or false
  156. */
  157. bool AM_PARAM::ReadParam( char*& aText )
  158. {
  159. bool found = false;
  160. int ivalue;
  161. double dvalue;
  162. bool end = false;
  163. while( !end )
  164. {
  165. switch( *aText )
  166. {
  167. case ',':
  168. aText++;
  169. if( !found ) // happens when a string starts by ',' before any param
  170. break; // just skip this separator
  171. KI_FALLTHROUGH;
  172. case '\n':
  173. case '\r':
  174. case 0: // EOL
  175. case '*': // Terminator in a gerber command
  176. end = true;
  177. break;
  178. case ' ':
  179. aText++;
  180. break;
  181. case '$':
  182. // defered value defined later, in ADD command which define defered parameters
  183. ++aText;
  184. ivalue = ReadInt( aText, false );
  185. if( m_index < 1 )
  186. SetIndex( ivalue );
  187. PushOperator( PUSHPARM, ivalue );
  188. found = true;
  189. break;
  190. case '/':
  191. PushOperator( DIV );
  192. aText++;
  193. break;
  194. case '(': // Open a block to evaluate an expression between '(' and ')'
  195. PushOperator( OPEN_PAR );
  196. aText++;
  197. break;
  198. case ')': // close a block between '(' and ')'
  199. PushOperator( CLOSE_PAR );
  200. aText++;
  201. break;
  202. case 'x':
  203. case 'X':
  204. PushOperator( MUL );
  205. aText++;
  206. break;
  207. case '-':
  208. case '+':
  209. // Test if this is an operator between 2 params, or the sign of a value
  210. if( m_paramStack.size() > 0 && !m_paramStack.back().IsOperator() )
  211. { // Seems an operator
  212. PushOperator( *aText == '+' ? ADD : SUB );
  213. aText++;
  214. }
  215. else
  216. { // seems the sign of a value
  217. dvalue = ReadDouble( aText, false );
  218. PushOperator( PUSHVALUE, dvalue );
  219. found = true;
  220. }
  221. break;
  222. case '=': // A local definition found like $4=$3/2
  223. // At this point, one defered parameter is expected to be read.
  224. // this parameter value (the index) is stored in m_index.
  225. // The list of items is cleared
  226. aText++;
  227. m_paramStack.clear();
  228. found = false;
  229. break;
  230. default:
  231. dvalue = ReadDouble( aText, false );
  232. PushOperator( PUSHVALUE, dvalue );
  233. found = true;
  234. break;
  235. }
  236. }
  237. return found;
  238. }