3 _____ __ _____________ _______ ______ ___________
4 / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
5 | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/
6 |__|_| /____/| __(____ /__| /____ >\___ >__|
8 Copyright (C) 2004 - 2020 Ingo Berg
10 Redistribution and use in source and binary forms, with or without modification, are permitted
11 provided that the following conditions are met:
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.
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.
29 #include "muParserBytecode.h"
37 #include "muParserDef.h"
38 #include "muParserError.h"
39 #include "muParserToken.h"
40 #include "muParserTemplateMagic.h"
45 /** \brief Bytecode default constructor. */
46 ParserByteCode::ParserByteCode()
50 , m_bEnableOptimizer(true)
56 /** \brief Copy constructor.
58 Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
60 ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode)
66 /** \brief Assignment operator.
68 Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)
70 ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode)
77 void ParserByteCode::EnableOptimizer(bool bStat)
79 m_bEnableOptimizer = bStat;
83 /** \brief Copy state of another object to this.
87 void ParserByteCode::Assign(const ParserByteCode& a_ByteCode)
89 if (this == &a_ByteCode)
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;
99 /** \brief Add a Variable pointer to bytecode.
100 \param a_pVar Pointer to be added.
103 void ParserByteCode::AddVar(value_type* a_pVar)
106 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
108 // optimization does not apply
111 tok.Val.ptr = a_pVar;
114 m_vRPN.push_back(tok);
118 /** \brief Add a Variable pointer to bytecode.
120 Value entries in byte code consist of:
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>
127 \param a_pVal Value to be added.
130 void ParserByteCode::AddVal(value_type a_fVal)
133 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
135 // If optimization does not apply
138 tok.Val.ptr = nullptr;
140 tok.Val.data2 = a_fVal;
141 m_vRPN.push_back(tok);
145 void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)
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;
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;
169 case cmPOW: x = MathImpl<value_type>::Pow(x, y);
179 /** \brief Add an operator identifier to bytecode.
181 Operator entries in byte code consist of:
183 <li>value array position of the result</li>
184 <li>the operator code according to ParserToken::ECmdCode</li>
187 \sa ParserToken::ECmdCode
189 void ParserByteCode::AddOp(ECmdCode a_Oprt)
191 bool bOptimized = false;
193 if (m_bEnableOptimizer)
195 std::size_t sz = m_vRPN.size();
197 // Check for foldable constants like:
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)
203 ConstantFolding(a_Oprt);
211 // Optimization for polynomials of low order
212 if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL)
214 if (m_vRPN[sz - 1].Val.data2 == 0)
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;
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;
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))
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));
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
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))
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;
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))
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)
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;
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;
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)
299 // Optimization: a*a -> a^2
300 m_vRPN[sz - 2].Cmd = cmVARPOW2;
307 if (m_vRPN[sz - 1].Cmd == cmVAL && m_vRPN[sz - 2].Cmd == cmVARMUL && m_vRPN[sz - 1].Val.data2 != 0)
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;
317 // no optimization for other opcodes
324 // If optimization can't be applied just write the value
330 m_vRPN.push_back(tok);
335 void ParserByteCode::AddIfElse(ECmdCode a_Oprt)
339 m_vRPN.push_back(tok);
343 /** \brief Add an assignment operator
345 Operator entries in byte code consist of:
347 <li>cmASSIGN code</li>
348 <li>the pointer of the destination variable</li>
351 \sa ParserToken::ECmdCode
353 void ParserByteCode::AddAssignOp(value_type* a_pVar)
359 tok.Oprt.ptr = a_pVar;
360 m_vRPN.push_back(tok);
364 /** \brief Add function to bytecode.
366 \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
367 \param a_pFun Pointer to function callback.
369 void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc)
371 std::size_t sz = m_vRPN.size();
372 bool optimize = false;
374 // only optimize functions with fixed number of more than a single arguments
375 if (m_bEnableOptimizer && a_iArgc > 0)
377 // <ibg 2020-06-10/> Unary Plus is a no-op
378 if ((void*)a_pFun == (void*)&MathImpl<value_type>::UnaryPlus)
383 for (int i = 0; i < std::abs(a_iArgc); ++i)
385 if (m_vRPN[sz - i - 1].Cmd != cmVAL)
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;
409 // For now functions with unlimited number of arguments are not optimized
410 throw ParserError(ecINTERNAL_ERROR);
413 // remove the folded values
414 m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end());
420 tok.Val.ptr = nullptr;
421 m_vRPN.push_back(tok);
427 tok.Fun.argc = a_iArgc;
428 tok.Fun.ptr = a_pFun;
429 m_vRPN.push_back(tok);
432 m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1;
433 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
438 /** \brief Add a bulk function to bytecode.
440 \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.
441 \param a_pFun Pointer to function callback.
443 void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc)
445 m_iStackPos = m_iStackPos - a_iArgc + 1;
446 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
449 tok.Cmd = cmFUNC_BULK;
450 tok.Fun.argc = a_iArgc;
451 tok.Fun.ptr = a_pFun;
452 m_vRPN.push_back(tok);
456 /** \brief Add Strung function entry to the parser bytecode.
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.
463 void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx)
465 m_iStackPos = m_iStackPos - a_iArgc + 1;
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);
474 m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);
478 /** \brief Add end marker to bytecode.
482 void ParserByteCode::Finalize()
486 m_vRPN.push_back(tok);
487 rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit
489 // Determine the if-then-else jump offsets
490 std::stack<int> stIf, stElse;
492 for (int i = 0; i < (int)m_vRPN.size(); ++i)
494 switch (m_vRPN[i].Cmd)
504 m_vRPN[idx].Oprt.offset = i - idx;
510 m_vRPN[idx].Oprt.offset = i - idx;
520 std::size_t ParserByteCode::GetMaxStackSize() const
522 return m_iMaxStackSize + 1;
526 /** \brief Delete the bytecode.
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
534 void ParserByteCode::clear()
542 /** \brief Dump bytecode (for debugging only!). */
543 void ParserByteCode::AsciiDump()
547 mu::console() << _T("No bytecode available\n");
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)
554 mu::console() << std::dec << i << _T(" : \t");
555 switch (m_vRPN[i].Cmd)
557 case cmVAL: mu::console() << _T("VAL \t");
558 mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n");
561 case cmVAR: mu::console() << _T("VAR \t");
562 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
565 case cmVARPOW2: mu::console() << _T("VARPOW2 \t");
566 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
569 case cmVARPOW3: mu::console() << _T("VARPOW3 \t");
570 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
573 case cmVARPOW4: mu::console() << _T("VARPOW4 \t");
574 mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n");
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");
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");
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");
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;
610 case cmIF: mu::console() << _T("IF\t");
611 mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
614 case cmELSE: mu::console() << _T("ELSE\t");
615 mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n");
618 case cmENDIF: mu::console() << _T("ENDIF\n"); break;
621 mu::console() << _T("ASSIGN\t");
622 mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n");
625 default: mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n");
630 mu::console() << _T("END") << std::endl;