/** * @file am_param.cpp */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2017 Jean-Pierre Charras * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include extern int ReadInt( char*& text, bool aSkipSeparator = true ); extern double ReadDouble( char*& text, bool aSkipSeparator = true ); extern double Evaluate( AM_PARAM_EVAL_STACK& aExp ); /* Class AM_PARAM * holds a parameter value for an "aperture macro" as defined within * standard RS274X. The parameter can be a constant, i.e. "immediate" parameter, * or depend on some defered values, defined in a D_CODE, by the ADD command. * Note the actual value could need an evaluation from an arithmetical expression * items in the expression are stored in . * A simple definition is just a value stored in one item in m_paramStack */ AM_PARAM::AM_PARAM( ) { m_index = -1; } /** * Function IsImmediate * tests if this AM_PARAM holds an immediate parameter or has parameter * held by an owning D_CODE. */ bool AM_PARAM::IsImmediate() const { bool is_immediate = true; for( unsigned ii = 0; ii < m_paramStack.size(); ii++ ) { if( m_paramStack[ii].IsDefered() ) { // a defered value is found in operand list, // so the parameter is not immediate is_immediate = false; break; } } return is_immediate; } double AM_PARAM::GetValue( const D_CODE* aDcode ) const { // In macros, actual values are sometimes given by an expression like: // 0-$2/2-$4 // Because arithmetic predence is used, the parameters (values (double) and operators) // are stored in a stack, with all numeric values converted to the actual values // when they are defered parameters // Each item is stored in a AM_PARAM_EVAL (a value or an operator) // // Then the stack with all values resolved is parsed and numeric values // calculated according to the precedence of operators double curr_value = 0.0; parm_item_type op_code; AM_PARAM_EVAL_STACK ops; for( unsigned ii = 0; ii < m_paramStack.size(); ii++ ) { AM_PARAM_ITEM item = m_paramStack[ii]; switch( item.GetType() ) { case ADD: case SUB: case MUL: case DIV: // just an operator for next parameter value case OPEN_PAR: case CLOSE_PAR: // Priority modifiers: store in stack op_code = item.GetType(); ops.push_back( AM_PARAM_EVAL( op_code ) ); break; case PUSHPARM: // a defered value: get the actual parameter from the aDcode if( aDcode ) // should be always true here { if( item.GetIndex() <= aDcode->GetParamCount() ) { curr_value = aDcode->GetParam( item.GetIndex() ); } else // Get parameter from local param definition { const APERTURE_MACRO * am_parent = aDcode->GetMacro(); curr_value = am_parent->GetLocalParam( aDcode, item.GetIndex() ); } } else { wxFAIL_MSG( wxT( "AM_PARAM::GetValue(): NULL param aDcode" ) ); } ops.push_back( AM_PARAM_EVAL( curr_value ) ); break; case PUSHVALUE: // a value is on the stack: curr_value = item.GetValue(); ops.push_back( AM_PARAM_EVAL( curr_value ) ); break; default: wxFAIL_MSG( wxString::Format( wxT( "AM_PARAM::GetValue(): dcode %d prm %d/%d: " "unexpected type %d" ), aDcode ? aDcode->m_Num_Dcode : -1, ii, m_paramStack.size(), item.GetType() ) ); break; } } double result = Evaluate( ops ); return result; } /** * add an operator/operand to the current stack * aType = NOP, PUSHVALUE, PUSHPARM, ADD, SUB, MUL, DIV, EQUATE * aValue required only for PUSHVALUE (double) or PUSHPARM (int) aType. */ void AM_PARAM::PushOperator( parm_item_type aType, double aValue ) { AM_PARAM_ITEM item( aType, aValue); m_paramStack.push_back( item ); } void AM_PARAM::PushOperator( parm_item_type aType, int aValue ) { AM_PARAM_ITEM item( aType, aValue); m_paramStack.push_back( item ); } /** * Function ReadParam * Read one aperture macro parameter * a parameter can be: * a number * a reference to an aperture definition parameter value: $1 ot $3 ... * a parameter definition can be complex and have operators between numbers and/or other parameter * like $1+3 or $2x2.. * Note minus sign is not always an operator. It can be the sign of a value. * Parameters are separated by a comma ( of finish by *) * @param aText = pointer to the parameter to read. Will be modified to point to the next field * @return true if a param is read, or false */ bool AM_PARAM::ReadParam( char*& aText ) { bool found = false; int ivalue; double dvalue; bool end = false; while( !end ) { switch( *aText ) { case ',': aText++; if( !found ) // happens when a string starts by ',' before any param break; // just skip this separator KI_FALLTHROUGH; case '\n': case '\r': case 0: // EOL case '*': // Terminator in a gerber command end = true; break; case ' ': aText++; break; case '$': // defered value defined later, in ADD command which define defered parameters ++aText; ivalue = ReadInt( aText, false ); if( m_index < 1 ) SetIndex( ivalue ); PushOperator( PUSHPARM, ivalue ); found = true; break; case '/': PushOperator( DIV ); aText++; break; case '(': // Open a block to evaluate an expression between '(' and ')' PushOperator( OPEN_PAR ); aText++; break; case ')': // close a block between '(' and ')' PushOperator( CLOSE_PAR ); aText++; break; case 'x': case 'X': PushOperator( MUL ); aText++; break; case '-': case '+': // Test if this is an operator between 2 params, or the sign of a value if( m_paramStack.size() > 0 && !m_paramStack.back().IsOperator() ) { // Seems an operator PushOperator( *aText == '+' ? ADD : SUB ); aText++; } else { // seems the sign of a value dvalue = ReadDouble( aText, false ); PushOperator( PUSHVALUE, dvalue ); found = true; } break; case '=': // A local definition found like $4=$3/2 // At this point, one defered parameter is expected to be read. // this parameter value (the index) is stored in m_index. // The list of items is cleared aText++; m_paramStack.clear(); found = false; break; default: dvalue = ReadDouble( aText, false ); PushOperator( PUSHVALUE, dvalue ); found = true; break; } } return found; }