muParser API -  1.35
Public Member Functions | List of all members
mu::ParserByteCode Class Referencefinal

Bytecode implementation of the Math Parser. More...

#include <muParserBytecode.h>

Public Member Functions

 ParserByteCode ()
 Bytecode default constructor.
 
 ParserByteCode (const ParserByteCode &a_ByteCode)
 Copy constructor. More...
 
ParserByteCodeoperator= (const ParserByteCode &a_ByteCode)
 Assignment operator. More...
 
void Assign (const ParserByteCode &a_ByteCode)
 Copy state of another object to this. More...
 
void AddVar (value_type *a_pVar)
 Add a Variable pointer to bytecode. More...
 
void AddVal (value_type a_fVal)
 Add a Variable pointer to bytecode. More...
 
void AddOp (ECmdCode a_Oprt)
 Add an operator identifier to bytecode. More...
 
void AddIfElse (ECmdCode a_Oprt)
 
void AddAssignOp (value_type *a_pVar)
 Add an assignment operator. More...
 
void AddFun (generic_callable_type a_pFun, int a_iArgc, bool isOptimizable)
 Add function to bytecode. More...
 
void AddBulkFun (generic_callable_type a_pFun, int a_iArgc)
 Add a bulk function to bytecode. More...
 
void AddStrFun (generic_callable_type a_pFun, int a_iArgc, int a_iIdx)
 Add Strung function entry to the parser bytecode. More...
 
void EnableOptimizer (bool bStat)
 
void Finalize ()
 Add end marker to bytecode. More...
 
void clear ()
 Delete the bytecode. More...
 
std::size_t GetMaxStackSize () const
 
std::size_t GetSize () const
 
const STokenGetBase () const
 
void AsciiDump ()
 Dump bytecode (for debugging only!).
 

Detailed Description

Bytecode implementation of the Math Parser.

The bytecode contains the formula converted to revers polish notation stored in a continious memory area. Associated with this data are operator codes, variable pointers, constant values and function pointers. Those are necessary in order to calculate the result. All those data items will be casted to the underlying datatype of the bytecode.

Definition at line 87 of file muParserBytecode.h.

Constructor & Destructor Documentation

mu::ParserByteCode::ParserByteCode ( const ParserByteCode a_ByteCode)

Copy constructor.

Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)

Definition at line 65 of file muParserBytecode.cpp.

66  {
67  Assign(a_ByteCode);
68  }
void Assign(const ParserByteCode &a_ByteCode)
Copy state of another object to this.

Member Function Documentation

void mu::ParserByteCode::AddAssignOp ( value_type a_pVar)

Add an assignment operator.

Operator entries in byte code consist of:

  • cmASSIGN code
  • the pointer of the destination variable
See also
ParserToken::ECmdCode

Definition at line 358 of file muParserBytecode.cpp.

359  {
360  --m_iStackPos;
361 
362  SToken tok;
363  tok.Cmd = cmASSIGN;
364  tok.Oprt.ptr = a_pVar;
365  m_vRPN.push_back(tok);
366  }
Operator item: Assignment operator.
Definition: muParserDef.h:153
void mu::ParserByteCode::AddBulkFun ( generic_callable_type  a_pFun,
int  a_iArgc 
)

Add a bulk function to bytecode.

Parameters
a_iArgcNumber of arguments, negative numbers indicate multiarg functions.
a_pFunPointer to function callback.

Definition at line 448 of file muParserBytecode.cpp.

449  {
450  m_iStackPos = m_iStackPos - a_iArgc + 1;
451  m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
452 
453  SToken tok;
454  tok.Cmd = cmFUNC_BULK;
455  tok.Fun.argc = a_iArgc;
456  tok.Fun.cb = a_pFun;
457  m_vRPN.push_back(tok);
458  }
Special callbacks for Bulk mode with an additional parameter for the bulk index.
Definition: muParserDef.h:172
void mu::ParserByteCode::AddFun ( generic_callable_type  a_pFun,
int  a_iArgc,
bool  isFunctionOptimizable 
)

Add function to bytecode.

Parameters
a_iArgcNumber of arguments, negative numbers indicate multiarg functions.
a_pFunPointer to function callback.

Definition at line 374 of file muParserBytecode.cpp.

375  {
376  std::size_t sz = m_vRPN.size();
377  bool optimize = false;
378 
379  // only optimize functions with fixed number of more than a single arguments
380  if (isFunctionOptimizable && m_bEnableOptimizer && a_iArgc > 0)
381  {
382  // <ibg 2020-06-10/> Unary Plus is a no-op
383  if (a_pFun == generic_callable_type{(erased_fun_type)&MathImpl<value_type>::UnaryPlus, nullptr})
384  return;
385 
386  optimize = true;
387 
388  for (int i = 0; i < std::abs(a_iArgc); ++i)
389  {
390  if (m_vRPN[sz - i - 1].Cmd != cmVAL)
391  {
392  optimize = false;
393  break;
394  }
395  }
396  }
397 
398  if (optimize)
399  {
400  value_type val = 0;
401  switch (a_iArgc)
402  {
403  case 1: val = a_pFun.call_fun<1>(m_vRPN[sz - 1].Val.data2); break;
404  case 2: val = a_pFun.call_fun<2>(m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break;
405  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;
406  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;
407  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;
408  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;
409  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;
410  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;
411  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;
412  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;
413  default:
414  // For now functions with unlimited number of arguments are not optimized
415  throw ParserError(ecINTERNAL_ERROR);
416  }
417 
418  // remove the folded values
419  m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end());
420 
421  SToken tok;
422  tok.Cmd = cmVAL;
423  tok.Val.data = 0;
424  tok.Val.data2 = val;
425  tok.Val.ptr = nullptr;
426  m_vRPN.push_back(tok);
427  }
428  else
429  {
430  SToken tok;
431  tok.Cmd = cmFUNC;
432  tok.Fun.argc = a_iArgc;
433  tok.Fun.cb = a_pFun;
434  m_vRPN.push_back(tok);
435  }
436 
437  m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1;
438  m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
439 
440  }
Internal error of any kind.
Definition: muParserDef.h:279
Code for a generic function item.
Definition: muParserDef.h:170
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:294
value item
Definition: muParserDef.h:161
void(* erased_fun_type)()
Function type used to erase type. Voluntarily needs explicit cast with all other fun_type.
Definition: muParserDef.h:325
void mu::ParserByteCode::AddOp ( ECmdCode  a_Oprt)

Add an operator identifier to bytecode.

Operator entries in byte code consist of:

  • value array position of the result
  • the operator code according to ParserToken::ECmdCode
See also
ParserToken::ECmdCode

Definition at line 194 of file muParserBytecode.cpp.

195  {
196  bool bOptimized = false;
197 
198  if (m_bEnableOptimizer)
199  {
200  std::size_t sz = m_vRPN.size();
201 
202  // Check for foldable constants like:
203  // cmVAL cmVAL cmADD
204  // where cmADD can stand fopr any binary operator applied to
205  // two constant values.
206  if (sz >= 2 && m_vRPN[sz - 2].Cmd == cmVAL && m_vRPN[sz - 1].Cmd == cmVAL)
207  {
208  ConstantFolding(a_Oprt);
209  bOptimized = true;
210  }
211  else
212  {
213  switch (a_Oprt)
214  {
215  case cmPOW:
216  // Optimization for polynomials of low order
217  if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL)
218  {
219  if (m_vRPN[sz - 1].Val.data2 == 0)
220  {
221  m_vRPN[sz - 2].Cmd = cmVAL;
222  m_vRPN[sz - 2].Val.ptr = nullptr;
223  m_vRPN[sz - 2].Val.data = 0;
224  m_vRPN[sz - 2].Val.data2 = 1;
225  }
226  else if (m_vRPN[sz - 1].Val.data2 == 1)
227  m_vRPN[sz - 2].Cmd = cmVAR;
228  else if (m_vRPN[sz - 1].Val.data2 == 2)
229  m_vRPN[sz - 2].Cmd = cmVARPOW2;
230  else if (m_vRPN[sz - 1].Val.data2 == 3)
231  m_vRPN[sz - 2].Cmd = cmVARPOW3;
232  else if (m_vRPN[sz - 1].Val.data2 == 4)
233  m_vRPN[sz - 2].Cmd = cmVARPOW4;
234  else
235  break;
236 
237  m_vRPN.pop_back();
238  bOptimized = true;
239  }
240  break;
241 
242  case cmSUB:
243  case cmADD:
244  // Simple optimization based on pattern recognition for a shitload of different
245  // bytecode combinations of addition/subtraction
246  if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
247  (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR) ||
248  (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
249  (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL) ||
250  (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
251  (m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
252  (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr) ||
253  (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr))
254  {
255  MUP_ASSERT(
256  (m_vRPN[sz - 2].Val.ptr == nullptr && m_vRPN[sz - 1].Val.ptr != nullptr) ||
257  (m_vRPN[sz - 2].Val.ptr != nullptr && m_vRPN[sz - 1].Val.ptr == nullptr) ||
258  (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr));
259 
260  m_vRPN[sz - 2].Cmd = cmVARMUL;
261  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
262  m_vRPN[sz - 2].Val.data2 += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data2; // offset
263  m_vRPN[sz - 2].Val.data += ((a_Oprt == cmSUB) ? -1 : 1) * m_vRPN[sz - 1].Val.data; // multiplicand
264  m_vRPN.pop_back();
265  bOptimized = true;
266  }
267  break;
268 
269  case cmMUL:
270  if ((m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAL) ||
271  (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVAR))
272  {
273  m_vRPN[sz - 2].Cmd = cmVARMUL;
274  m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
275  m_vRPN[sz - 2].Val.data = m_vRPN[sz - 2].Val.data2 + m_vRPN[sz - 1].Val.data2;
276  m_vRPN[sz - 2].Val.data2 = 0;
277  m_vRPN.pop_back();
278  bOptimized = true;
279  }
280  else if (
281  (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL) ||
282  (m_vRPN[sz - 1].Cmd == cmVARMUL && m_vRPN[sz - 2].Cmd == cmVAL))
283  {
284  // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2
285  m_vRPN[sz - 2].Cmd = cmVARMUL;
286  m_vRPN[sz - 2].Val.ptr = (value_type*)((long long)(m_vRPN[sz - 2].Val.ptr) | (long long)(m_vRPN[sz - 1].Val.ptr));
287  if (m_vRPN[sz - 1].Cmd == cmVAL)
288  {
289  m_vRPN[sz - 2].Val.data *= m_vRPN[sz - 1].Val.data2;
290  m_vRPN[sz - 2].Val.data2 *= m_vRPN[sz - 1].Val.data2;
291  }
292  else
293  {
294  m_vRPN[sz - 2].Val.data = m_vRPN[sz - 1].Val.data * m_vRPN[sz - 2].Val.data2;
295  m_vRPN[sz - 2].Val.data2 = m_vRPN[sz - 1].Val.data2 * m_vRPN[sz - 2].Val.data2;
296  }
297  m_vRPN.pop_back();
298  bOptimized = true;
299  }
300  else if (
301  m_vRPN[sz - 1].Cmd == cmVAR && m_vRPN[sz - 2].Cmd == cmVAR &&
302  m_vRPN[sz - 1].Val.ptr == m_vRPN[sz - 2].Val.ptr)
303  {
304  // Optimization: a*a -> a^2
305  m_vRPN[sz - 2].Cmd = cmVARPOW2;
306  m_vRPN.pop_back();
307  bOptimized = true;
308  }
309  break;
310 
311  case cmDIV:
312  if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0)
313  {
314  // Optimization: 4*a/2 -> 2*a
315  m_vRPN[sz - 2].Val.data /= m_vRPN[sz - 1].Val.data2;
316  m_vRPN[sz - 2].Val.data2 /= m_vRPN[sz - 1].Val.data2;
317  m_vRPN.pop_back();
318  bOptimized = true;
319  }
320  break;
321 
322  // no optimization for other opcodes
323  default:
324  break;
325  } // switch a_Oprt
326  }
327  }
328 
329  // If optimization can't be applied just write the value
330  if (!bOptimized)
331  {
332  --m_iStackPos;
333  SToken tok;
334  tok.Cmd = a_Oprt;
335  m_vRPN.push_back(tok);
336  }
337  }
Operator item: y to the power of ...
Definition: muParserDef.h:150
Operator item: subtract.
Definition: muParserDef.h:147
Operator item: multiply.
Definition: muParserDef.h:148
#define MUP_ASSERT(COND)
An assertion that does not kill the program.
Definition: muParserDef.h:77
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:294
Operator item: division.
Definition: muParserDef.h:149
Operator item: add.
Definition: muParserDef.h:146
value item
Definition: muParserDef.h:161
variable item
Definition: muParserDef.h:160
void mu::ParserByteCode::AddStrFun ( generic_callable_type  a_pFun,
int  a_iArgc,
int  a_iIdx 
)

Add Strung function entry to the parser bytecode.

Exceptions
nothrowA string function entry consists of the stack position of the return value, followed by a cmSTRFUNC code, the function pointer and an index into the string buffer maintained by the parser.

Definition at line 468 of file muParserBytecode.cpp.

469  {
470  m_iStackPos = m_iStackPos - a_iArgc + 1;
471 
472  SToken tok;
473  tok.Cmd = cmFUNC_STR;
474  tok.Fun.argc = a_iArgc;
475  tok.Fun.idx = a_iIdx;
476  tok.Fun.cb = a_pFun;
477  m_vRPN.push_back(tok);
478 
479  m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
480  }
Code for a function with a string parameter.
Definition: muParserDef.h:171
void mu::ParserByteCode::AddVal ( value_type  a_fVal)

Add a Variable pointer to bytecode.

Value entries in byte code consist of:

  • value array position of the value
  • the operator code according to ParserToken::cmVAL
  • the value stored in #mc_iSizeVal number of bytecode entries.
Parameters
a_pValValue to be added.
Exceptions
nothrow

Definition at line 135 of file muParserBytecode.cpp.

136  {
137  ++m_iStackPos;
138  m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
139 
140  // If optimization does not apply
141  SToken tok;
142  tok.Cmd = cmVAL;
143  tok.Val.ptr = nullptr;
144  tok.Val.data = 0;
145  tok.Val.data2 = a_fVal;
146  m_vRPN.push_back(tok);
147  }
value item
Definition: muParserDef.h:161
void mu::ParserByteCode::AddVar ( value_type a_pVar)

Add a Variable pointer to bytecode.

Parameters
a_pVarPointer to be added.
Exceptions
nothrow

Definition at line 108 of file muParserBytecode.cpp.

109  {
110  ++m_iStackPos;
111  m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
112 
113  // optimization does not apply
114  SToken tok;
115  tok.Cmd = cmVAR;
116  tok.Val.ptr = a_pVar;
117  tok.Val.data = 1;
118  tok.Val.data2 = 0;
119  m_vRPN.push_back(tok);
120  }
variable item
Definition: muParserDef.h:160
void mu::ParserByteCode::Assign ( const ParserByteCode a_ByteCode)

Copy state of another object to this.

Exceptions
nowthrow

Definition at line 92 of file muParserBytecode.cpp.

Referenced by operator=(), and ParserByteCode().

93  {
94  if (this == &a_ByteCode)
95  return;
96 
97  m_iStackPos = a_ByteCode.m_iStackPos;
98  m_vRPN = a_ByteCode.m_vRPN;
99  m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;
100  m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;
101  }
void mu::ParserByteCode::clear ( )

Delete the bytecode.

Exceptions
nothrowThe name of this function is a violation of my own coding guidelines but this way it's more in line with the STL functions thus more intuitive.

Definition at line 539 of file muParserBytecode.cpp.

540  {
541  m_vRPN.clear();
542  m_iStackPos = 0;
543  m_iMaxStackSize = 0;
544  }
void mu::ParserByteCode::Finalize ( )

Add end marker to bytecode.

Exceptions
nothrow

Definition at line 487 of file muParserBytecode.cpp.

488  {
489  SToken tok;
490  tok.Cmd = cmEND;
491  m_vRPN.push_back(tok);
492  rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit
493 
494  // Determine the if-then-else jump offsets
495  std::stack<int> stIf, stElse;
496  int idx;
497  for (int i = 0; i < (int)m_vRPN.size(); ++i)
498  {
499  switch (m_vRPN[i].Cmd)
500  {
501  case cmIF:
502  stIf.push(i);
503  break;
504 
505  case cmELSE:
506  stElse.push(i);
507  idx = stIf.top();
508  stIf.pop();
509  m_vRPN[idx].Oprt.offset = i - idx;
510  break;
511 
512  case cmENDIF:
513  idx = stElse.top();
514  stElse.pop();
515  m_vRPN[idx].Oprt.offset = i - idx;
516  break;
517 
518  default:
519  break;
520  }
521  }
522  }
For use in the ternary if-then-else operator.
Definition: muParserDef.h:156
end of formula
Definition: muParserDef.h:177
For use in the ternary if-then-else operator.
Definition: muParserDef.h:157
For use in the ternary if-then-else operator.
Definition: muParserDef.h:158
ParserByteCode & mu::ParserByteCode::operator= ( const ParserByteCode a_ByteCode)

Assignment operator.

Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)

Definition at line 75 of file muParserBytecode.cpp.

76  {
77  Assign(a_ByteCode);
78  return *this;
79  }
void Assign(const ParserByteCode &a_ByteCode)
Copy state of another object to this.

The documentation for this class was generated from the following files: