KiCad PCB EDA Suite
Loading...
Searching...
No Matches
evaluate.cpp
Go to the documentation of this file.
1
4
5/*
6 * This program source code file is part of KiCad, a free EDA CAD application.
7 *
8 * Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
9 * Copyright The 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, see <https://www.gnu.org/licenses/>.
23 */
24
25/* How to evaluate an arithmetic expression like those used in Aperture Macro Definition in Gerber?
26 *
27 * See http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence
28 *
29 * The shunting yard algorithm is the right tool for this.
30 * Wikipedia is really confusing about this, but basically the algorithm works like this:
31 *
32 * Say, you want to evaluate 1 + 2 * 3 + 4. Intuitively, you "know" you have to do the 2 * 3 first,
33 * but how do you get this result?
34 * The key is to realize that when you're scanning the string from left to right, you will evaluate
35 * an operator when the operator that follows it has a lower (or equal to) precedence.
36 *
37 * In the context of the example, here's what you want to do:
38 *
39 * Look at: 1 + 2, don't do anything.
40 * Now look at 1 + 2 * 3, still don't do anything.
41 * Now look at 1 + 2 * 3 + 4, now you know that 2 * 3 has to to be evaluated because
42 * the next operator has lower precedence.
43 *
44 * How do you implement this?
45 *
46 * You want to have two stacks, one for numbers, and another for operators.
47 * You push numbers onto the stack all the time.
48 * You compare each new operator with the one at the top of the stack,
49 * if the one on top of the stack has higher priority, you pop it off the operator stack,
50 * pop the operands off the number stack, apply the operator and push the result onto the number stack.
51 *
52 * Now you repeat the comparison with the top of stack operator.
53 *
54 * Coming back to the example, it works like this:
55 *
56 * N = [ ] Ops = [ ]
57 *
58 * Read 1. N = [1], Ops = [ ]
59 * Read +. N = [1], Ops = [+]
60 * Read 2. N = [1 2], Ops = [+]
61 * Read *. N = [1 2], Ops = [+ *]
62 * Read 3. N = [1 2 3], Ops = [+ *]
63 * Read +. N = [1 2 3], Ops = [+ *]
64 * Pop 3, 2 and execute 2*3, and push result onto N. N = [1 6], Ops = [+]
65 * is left associative, so you want to pop 1, 6 off as well and execute the +. N = [7], Ops = [].
66 * Finally push the [+] onto the operator stack. N = [7], Ops = [+].
67 * Read 4. N = [7 4]. Ops = [+].
68 *
69 * You're run out off input, so you want to empty the stacks now.
70 * Upon which you will get the result 11.
71 */
72
73#include <am_param.h>
74
84
85 /*
86 The instructions ( subset of parm_item_type)
87----------------
88NOP : The no operation. the AM_PARAM_EVAL item stores a value.
89ADD
90SUB
91MUL
92DIV
93OPEN_PAR : Opening parenthesis: modify the precedence of operators inside ( and )
94CLOSE_PAR : Closing parenthesis: modify the precedence of operators by closing the local block.
95POPVALUE : used to initialize a sequence
96*/
97
99{
100 class OP_CODE // A small class to store a operator and its priority
101 {
102 public:
103 parm_item_type m_Optype;
104 int m_Priority;
105
106 OP_CODE( AM_PARAM_EVAL& aAmPrmEval )
107 : m_Optype( aAmPrmEval.GetOperator() ),
108 m_Priority( aAmPrmEval.GetPriority() )
109 {}
110
111 OP_CODE( parm_item_type aOptype )
112 : m_Optype( aOptype ), m_Priority( 0 )
113 {}
114 };
115
116 double result = 0.0;
117
118 std::vector<double> values; // the current list of values
119 std::vector<OP_CODE> optype; // the list of arith operators
120
121 double curr_value = 0.0;
122 int extra_priority = 0;
123
124 for( unsigned ii = 0; ii < aExp.size(); ii++ )
125 {
126 AM_PARAM_EVAL& prm = aExp[ii];
127
128 if( prm.IsOperator() )
129 {
130 if( prm.GetOperator() == OPEN_PAR )
131 {
132 extra_priority += AM_PARAM_EVAL::GetPriority( OPEN_PAR );
133 }
134 else if( prm.GetOperator() == CLOSE_PAR )
135 {
136 extra_priority -= AM_PARAM_EVAL::GetPriority( CLOSE_PAR );
137 }
138 else
139 {
140 optype.emplace_back( prm );
141 optype.back().m_Priority += extra_priority;
142 }
143 }
144 else // we have a value:
145 {
146 values.push_back( prm.GetValue() );
147
148 if( optype.size() < 2 )
149 continue;
150
151 OP_CODE& previous_optype = optype[optype.size() - 2];
152
153 if( optype.back().m_Priority > previous_optype.m_Priority )
154 {
155 double op1 = 0.0;
156
157 double op2 = values.back();
158 values.pop_back();
159
160 if( values.size() )
161 {
162 op1 = values.back();
163 values.pop_back();
164 }
165
166 switch( optype.back().m_Optype )
167 {
168 case ADD:
169 values.push_back( op1+op2 );
170 break;
171
172 case SUB:
173 values.push_back( op1-op2 );
174 break;
175
176 case MUL:
177 values.push_back( op1*op2 );
178 break;
179
180 case DIV:
181 values.push_back( op1/op2 );
182 break;
183
184 default:
185 break;
186 }
187
188 optype.pop_back();
189 }
190 }
191 }
192
193 // Now all operators have the same priority, or those having the higher priority
194 // are before others, calculate the final result by combining initial values and/or
195 // replaced values.
196 if( values.size() > optype.size() )
197 // If there are n values, the number of operator is n-1 or n if the first
198 // item of the expression to evaluate is + or - (like -$1/2)
199 // If the number of operator is n-1 the first value is just copied to result
200 optype.insert( optype.begin(), OP_CODE( POPVALUE ) );
201
202 wxASSERT( values.size() == optype.size() );
203
204 for( unsigned idx = 0; idx < values.size(); idx++ )
205 {
206 curr_value = values[idx];
207
208 switch( optype[idx].m_Optype )
209 {
210 case POPVALUE:
211 result = curr_value;
212 break;
213
214 case ADD:
215 result += curr_value;
216 break;
217
218 case SUB:
219 result -= curr_value;
220 break;
221
222 case MUL:
223 result *= curr_value;
224 break;
225
226 case DIV:
227 result /= curr_value;
228 break;
229
230 default:
231 break;
232 }
233 }
234
235 return result;
236}
parm_item_type
Definition am_param.h:145
@ OPEN_PAR
Definition am_param.h:146
@ MUL
Definition am_param.h:146
@ SUB
Definition am_param.h:146
@ CLOSE_PAR
Definition am_param.h:146
@ DIV
Definition am_param.h:146
@ ADD
Definition am_param.h:146
@ POPVALUE
Definition am_param.h:146
std::vector< AM_PARAM_EVAL > AM_PARAM_EVAL_STACK
Definition am_param.h:206
This helper class hold a value or an arithmetic operator to calculate the final value of a aperture m...
Definition am_param.h:157
parm_item_type GetOperator() const
Definition am_param.h:174
bool IsOperator() const
Definition am_param.h:172
double GetValue() const
Definition am_param.h:173
int GetPriority() const
Definition am_param.h:175
double Evaluate(AM_PARAM_EVAL_STACK &aExp)
Evaluate an basic arithmetic expression (infix notation) with precedence The expression is a sequence...
Definition evaluate.cpp:98
wxString result
Test unit parsing edge cases and error handling.