240 lines
7.7 KiB
C++
240 lines
7.7 KiB
C++
/**
|
|
* @file evaluate.cpp
|
|
*/
|
|
|
|
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
|
|
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
|
|
*/
|
|
|
|
/* How to evaluate an arithmetic expression like those used in Aperture Macro Definition in Gerber?
|
|
*
|
|
* See http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence
|
|
*
|
|
* The shunting yard algorithm is the right tool for this.
|
|
* Wikipedia is really confusing about this, but basically the algorithm works like this:
|
|
*
|
|
* Say, you want to evaluate 1 + 2 * 3 + 4. Intuitively, you "know" you have to do the 2 * 3 first,
|
|
* but how do you get this result?
|
|
* The key is to realize that when you're scanning the string from left to right, you will evaluate
|
|
* an operator when the operator that follows it has a lower (or equal to) precedence.
|
|
*
|
|
* In the context of the example, here's what you want to do:
|
|
*
|
|
* Look at: 1 + 2, don't do anything.
|
|
* Now look at 1 + 2 * 3, still don't do anything.
|
|
* Now look at 1 + 2 * 3 + 4, now you know that 2 * 3 has to to be evaluated because
|
|
* the next operator has lower precedence.
|
|
*
|
|
* How do you implement this?
|
|
*
|
|
* You want to have two stacks, one for numbers, and another for operators.
|
|
* You push numbers onto the stack all the time.
|
|
* You compare each new operator with the one at the top of the stack,
|
|
* if the one on top of the stack has higher priority, you pop it off the operator stack,
|
|
* pop the operands off the number stack, apply the operator and push the result onto the number stack.
|
|
*
|
|
* Now you repeat the comparison with the top of stack operator.
|
|
*
|
|
* Coming back to the example, it works like this:
|
|
*
|
|
* N = [ ] Ops = [ ]
|
|
*
|
|
* Read 1. N = [1], Ops = [ ]
|
|
* Read +. N = [1], Ops = [+]
|
|
* Read 2. N = [1 2], Ops = [+]
|
|
* Read *. N = [1 2], Ops = [+ *]
|
|
* Read 3. N = [1 2 3], Ops = [+ *]
|
|
* Read +. N = [1 2 3], Ops = [+ *]
|
|
* Pop 3, 2 and execute 2*3, and push result onto N. N = [1 6], Ops = [+]
|
|
* is left associative, so you want to pop 1, 6 off as well and execute the +. N = [7], Ops = [].
|
|
* Finally push the [+] onto the operator stack. N = [7], Ops = [+].
|
|
* Read 4. N = [7 4]. Ops = [+].
|
|
*
|
|
* You're run out off input, so you want to empty the stacks now.
|
|
* Upon which you will get the result 11.
|
|
*/
|
|
|
|
#include <class_am_param.h>
|
|
|
|
/**
|
|
* Evaluate an basic arithmetic expression (infix notation) with precedence
|
|
* The expression is a sequence of numbers (double) and arith operators:
|
|
* operators are + - x / ( and )
|
|
* the expression is stored in a std::vector
|
|
* each item is a AM_PARAM_EVAL (each item is an operator or a double)
|
|
* @param aExp = the arithmetic expression to evaluate
|
|
* @return the value
|
|
*/
|
|
|
|
/*
|
|
The instructions ( subset of parm_item_type)
|
|
----------------
|
|
NOP : The no operation. the AM_PARAM_EVAL item stores a value.
|
|
ADD
|
|
SUB
|
|
MUL
|
|
DIV
|
|
OPEN_PAR : Opening parenthesis: modify the precedence of operators inside ( and )
|
|
CLOSE_PAR : Closing parenthesis: modify the precedence of operators by closing the local block.
|
|
POPVALUE : used to initialize a sequence
|
|
*/
|
|
|
|
double Evaluate( AM_PARAM_EVAL_STACK& aExp )
|
|
{
|
|
class OP_CODE // A small class to store a operator and its priority
|
|
{
|
|
public:
|
|
parm_item_type m_Optype;
|
|
int m_Priority;
|
|
|
|
OP_CODE( AM_PARAM_EVAL& aAmPrmEval )
|
|
: m_Optype( aAmPrmEval.GetOperator() ),
|
|
m_Priority( aAmPrmEval.GetPriority() )
|
|
{}
|
|
|
|
OP_CODE( parm_item_type aOptype )
|
|
: m_Optype( aOptype ), m_Priority( 0 )
|
|
{}
|
|
};
|
|
|
|
double result = 0.0;
|
|
|
|
std::vector<double> values; // the current list of values
|
|
std::vector<OP_CODE> optype; // the list of arith operators
|
|
|
|
double curr_value = 0.0;
|
|
int extra_priority = 0;
|
|
|
|
for( unsigned ii = 0; ii < aExp.size(); ii++ )
|
|
{
|
|
AM_PARAM_EVAL& prm = aExp[ii];
|
|
|
|
if( prm.IsOperator() )
|
|
{
|
|
if( prm.GetOperator() == OPEN_PAR )
|
|
{
|
|
extra_priority += AM_PARAM_EVAL::GetPriority( OPEN_PAR );
|
|
}
|
|
else if( prm.GetOperator() == CLOSE_PAR )
|
|
{
|
|
extra_priority -= AM_PARAM_EVAL::GetPriority( CLOSE_PAR );
|
|
}
|
|
else
|
|
{
|
|
optype.push_back( OP_CODE( prm ) );
|
|
optype.back().m_Priority += extra_priority;
|
|
}
|
|
}
|
|
else // we have a value:
|
|
{
|
|
values.push_back( prm.GetValue() );
|
|
|
|
if( optype.size() < 2 )
|
|
continue;
|
|
|
|
OP_CODE& previous_optype = optype[optype.size() - 2];
|
|
|
|
if( optype.back().m_Priority > previous_optype.m_Priority )
|
|
{
|
|
double op1 = 0.0;
|
|
|
|
double op2 = values.back();
|
|
values.pop_back();
|
|
|
|
if( values.size() )
|
|
{
|
|
op1 = values.back();
|
|
values.pop_back();
|
|
}
|
|
|
|
switch( optype.back().m_Optype )
|
|
{
|
|
case ADD:
|
|
values.push_back( op1+op2 );
|
|
break;
|
|
|
|
case SUB:
|
|
values.push_back( op1-op2 );
|
|
break;
|
|
|
|
case MUL:
|
|
values.push_back( op1*op2 );
|
|
break;
|
|
|
|
case DIV:
|
|
values.push_back( op1/op2 );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
optype.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now all operators have the same priority, or those having the higher priority
|
|
// are before others, calculate the final result by combining initial values and/or
|
|
// replaced values.
|
|
if( values.size() > optype.size() )
|
|
// If there are n values, the number of operator is n-1 or n if the first
|
|
// item of the expression to evaluate is + or - (like -$1/2)
|
|
// If the number of operator is n-1 the first value is just copied to result
|
|
optype.insert( optype.begin(), OP_CODE( POPVALUE ) );
|
|
|
|
wxASSERT( values.size() == optype.size() );
|
|
|
|
for( unsigned idx = 0; idx < values.size(); idx++ )
|
|
{
|
|
curr_value = values[idx];
|
|
|
|
switch( optype[idx].m_Optype )
|
|
{
|
|
case POPVALUE:
|
|
result = curr_value;
|
|
break;
|
|
|
|
case ADD:
|
|
result += curr_value;
|
|
break;
|
|
|
|
case SUB:
|
|
result -= curr_value;
|
|
break;
|
|
|
|
case MUL:
|
|
result *= curr_value;
|
|
break;
|
|
|
|
case DIV:
|
|
result /= curr_value;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} |