Coverage Report

Created: 2026-08-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/muparser/src/muParserBytecode.cpp
Line
Count
Source
1
/*
2
3
   _____  __ _____________ _______  ______ ___________
4
  /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
5
   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
6
   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
7
     \/      |__|       \/           \/     \/
8
   Copyright (C) 2026 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
#if defined(_MSC_VER)
43
  #pragma warning(push)
44
  #pragma warning(disable : 26812) 
45
#endif
46
47
48
namespace mu
49
{
50
  /** \brief Bytecode default constructor. */
51
  ParserByteCode::ParserByteCode()
52
6.60k
    : m_iStackPos(0)
53
6.60k
    , m_stringBuffer()
54
6.60k
    , m_expr()
55
6.60k
    , m_iMaxStackSize(0)
56
6.60k
    , m_vRPN()
57
6.60k
    , m_bEnableOptimizer(true)
58
6.60k
  {
59
6.60k
    m_vRPN.reserve(50);
60
6.60k
  }
61
62
63
  /** \brief Copy constructor.
64
65
    Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
66
  */
67
  ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode)
68
0
  {
69
0
    Assign(a_ByteCode);
70
0
  }
71
72
73
  /** \brief Assignment operator.
74
75
    Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
76
  */
77
  ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode)
78
0
  {
79
0
    Assign(a_ByteCode);
80
0
    return *this;
81
0
  }
82
83
84
  void ParserByteCode::EnableOptimizer(bool bStat)
85
0
  {
86
0
    m_bEnableOptimizer = bStat;
87
0
  }
88
89
90
  /** \brief Copy state of another object to this.
91
92
    \throw nowthrow
93
  */
94
  void ParserByteCode::Assign(const ParserByteCode& a_ByteCode)
95
0
  {
96
0
    if (this == &a_ByteCode)
97
0
      return;
98
99
0
    m_iStackPos = a_ByteCode.m_iStackPos;
100
0
    m_vRPN = a_ByteCode.m_vRPN;
101
0
    m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;
102
0
    m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;
103
    
104
0
    m_stringBuffer = a_ByteCode.m_stringBuffer;
105
0
    m_expr = a_ByteCode.m_expr;
106
0
  }
107
108
109
  /** \brief Add a Variable pointer to bytecode.
110
    \param a_pVar Pointer to be added.
111
    \throw nothrow
112
  */
113
  void ParserByteCode::AddVar(value_type* a_pVar)
114
621k
  {
115
621k
    ++m_iStackPos;
116
621k
    m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
117
118
    // optimization does not apply
119
621k
    SToken tok;
120
621k
    tok.Cmd = cmVAR;
121
621k
    tok.Val.ptr = a_pVar;
122
621k
    tok.Val.data = 1;
123
621k
    tok.Val.data2 = 0;
124
621k
    m_vRPN.push_back(tok);
125
621k
  }
126
127
128
  /** \brief Add a Variable pointer to bytecode.
129
130
    Value entries in byte code consist of:
131
    <ul>
132
      <li>value array position of the value</li>
133
      <li>the operator code according to ParserToken::cmVAL</li>
134
      <li>the value stored in #mc_iSizeVal number of bytecode entries.</li>
135
    </ul>
136
137
    \param a_pVal Value to be added.
138
    \throw nothrow
139
  */
140
  void ParserByteCode::AddVal(value_type a_fVal)
141
367k
  {
142
367k
    ++m_iStackPos;
143
367k
    m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
144
145
    // If optimization does not apply
146
367k
    SToken tok;
147
367k
    tok.Cmd = cmVAL;
148
367k
    tok.Val.ptr = nullptr;
149
367k
    tok.Val.data = 0;
150
367k
    tok.Val.data2 = a_fVal;
151
367k
    m_vRPN.push_back(tok);
152
367k
  }
153
154
155
  void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)
156
21.7k
  {
157
21.7k
    std::size_t sz = m_vRPN.size();
158
21.7k
    value_type& x = m_vRPN[sz - 2].Val.data2;
159
21.7k
    value_type& y = m_vRPN[sz - 1].Val.data2;
160
161
21.7k
    switch (a_Oprt)
162
21.7k
    {
163
1.31k
    case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break;
164
784
    case cmLOR:  x = (int)x || (int)y; m_vRPN.pop_back(); break;
165
1.76k
    case cmLT:   x = x < y;  m_vRPN.pop_back();  break;
166
1.14k
    case cmGT:   x = x > y;  m_vRPN.pop_back();  break;
167
698
    case cmLE:   x = x <= y; m_vRPN.pop_back();  break;
168
722
    case cmGE:   x = x >= y; m_vRPN.pop_back();  break;
169
490
    case cmNEQ:  x = x != y; m_vRPN.pop_back();  break;
170
425
    case cmEQ:   x = x == y; m_vRPN.pop_back();  break;
171
1.58k
    case cmADD:  x = x + y;  m_vRPN.pop_back();  break;
172
1.08k
    case cmSUB:  x = x - y;  m_vRPN.pop_back();  break;
173
963
    case cmMUL:  x = x * y;  m_vRPN.pop_back();  break;
174
2.28k
    case cmDIV:
175
2.28k
      if (y == 0)
176
690
        break;
177
1.59k
      x = x / y;
178
1.59k
      m_vRPN.pop_back();
179
1.59k
      break;
180
181
8.52k
    case cmPOW: x = MathImpl<value_type>::Pow(x, y);
182
8.52k
      m_vRPN.pop_back();
183
8.52k
      break;
184
185
0
    default:
186
0
      break;
187
21.7k
    } // switch opcode
188
21.7k
  }
189
190
191
  /** \brief Add an operator identifier to bytecode.
192
193
    Operator entries in byte code consist of:
194
    <ul>
195
      <li>value array position of the result</li>
196
      <li>the operator code according to ParserToken::ECmdCode</li>
197
    </ul>
198
199
    \sa  ParserToken::ECmdCode
200
  */
201
  void ParserByteCode::AddOp(ECmdCode a_Oprt)
202
177k
  {
203
177k
    bool bOptimized = false;
204
205
177k
    if (m_bEnableOptimizer)
206
177k
    {
207
177k
      std::size_t sz = m_vRPN.size();
208
209
      // Check for foldable constants like:
210
      //   cmVAL cmVAL cmADD 
211
      // where cmADD can stand fopr any binary operator applied to
212
      // two constant values.
213
177k
      if (sz >= 2 && m_vRPN[sz - 2].Cmd == cmVAL && m_vRPN[sz - 1].Cmd == cmVAL)
214
21.7k
      {
215
21.7k
        ConstantFolding(a_Oprt);
216
21.7k
        bOptimized = true;
217
21.7k
      }
218
155k
      else
219
155k
      {
220
155k
        switch (a_Oprt)
221
155k
        {
222
41.4k
        case  cmPOW:
223
          // Optimization for polynomials of low order
224
41.4k
          if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL)
225
3.41k
          {
226
3.41k
            if (m_vRPN[sz - 1].Val.data2 == 0)
227
236
            {
228
236
              m_vRPN[sz - 2].Cmd = cmVAL;
229
236
              m_vRPN[sz - 2].Val.ptr = nullptr;
230
236
              m_vRPN[sz - 2].Val.data = 0;
231
236
              m_vRPN[sz - 2].Val.data2 = 1;
232
236
            }
233
3.17k
            else if (m_vRPN[sz - 1].Val.data2 == 1)
234
407
              m_vRPN[sz - 2].Cmd = cmVAR;
235
2.76k
            else if (m_vRPN[sz - 1].Val.data2 == 2)
236
439
              m_vRPN[sz - 2].Cmd = cmVARPOW2;
237
2.33k
            else if (m_vRPN[sz - 1].Val.data2 == 3)
238
845
              m_vRPN[sz - 2].Cmd = cmVARPOW3;
239
1.48k
            else if (m_vRPN[sz - 1].Val.data2 == 4)
240
1.15k
              m_vRPN[sz - 2].Cmd = cmVARPOW4;
241
329
            else
242
329
              break;
243
244
3.08k
            m_vRPN.pop_back();
245
3.08k
            bOptimized = true;
246
3.08k
          }
247
41.1k
          break;
248
249
41.1k
        case  cmSUB:
250
45.7k
        case  cmADD:
251
          // Simple optimization based on pattern recognition for a shitload of different
252
          // bytecode combinations of addition/subtraction
253
45.7k
          if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
254
45.0k
            (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR) ||
255
36.4k
            (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
256
35.1k
            (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL) ||
257
34.9k
            (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
258
31.1k
            (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
259
26.0k
            (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
260
25.7k
            (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr))
261
20.4k
          {
262
20.4k
            MUP_ASSERT(
263
20.4k
              (m_vRPN[sz - 2].Val.ptr == nullptr && m_vRPN[sz - 1].Val.ptr != nullptr) ||
264
20.4k
              (m_vRPN[sz - 2].Val.ptr != nullptr && m_vRPN[sz - 1].Val.ptr == nullptr) ||
265
20.4k
              (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr));
266
267
20.4k
            m_vRPN[sz - 2].Cmd = cmVARMUL;
268
20.4k
            m_vRPN[sz - 2].Val.ptr = m_vRPN[sz - 2].Val.ptr ? m_vRPN[sz - 2].Val.ptr : m_vRPN[sz - 1].Val.ptr;    // variable
269
20.4k
            m_vRPN[sz - 2].Val.data2 += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data2;  // offset
270
20.4k
            m_vRPN[sz - 2].Val.data += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data;   // multiplicand
271
20.4k
            m_vRPN.pop_back();
272
20.4k
            bOptimized = true;
273
20.4k
          }
274
45.7k
          break;
275
276
45.7k
        case  cmMUL:
277
16.0k
          if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
278
14.3k
            (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR))
279
3.19k
          {
280
3.19k
            m_vRPN[sz - 2].Cmd = cmVARMUL;
281
3.19k
            m_vRPN[sz - 2].Val.ptr = m_vRPN[sz - 2].Val.ptr ? m_vRPN[sz - 2].Val.ptr : m_vRPN[sz - 1].Val.ptr;
282
3.19k
            m_vRPN[sz - 2].Val.data = m_vRPN[sz - 2].Val.data2 + m_vRPN[sz - 1].Val.data2;
283
3.19k
            m_vRPN[sz - 2].Val.data2 = 0;
284
3.19k
            m_vRPN.pop_back();
285
3.19k
            bOptimized = true;
286
3.19k
          }
287
12.8k
          else if (
288
12.8k
            (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
289
12.5k
            (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL))
290
476
          {
291
            // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2
292
476
            m_vRPN[sz - 2].Cmd = cmVARMUL;
293
476
            m_vRPN[sz - 2].Val.ptr = m_vRPN[sz - 2].Val.ptr ? m_vRPN[sz - 2].Val.ptr : m_vRPN[sz - 1].Val.ptr;
294
476
            if (m_vRPN[sz - 1].Cmd == cmVAL)
295
277
            {
296
277
              m_vRPN[sz - 2].Val.data *= m_vRPN[sz - 1].Val.data2;
297
277
              m_vRPN[sz - 2].Val.data2 *= m_vRPN[sz - 1].Val.data2;
298
277
            }
299
199
            else
300
199
            {
301
199
              m_vRPN[sz - 2].Val.data = m_vRPN[sz - 1].Val.data * m_vRPN[sz - 2].Val.data2;
302
199
              m_vRPN[sz - 2].Val.data2 = m_vRPN[sz - 1].Val.data2 * m_vRPN[sz - 2].Val.data2;
303
199
            }
304
476
            m_vRPN.pop_back();
305
476
            bOptimized = true;
306
476
          }
307
12.3k
          else if (
308
12.3k
            m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR &&
309
6.01k
            m_vRPN[sz - 1].Val.ptr == m_vRPN[sz - 2].Val.ptr)
310
5.80k
          {
311
            // Optimization: a*a -> a^2
312
5.80k
            m_vRPN[sz - 2].Cmd = cmVARPOW2;
313
5.80k
            m_vRPN.pop_back();
314
5.80k
            bOptimized = true;
315
5.80k
          }
316
16.0k
          break;
317
318
34.8k
        case cmDIV:
319
34.8k
          if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0)
320
254
          {
321
            // Optimization: 4*a/2 -> 2*a
322
254
            m_vRPN[sz - 2].Val.data /= m_vRPN[sz - 1].Val.data2;
323
254
            m_vRPN[sz - 2].Val.data2 /= m_vRPN[sz - 1].Val.data2;
324
254
            m_vRPN.pop_back();
325
254
            bOptimized = true;
326
254
          }
327
34.8k
          break;
328
329
          // no optimization for other opcodes
330
17.8k
        default:
331
17.8k
          break;
332
155k
        } // switch a_Oprt
333
155k
      }
334
177k
    }
335
336
    // If optimization can't be applied just write the value
337
177k
    if (!bOptimized)
338
122k
    {
339
122k
      --m_iStackPos;
340
122k
      SToken tok;
341
122k
      tok.Cmd = a_Oprt;
342
122k
      m_vRPN.push_back(tok);
343
122k
    }
344
177k
  }
345
346
347
  void ParserByteCode::AddIfElse(ECmdCode a_Oprt)
348
161k
  {
349
161k
    SToken tok;
350
161k
    tok.Cmd = a_Oprt;
351
161k
    m_vRPN.push_back(tok);
352
161k
  }
353
354
355
  /** \brief Add an assignment operator
356
357
    Operator entries in byte code consist of:
358
    <ul>
359
      <li>cmASSIGN code</li>
360
      <li>the pointer of the destination variable</li>
361
    </ul>
362
363
    \sa  ParserToken::ECmdCode
364
  */
365
  void ParserByteCode::AddAssignOp(value_type* a_pVar)
366
996
  {
367
996
    --m_iStackPos;
368
369
996
    SToken tok;
370
996
    tok.Cmd = cmASSIGN;
371
996
    tok.Oprt.ptr = a_pVar;
372
996
    m_vRPN.push_back(tok);
373
996
  }
374
375
376
  /** \brief Add function to bytecode.
377
378
    \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
379
    \param a_pFun Pointer to function callback.
380
  */
381
  void ParserByteCode::AddFun(generic_callable_type a_pFun, int a_iArgc, bool isFunctionOptimizable)
382
226k
  {
383
226k
    std::size_t sz = m_vRPN.size();
384
226k
    bool optimize = false;
385
386
    // only optimize functions with fixed number of more than a single arguments
387
226k
    if (isFunctionOptimizable && m_bEnableOptimizer && a_iArgc > 0)
388
58.0k
    {
389
      // <ibg 2020-06-10/> Unary Plus is a no-op, optimize it away
390
58.0k
      if (a_pFun == generic_callable_type{(erased_fun_type)&MathImpl<value_type>::UnaryPlus, nullptr})
391
1.12k
        return;
392
393
56.9k
      optimize = true;
394
395
66.5k
      for (int i = 0; i < std::abs(a_iArgc); ++i)
396
58.2k
      {
397
58.2k
        if (m_vRPN[sz - i - 1].Cmd != cmVAL)
398
48.5k
        {
399
48.5k
          optimize = false;
400
48.5k
          break;
401
48.5k
        }
402
58.2k
      }
403
56.9k
    }
404
405
225k
    if (optimize)
406
8.35k
    {
407
8.35k
      value_type val = 0;
408
8.35k
      switch (a_iArgc)
409
8.35k
      {
410
7.48k
      case 1:  val = a_pFun.call_fun<1>(m_vRPN[sz - 1].Val.data2); break;
411
583
      case 2:  val = a_pFun.call_fun<2>(m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
412
291
      case 3:  val = a_pFun.call_fun<3>(m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
413
0
      case 4:  val = a_pFun.call_fun<4>(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;
414
0
      case 5:  val = a_pFun.call_fun<5>(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;
415
0
      case 6:  val = a_pFun.call_fun<6>(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;
416
0
      case 7:  val = a_pFun.call_fun<7>(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;
417
0
      case 8:  val = a_pFun.call_fun<8>(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;
418
0
      case 9:  val = a_pFun.call_fun<9>(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;
419
0
      case 10: val = a_pFun.call_fun<10>(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;
420
0
      default:
421
        // For now functions with unlimited number of arguments are not optimized
422
0
        throw ParserError(ecINTERNAL_ERROR);
423
8.35k
      }
424
425
      // remove the folded values
426
8.35k
      m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end());
427
428
8.35k
      SToken tok;
429
8.35k
      tok.Cmd = cmVAL;
430
8.35k
      tok.Val.data = 0;
431
8.35k
      tok.Val.data2 = val;
432
8.35k
      tok.Val.ptr = nullptr;
433
8.35k
      m_vRPN.push_back(tok);
434
8.35k
    }
435
216k
    else
436
216k
    {
437
216k
      SToken tok;
438
216k
      tok.Cmd = cmFUNC;
439
216k
      tok.Fun.argc = a_iArgc;
440
216k
      tok.Fun.cb = a_pFun;
441
216k
      m_vRPN.push_back(tok);
442
216k
    }
443
444
225k
    m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1;
445
225k
    m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
446
447
225k
  }
448
449
450
  /** \brief Add a bulk function to bytecode.
451
452
    \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
453
    \param a_pFun Pointer to function callback.
454
  */
455
  void ParserByteCode::AddBulkFun(generic_callable_type a_pFun, int a_iArgc)
456
0
  {
457
0
    m_iStackPos = m_iStackPos - a_iArgc + 1;
458
0
    m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
459
460
0
    SToken tok;
461
0
    tok.Cmd = cmFUNC_BULK;
462
0
    tok.Fun.argc = a_iArgc;
463
0
    tok.Fun.cb = a_pFun;
464
0
    m_vRPN.push_back(tok);
465
0
  }
466
467
468
  /** \brief Add Strung function entry to the parser bytecode.
469
    \throw nothrow
470
471
    A string function entry consists of the stack position of the return value,
472
    followed by a cmSTRFUNC code, the function pointer and an index into the
473
    string buffer maintained by the parser.
474
  */
475
  void ParserByteCode::AddStrFun(generic_callable_type a_pFun, int a_iArgc, int a_iIdx)
476
0
  {
477
0
    m_iStackPos = m_iStackPos - a_iArgc + 1;
478
479
0
    SToken tok;
480
0
    tok.Cmd = cmFUNC_STR;
481
0
    tok.Fun.argc = a_iArgc;
482
0
    tok.Fun.idx = a_iIdx;
483
0
    tok.Fun.cb = a_pFun;
484
0
    m_vRPN.push_back(tok);
485
486
0
    m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
487
0
  }
488
489
490
  /** \brief Add end marker to bytecode.
491
492
    \throw nothrow
493
  */
494
  void ParserByteCode::Finalize()
495
4.27k
  {
496
4.27k
    SToken tok;
497
4.27k
    tok.Cmd = cmEND;
498
4.27k
    m_vRPN.push_back(tok);
499
4.27k
    rpn_type(m_vRPN).swap(m_vRPN);     // shrink bytecode vector to fit
500
501
    // Determine the if-then-else jump offsets
502
4.27k
    std::stack<int> stIf, stElse;
503
4.27k
    int idx;
504
1.09M
    for (int i = 0; i < (int)m_vRPN.size(); ++i)
505
1.09M
    {
506
1.09M
      switch (m_vRPN[i].Cmd)
507
1.09M
      {
508
59.8k
      case cmIF:
509
59.8k
        stIf.push(i);
510
59.8k
        break;
511
512
42.8k
      case cmELSE:
513
42.8k
        stElse.push(i);
514
42.8k
        if (stIf.empty())
515
0
          throw ParserError(ecINTERNAL_ERROR);
516
42.8k
        idx = stIf.top();
517
42.8k
        stIf.pop();
518
42.8k
        m_vRPN[idx].Oprt.offset = i - idx;
519
42.8k
        break;
520
521
11.6k
      case cmENDIF:
522
11.6k
        if (stElse.empty())
523
0
          throw ParserError(ecINTERNAL_ERROR);
524
11.6k
        idx = stElse.top();
525
11.6k
        stElse.pop();
526
11.6k
        m_vRPN[idx].Oprt.offset = i - idx;
527
11.6k
        break;
528
529
980k
      default:
530
980k
        break;
531
1.09M
      }
532
1.09M
    }
533
4.27k
  }
534
535
536
  std::size_t ParserByteCode::GetMaxStackSize() const
537
4.00k
  {
538
4.00k
    return m_iMaxStackSize + 1;
539
4.00k
  }
540
541
542
  /** \brief Delete the bytecode.
543
544
    \throw nothrow
545
546
    The name of this function is a violation of my own coding guidelines
547
    but this way it's more in line with the STL functions thus more
548
    intuitive.
549
  */
550
  void ParserByteCode::clear()
551
248k
  {
552
248k
    m_vRPN.clear();
553
248k
    m_iStackPos = 0;
554
248k
    m_iMaxStackSize = 0;
555
248k
  }
556
557
558
  /** \brief Dump bytecode (for debugging only!). */
559
  void ParserByteCode::AsciiDump() const
560
0
  {
561
0
    if (!m_vRPN.size())
562
0
    {
563
0
      mu::console() << _T("No bytecode available\n");
564
0
      return;
565
0
    }
566
567
0
    mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n");
568
0
    for (std::size_t i = 0; i < m_vRPN.size() && m_vRPN[i].Cmd != cmEND; ++i)
569
0
    {
570
0
      mu::console() << std::dec << i << _T(" : \t");
571
0
      switch (m_vRPN[i].Cmd)
572
0
      {
573
0
      case cmVAL:   mu::console() << _T("VAL \t");
574
0
        mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n");
575
0
        break;
576
577
0
      case cmVAR:   mu::console() << _T("VAR \t");
578
0
        mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
579
0
        break;
580
581
0
      case cmVARPOW2: mu::console() << _T("VARPOW2 \t");
582
0
        mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
583
0
        break;
584
585
0
      case cmVARPOW3: mu::console() << _T("VARPOW3 \t");
586
0
        mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
587
0
        break;
588
589
0
      case cmVARPOW4: mu::console() << _T("VARPOW4 \t");
590
0
        mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
591
0
        break;
592
593
0
      case cmVARMUL:  mu::console() << _T("VARMUL \t");
594
0
        mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]");
595
0
        mu::console() << _T(" * [") << m_vRPN[i].Val.data << _T("]");
596
0
        mu::console() << _T(" + [") << m_vRPN[i].Val.data2 << _T("]\n");
597
0
        break;
598
599
0
      case cmFUNC:  mu::console() << _T("CALL\t");
600
0
        mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
601
0
        mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.cb._pRawFun) << _T("]");
602
0
        mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.cb._pUserData) << _T("]");
603
0
        mu::console() << _T("\n");
604
0
        break;
605
606
0
      case cmFUNC_STR:
607
0
        mu::console() << _T("CALL STRFUNC\t");
608
0
        mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]");
609
610
0
        {
611
0
          int idx = m_vRPN[i].Fun.idx;
612
0
          if (idx < 0 || idx >= (int)m_stringBuffer.size())
613
0
            throw ParserError(ecINTERNAL_ERROR);
614
615
0
          mu::console() << _T("[IDX:") << std::dec << idx << _T("=\"") << m_stringBuffer[idx] << ("\"]");
616
0
        }
617
0
        mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.cb._pRawFun) << _T("]");
618
0
        mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast<void*>(m_vRPN[i].Fun.cb._pUserData) << _T("]");
619
0
        mu::console() << _T("\n");
620
0
        break;
621
622
0
      case cmLT:    mu::console() << _T("LT\n");  break;
623
0
      case cmGT:    mu::console() << _T("GT\n");  break;
624
0
      case cmLE:    mu::console() << _T("LE\n");  break;
625
0
      case cmGE:    mu::console() << _T("GE\n");  break;
626
0
      case cmEQ:    mu::console() << _T("EQ\n");  break;
627
0
      case cmNEQ:   mu::console() << _T("NEQ\n"); break;
628
0
      case cmADD:   mu::console() << _T("ADD\n"); break;
629
0
      case cmLAND:  mu::console() << _T("&&\n"); break;
630
0
      case cmLOR:   mu::console() << _T("||\n"); break;
631
0
      case cmSUB:   mu::console() << _T("SUB\n"); break;
632
0
      case cmMUL:   mu::console() << _T("MUL\n"); break;
633
0
      case cmDIV:   mu::console() << _T("DIV\n"); break;
634
0
      case cmPOW:   mu::console() << _T("POW\n"); break;
635
636
0
      case cmIF:    mu::console() << _T("IF\t");
637
0
        mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
638
0
        break;
639
640
0
      case cmELSE:  mu::console() << _T("ELSE\t");
641
0
        mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
642
0
        break;
643
644
0
      case cmENDIF: mu::console() << _T("ENDIF\n"); break;
645
646
0
      case cmASSIGN:
647
0
        mu::console() << _T("ASSIGN\t");
648
0
        mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n");
649
0
        break;
650
651
0
      default:      mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n");
652
0
        break;
653
0
      } // switch cmdCode
654
0
    } // while bytecode
655
656
0
    mu::console() << _T("END") << std::endl;
657
0
  }
658
} // namespace mu
659
660
#if defined(_MSC_VER)
661
  #pragma warning(pop)
662
#endif