/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 |