Add HeFFTe based FFT backend
[alexxy/gromacs.git] / src / external / muparser / src / muParserBytecode.cpp
1 /*
2
3          _____  __ _____________ _______  ______ ___________
4         /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
5    |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
6    |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
7                  \/      |__|       \/           \/     \/
8    Copyright (C) 2004 - 2020 Ingo Berg
9
10         Redistribution and use in source and binary forms, with or without modification, are permitted
11         provided that the following conditions are met:
12
13           * Redistributions of source code must retain the above copyright notice, this list of
14                 conditions and the following disclaimer.
15           * Redistributions in binary form must reproduce the above copyright notice, this list of
16                 conditions and the following disclaimer in the documentation and/or other materials provided
17                 with the distribution.
18
19         THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20         IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21         FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22         CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23         DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24         DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25         IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26         OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "muParserBytecode.h"
30
31 #include <algorithm>
32 #include <string>
33 #include <stack>
34 #include <vector>
35 #include <iostream>
36
37 #include "muParserDef.h"
38 #include "muParserError.h"
39 #include "muParserToken.h"
40 #include "muParserTemplateMagic.h"
41
42
43 namespace mu
44 {
45         /** \brief Bytecode default constructor. */
46         ParserByteCode::ParserByteCode()
47                 :m_iStackPos(0)
48                 , m_iMaxStackSize(0)
49                 , m_vRPN()
50                 , m_bEnableOptimizer(true)
51         {
52                 m_vRPN.reserve(50);
53         }
54
55
56         /** \brief Copy constructor.
57
58                 Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
59         */
60         ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode)
61         {
62                 Assign(a_ByteCode);
63         }
64
65
66         /** \brief Assignment operator.
67
68                 Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
69         */
70         ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode)
71         {
72                 Assign(a_ByteCode);
73                 return *this;
74         }
75
76
77         void ParserByteCode::EnableOptimizer(bool bStat)
78         {
79                 m_bEnableOptimizer = bStat;
80         }
81
82
83         /** \brief Copy state of another object to this.
84
85                 \throw nowthrow
86         */
87         void ParserByteCode::Assign(const ParserByteCode& a_ByteCode)
88         {
89                 if (this == &a_ByteCode)
90                         return;
91
92                 m_iStackPos = a_ByteCode.m_iStackPos;
93                 m_vRPN = a_ByteCode.m_vRPN;
94                 m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;
95                 m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;
96         }
97
98
99         /** \brief Add a Variable pointer to bytecode.
100                 \param a_pVar Pointer to be added.
101                 \throw nothrow
102         */
103         void ParserByteCode::AddVar(value_type* a_pVar)
104         {
105                 ++m_iStackPos;
106                 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
107
108                 // optimization does not apply
109                 SToken tok;
110                 tok.Cmd = cmVAR;
111                 tok.Val.ptr = a_pVar;
112                 tok.Val.data = 1;
113                 tok.Val.data2 = 0;
114                 m_vRPN.push_back(tok);
115         }
116
117
118         /** \brief Add a Variable pointer to bytecode.
119
120                 Value entries in byte code consist of:
121                 <ul>
122                   <li>value array position of the value</li>
123                   <li>the operator code according to ParserToken::cmVAL</li>
124                   <li>the value stored in #mc_iSizeVal number of bytecode entries.</li>
125                 </ul>
126
127                 \param a_pVal Value to be added.
128                 \throw nothrow
129         */
130         void ParserByteCode::AddVal(value_type a_fVal)
131         {
132                 ++m_iStackPos;
133                 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
134
135                 // If optimization does not apply
136                 SToken tok;
137                 tok.Cmd = cmVAL;
138                 tok.Val.ptr = nullptr;
139                 tok.Val.data = 0;
140                 tok.Val.data2 = a_fVal;
141                 m_vRPN.push_back(tok);
142         }
143
144
145         void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)
146         {
147                 std::size_t sz = m_vRPN.size();
148                 value_type& x = m_vRPN[sz - 2].Val.data2;
149                 value_type& y = m_vRPN[sz - 1].Val.data2;
150
151                 switch (a_Oprt)
152                 {
153                 case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break;
154                 case cmLOR:  x = (int)x || (int)y; m_vRPN.pop_back(); break;
155                 case cmLT:   x = x < y;  m_vRPN.pop_back();  break;
156                 case cmGT:   x = x > y;  m_vRPN.pop_back();  break;
157                 case cmLE:   x = x <= y; m_vRPN.pop_back();  break;
158                 case cmGE:   x = x >= y; m_vRPN.pop_back();  break;
159                 case cmNEQ:  x = x != y; m_vRPN.pop_back();  break;
160                 case cmEQ:   x = x == y; m_vRPN.pop_back();  break;
161                 case cmADD:  x = x + y;  m_vRPN.pop_back();  break;
162                 case cmSUB:  x = x - y;  m_vRPN.pop_back();  break;
163                 case cmMUL:  x = x * y;  m_vRPN.pop_back();  break;
164                 case cmDIV:
165                         x = x / y;
166                         m_vRPN.pop_back();
167                         break;
168
169                 case cmPOW: x = MathImpl<value_type>::Pow(x, y);
170                         m_vRPN.pop_back();
171                         break;
172
173                 default:
174                         break;
175                 } // switch opcode
176         }
177
178
179         /** \brief Add an operator identifier to bytecode.
180
181                 Operator entries in byte code consist of:
182                 <ul>
183                   <li>value array position of the result</li>
184                   <li>the operator code according to ParserToken::ECmdCode</li>
185                 </ul>
186
187                 \sa  ParserToken::ECmdCode
188         */
189         void ParserByteCode::AddOp(ECmdCode a_Oprt)
190         {
191                 bool bOptimized = false;
192
193                 if (m_bEnableOptimizer)
194                 {
195                         std::size_t sz = m_vRPN.size();
196
197                         // Check for foldable constants like:
198                         //   cmVAL cmVAL cmADD 
199                         // where cmADD can stand fopr any binary operator applied to
200                         // two constant values.
201                         if (sz >= 2 && m_vRPN[sz - 2].Cmd == cmVAL && m_vRPN[sz - 1].Cmd == cmVAL)
202                         {
203                                 ConstantFolding(a_Oprt);
204                                 bOptimized = true;
205                         }
206                         else
207                         {
208                                 switch (a_Oprt)
209                                 {
210                                 case  cmPOW:
211                                         // Optimization for polynomials of low order
212                                         if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL)
213                                         {
214                                                 if (m_vRPN[sz - 1].Val.data2 == 0)
215                                                 {
216                                                         m_vRPN[sz - 2].Cmd = cmVAL;
217                                                         m_vRPN[sz - 2].Val.ptr = nullptr;
218                                                         m_vRPN[sz - 2].Val.data = 0;
219                                                         m_vRPN[sz - 2].Val.data2 = 1;
220                                                 }
221                                                 else if (m_vRPN[sz - 1].Val.data2 == 1)
222                                                         m_vRPN[sz - 2].Cmd = cmVAR;
223                                                 else if (m_vRPN[sz - 1].Val.data2 == 2)
224                                                         m_vRPN[sz - 2].Cmd = cmVARPOW2;
225                                                 else if (m_vRPN[sz - 1].Val.data2 == 3)
226                                                         m_vRPN[sz - 2].Cmd = cmVARPOW3;
227                                                 else if (m_vRPN[sz - 1].Val.data2 == 4)
228                                                         m_vRPN[sz - 2].Cmd = cmVARPOW4;
229                                                 else
230                                                         break;
231
232                                                 m_vRPN.pop_back();
233                                                 bOptimized = true;
234                                         }
235                                         break;
236
237                                 case  cmSUB:
238                                 case  cmADD:
239                                         // Simple optimization based on pattern recognition for a shitload of different
240                                         // bytecode combinations of addition/subtraction
241                                         if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
242                                                 (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR) ||
243                                                 (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
244                                                 (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL) ||
245                                                 (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
246                                                 (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
247                                                 (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
248                                                 (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr))
249                                         {
250                                                 MUP_ASSERT(
251                                                         (m_vRPN[sz - 2].Val.ptr == nullptr && m_vRPN[sz - 1].Val.ptr != nullptr) ||
252                                                         (m_vRPN[sz - 2].Val.ptr != nullptr && m_vRPN[sz - 1].Val.ptr == nullptr) ||
253                                                         (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr));
254
255                                                 m_vRPN[sz - 2].Cmd = cmVARMUL;
256                                                 m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));    // variable
257                                                 m_vRPN[sz - 2].Val.data2 += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data2;  // offset
258                                                 m_vRPN[sz - 2].Val.data += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data;   // multiplicand
259                                                 m_vRPN.pop_back();
260                                                 bOptimized = true;
261                                         }
262                                         break;
263
264                                 case  cmMUL:
265                                         if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
266                                                 (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR))
267                                         {
268                                                 m_vRPN[sz - 2].Cmd = cmVARMUL;
269                                                 m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
270                                                 m_vRPN[sz - 2].Val.data = m_vRPN[sz - 2].Val.data2 + m_vRPN[sz - 1].Val.data2;
271                                                 m_vRPN[sz - 2].Val.data2 = 0;
272                                                 m_vRPN.pop_back();
273                                                 bOptimized = true;
274                                         }
275                                         else if (
276                                                 (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
277                                                 (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL))
278                                         {
279                                                 // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2
280                                                 m_vRPN[sz - 2].Cmd = cmVARMUL;
281                                                 m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
282                                                 if (m_vRPN[sz - 1].Cmd == cmVAL)
283                                                 {
284                                                         m_vRPN[sz - 2].Val.data *= m_vRPN[sz - 1].Val.data2;
285                                                         m_vRPN[sz - 2].Val.data2 *= m_vRPN[sz - 1].Val.data2;
286                                                 }
287                                                 else
288                                                 {
289                                                         m_vRPN[sz - 2].Val.data = m_vRPN[sz - 1].Val.data * m_vRPN[sz - 2].Val.data2;
290                                                         m_vRPN[sz - 2].Val.data2 = m_vRPN[sz - 1].Val.data2 * m_vRPN[sz - 2].Val.data2;
291                                                 }
292                                                 m_vRPN.pop_back();
293                                                 bOptimized = true;
294                                         }
295                                         else if (
296                                                 m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR &&
297                                                 m_vRPN[sz - 1].Val.ptr == m_vRPN[sz - 2].Val.ptr)
298                                         {
299                                                 // Optimization: a*a -> a^2
300                                                 m_vRPN[sz - 2].Cmd = cmVARPOW2;
301                                                 m_vRPN.pop_back();
302                                                 bOptimized = true;
303                                         }
304                                         break;
305
306                                 case cmDIV:
307                                         if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0)
308                                         {
309                                                 // Optimization: 4*a/2 -> 2*a
310                                                 m_vRPN[sz - 2].Val.data /= m_vRPN[sz - 1].Val.data2;
311                                                 m_vRPN[sz - 2].Val.data2 /= m_vRPN[sz - 1].Val.data2;
312                                                 m_vRPN.pop_back();
313                                                 bOptimized = true;
314                                         }
315                                         break;
316
317                                         // no optimization for other opcodes
318                                 default:
319                                         break;
320                                 } // switch a_Oprt
321                         }
322                 }
323
324                 // If optimization can't be applied just write the value
325                 if (!bOptimized)
326                 {
327                         --m_iStackPos;
328                         SToken tok;
329                         tok.Cmd = a_Oprt;
330                         m_vRPN.push_back(tok);
331                 }
332         }
333
334
335         void ParserByteCode::AddIfElse(ECmdCode a_Oprt)
336         {
337                 SToken tok;
338                 tok.Cmd = a_Oprt;
339                 m_vRPN.push_back(tok);
340         }
341
342
343         /** \brief Add an assignment operator
344
345                 Operator entries in byte code consist of:
346                 <ul>
347                   <li>cmASSIGN code</li>
348                   <li>the pointer of the destination variable</li>
349                 </ul>
350
351                 \sa  ParserToken::ECmdCode
352         */
353         void ParserByteCode::AddAssignOp(value_type* a_pVar)
354         {
355                 --m_iStackPos;
356
357                 SToken tok;
358                 tok.Cmd = cmASSIGN;
359                 tok.Oprt.ptr = a_pVar;
360                 m_vRPN.push_back(tok);
361         }
362
363
364         /** \brief Add function to bytecode.
365
366                 \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
367                 \param a_pFun Pointer to function callback.
368         */
369         void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc)
370         {
371                 std::size_t sz = m_vRPN.size();
372                 bool optimize = false;
373
374                 // only optimize functions with fixed number of more than a single arguments
375                 if (m_bEnableOptimizer && a_iArgc > 0)
376                 {
377                         // <ibg 2020-06-10/> Unary Plus is a no-op
378                         if ((void*)a_pFun == (void*)&MathImpl<value_type>::UnaryPlus)
379                                 return;
380
381                         optimize = true;
382
383                         for (int i = 0; i < std::abs(a_iArgc); ++i)
384                         {
385                                 if (m_vRPN[sz - i - 1].Cmd != cmVAL)
386                                 {
387                                         optimize = false;
388                                         break;
389                                 }
390                         }
391                 }
392
393                 if (optimize)
394                 {
395                         value_type val = 0;
396                         switch (a_iArgc)
397                         {
398                         case 1:  val = (*reinterpret_cast<fun_type1>(a_pFun))(m_vRPN[sz - 1].Val.data2);   break;
399                         case 2:  val = (*reinterpret_cast<fun_type2>(a_pFun))(m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
400                         case 3:  val = (*reinterpret_cast<fun_type3>(a_pFun))(m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
401                         case 4:  val = (*reinterpret_cast<fun_type4>(a_pFun))(m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
402                         case 5:  val = (*reinterpret_cast<fun_type5>(a_pFun))(m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
403                         case 6:  val = (*reinterpret_cast<fun_type6>(a_pFun))(m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
404                         case 7:  val = (*reinterpret_cast<fun_type7>(a_pFun))(m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
405                         case 8:  val = (*reinterpret_cast<fun_type8>(a_pFun))(m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
406                         case 9:  val = (*reinterpret_cast<fun_type9>(a_pFun))(m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
407                         case 10: val = (*reinterpret_cast<fun_type10>(a_pFun))(m_vRPN[sz - 10].Val.data2, m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
408                         default:
409                                 // For now functions with unlimited number of arguments are not optimized
410                                 throw ParserError(ecINTERNAL_ERROR);
411                         }
412
413                         // remove the folded values
414                         m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end());
415
416                         SToken tok;
417                         tok.Cmd = cmVAL;
418                         tok.Val.data = 0;
419                         tok.Val.data2 = val;
420                         tok.Val.ptr = nullptr;
421                         m_vRPN.push_back(tok);
422                 }
423                 else
424                 {
425                         SToken tok;
426                         tok.Cmd = cmFUNC;
427                         tok.Fun.argc = a_iArgc;
428                         tok.Fun.ptr = a_pFun;
429                         m_vRPN.push_back(tok);
430                 }
431
432                 m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1;
433                 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
434
435         }
436
437
438         /** \brief Add a bulk function to bytecode.
439
440                 \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
441                 \param a_pFun Pointer to function callback.
442         */
443         void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc)
444         {
445                 m_iStackPos = m_iStackPos - a_iArgc + 1;
446                 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
447
448                 SToken tok;
449                 tok.Cmd = cmFUNC_BULK;
450                 tok.Fun.argc = a_iArgc;
451                 tok.Fun.ptr = a_pFun;
452                 m_vRPN.push_back(tok);
453         }
454
455
456         /** \brief Add Strung function entry to the parser bytecode.
457                 \throw nothrow
458
459                 A string function entry consists of the stack position of the return value,
460                 followed by a cmSTRFUNC code, the function pointer and an index into the
461                 string buffer maintained by the parser.
462         */
463         void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx)
464         {
465                 m_iStackPos = m_iStackPos - a_iArgc + 1;
466
467                 SToken tok;
468                 tok.Cmd = cmFUNC_STR;
469                 tok.Fun.argc = a_iArgc;
470                 tok.Fun.idx = a_iIdx;
471                 tok.Fun.ptr = a_pFun;
472                 m_vRPN.push_back(tok);
473
474                 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
475         }
476
477
478         /** \brief Add end marker to bytecode.
479
480                 \throw nothrow
481         */
482         void ParserByteCode::Finalize()
483         {
484                 SToken tok;
485                 tok.Cmd = cmEND;
486                 m_vRPN.push_back(tok);
487                 rpn_type(m_vRPN).swap(m_vRPN);     // shrink bytecode vector to fit
488
489                 // Determine the if-then-else jump offsets
490                 std::stack<int> stIf, stElse;
491                 int idx;
492                 for (int i = 0; i < (int)m_vRPN.size(); ++i)
493                 {
494                         switch (m_vRPN[i].Cmd)
495                         {
496                         case cmIF:
497                                 stIf.push(i);
498                                 break;
499
500                         case cmELSE:
501                                 stElse.push(i);
502                                 idx = stIf.top();
503                                 stIf.pop();
504                                 m_vRPN[idx].Oprt.offset = i - idx;
505                                 break;
506
507                         case cmENDIF:
508                                 idx = stElse.top();
509                                 stElse.pop();
510                                 m_vRPN[idx].Oprt.offset = i - idx;
511                                 break;
512
513                         default:
514                                 break;
515                         }
516                 }
517         }
518
519
520         std::size_t ParserByteCode::GetMaxStackSize() const
521         {
522                 return m_iMaxStackSize + 1;
523         }
524
525
526         /** \brief Delete the bytecode.
527
528                 \throw nothrow
529
530                 The name of this function is a violation of my own coding guidelines
531                 but this way it's more in line with the STL functions thus more
532                 intuitive.
533         */
534         void ParserByteCode::clear()
535         {
536                 m_vRPN.clear();
537                 m_iStackPos = 0;
538                 m_iMaxStackSize = 0;
539         }
540
541
542         /** \brief Dump bytecode (for debugging only!). */
543         void ParserByteCode::AsciiDump()
544         {
545                 if (!m_vRPN.size())
546                 {
547                         mu::console() << _T("No bytecode available\n");
548                         return;
549                 }
550
551                 mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n");
552                 for (std::size_t i = 0; i < m_vRPN.size() && m_vRPN[i].Cmd != cmEND; ++i)
553                 {
554                         mu::console() << std::dec << i << _T(" : \t");
555                         switch (m_vRPN[i].Cmd)
556                         {
557                         case cmVAL:   mu::console() << _T("VAL \t");
558                                 mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n");
559                                 break;
560
561                         case cmVAR:   mu::console() << _T("VAR \t");
562                                 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
563                                 break;
564
565                         case cmVARPOW2: mu::console() << _T("VARPOW2 \t");
566                                 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
567                                 break;
568
569                         case cmVARPOW3: mu::console() << _T("VARPOW3 \t");
570                                 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
571                                 break;
572
573                         case cmVARPOW4: mu::console() << _T("VARPOW4 \t");
574                                 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
575                                 break;
576
577                         case cmVARMUL:  mu::console() << _T("VARMUL \t");
578                                 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]");
579                                 mu::console() << _T(" * [") << m_vRPN[i].Val.data << _T("]");
580                                 mu::console() << _T(" + [") << m_vRPN[i].Val.data2 << _T("]\n");
581                                 break;
582
583                         case cmFUNC:  mu::console() << _T("CALL\t");
584                                 mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
585                                 mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.ptr) << _T("]");
586                                 mu::console() << _T("\n");
587                                 break;
588
589                         case cmFUNC_STR:
590                                 mu::console() << _T("CALL STRFUNC\t");
591                                 mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
592                                 mu::console() << _T("[IDX:") << std::dec << m_vRPN[i].Fun.idx << _T("]");
593                                 mu::console() << _T("[ADDR: 0x") << reinterpret_cast<void*>(m_vRPN[i].Fun.ptr) << _T("]\n");
594                                 break;
595
596                         case cmLT:    mu::console() << _T("LT\n");  break;
597                         case cmGT:    mu::console() << _T("GT\n");  break;
598                         case cmLE:    mu::console() << _T("LE\n");  break;
599                         case cmGE:    mu::console() << _T("GE\n");  break;
600                         case cmEQ:    mu::console() << _T("EQ\n");  break;
601                         case cmNEQ:   mu::console() << _T("NEQ\n"); break;
602                         case cmADD:   mu::console() << _T("ADD\n"); break;
603                         case cmLAND:  mu::console() << _T("&&\n"); break;
604                         case cmLOR:   mu::console() << _T("||\n"); break;
605                         case cmSUB:   mu::console() << _T("SUB\n"); break;
606                         case cmMUL:   mu::console() << _T("MUL\n"); break;
607                         case cmDIV:   mu::console() << _T("DIV\n"); break;
608                         case cmPOW:   mu::console() << _T("POW\n"); break;
609
610                         case cmIF:    mu::console() << _T("IF\t");
611                                 mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
612                                 break;
613
614                         case cmELSE:  mu::console() << _T("ELSE\t");
615                                 mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
616                                 break;
617
618                         case cmENDIF: mu::console() << _T("ENDIF\n"); break;
619
620                         case cmASSIGN:
621                                 mu::console() << _T("ASSIGN\t");
622                                 mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n");
623                                 break;
624
625                         default:      mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n");
626                                 break;
627                         } // switch cmdCode
628                 } // while bytecode
629
630                 mu::console() << _T("END") << std::endl;
631         }
632 } // namespace mu