• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 269

29 Sep 2025 11:58AM UTC coverage: 64.66% (+2.4%) from 62.222%
269

Pull #1096

circleci

Frotty
add RealRealMixed rewrites and tests
Pull Request #1096: Perf improvements

18205 of 28155 relevant lines covered (64.66%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.0
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SimpleRewrites.java
1
package de.peeeq.wurstscript.intermediatelang.optimizer;
2

3
import de.peeeq.wurstscript.WLogger;
4
import de.peeeq.wurstscript.WurstOperator;
5
import de.peeeq.wurstscript.jassIm.*;
6
import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
7
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
8
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
9

10
import java.text.DecimalFormat;
11
import java.text.DecimalFormatSymbols;
12
import java.util.Iterator;
13
import java.util.List;
14
import java.util.Locale;
15

16
public class SimpleRewrites implements OptimizerPass {
1✔
17
    private SideEffectAnalyzer sideEffectAnalysis;
18
    private int totalRewrites = 0;
1✔
19
    private final boolean showRewrites = false;
1✔
20

21
    private static boolean isNumberLiteral(ImExpr e) {
22
        return e instanceof ImIntVal || e instanceof ImRealVal;
1✔
23
    }
24

25
    private static float asFloat(ImExpr e) {
26
        if (e instanceof ImRealVal) {
1✔
27
            return Float.parseFloat(((ImRealVal) e).getValR());
1✔
28
        } else {
29
            return ((ImIntVal) e).getValI();
1✔
30
        }
31
    }
32

33
    @Override
34
    public int optimize(ImTranslator trans) {
35
        ImProg prog = trans.getImProg();
1✔
36
        this.sideEffectAnalysis = new SideEffectAnalyzer(prog);
1✔
37
        totalRewrites = 0;
1✔
38
        optimizeElement(prog);
1✔
39
        // we need to flatten the program, because we introduced new
40
        // StatementExprs
41
        prog.flatten(trans);
1✔
42
        removeUnreachableCode(prog);
1✔
43
        return totalRewrites;
1✔
44
    }
45

46
    @Override
47
    public String getName() {
48
        return "Simple Rewrites";
1✔
49
    }
50

51
    private void removeUnreachableCode(ImProg prog) {
52
        prog.accept(new ImProg.DefaultVisitor() {
1✔
53
            @Override
54
            public void visit(ImStmts stmts) {
55
                super.visit(stmts);
1✔
56
                removeUnreachableCode(stmts);
1✔
57
            }
1✔
58
        });
59
    }
1✔
60

61
    private void removeUnreachableCode(ImStmts stmts) {
62
        Iterator<ImStmt> it = stmts.iterator();
1✔
63
        boolean reachable = true;
1✔
64
        while (it.hasNext()) {
1✔
65
            ImStmt s = it.next();
1✔
66
            if (reachable) {
1✔
67
                if (s instanceof ImReturn) {
1✔
68
                    reachable = false;
1✔
69
                } else if (s instanceof ImExitwhen) {
1✔
70
                    ImExitwhen imExitwhen = (ImExitwhen) s;
1✔
71
                    ImExpr expr = imExitwhen.getCondition();
1✔
72
                    if (expr instanceof ImBoolVal) {
1✔
73
                        boolean b = ((ImBoolVal) expr).getValB();
1✔
74
                        if (b) {
1✔
75
                            // found "exitwhen true"
76
                            reachable = false;
1✔
77
                        }
78
                    }
79
                }
1✔
80
            } else {
81
                totalRewrites++;
1✔
82
                it.remove();
1✔
83
            }
84
        }
1✔
85
    }
1✔
86

87
    /**
88
     * Recursively optimizes the element
89
     */
90
    private void optimizeElement(Element elem) {
91
        // optimize children:
92
        for (int i = 0; i < elem.size(); i++) {
1✔
93
            optimizeElement(elem.get(i));
1✔
94
            if (i > 0) {
1✔
95
                Element lookback = elem.get(i - 1);
1✔
96
                if (elem.get(i) instanceof ImExitwhen && lookback instanceof ImExitwhen) {
1✔
97
                    optimizeConsecutiveExitWhen((ImExitwhen) lookback, (ImExitwhen) elem.get(i));
1✔
98
                }
99

100
                if (elem.get(i) instanceof ImSet && lookback instanceof ImSet) {
1✔
101
                    optimizeConsecutiveSet((ImSet) lookback, (ImSet) elem.get(i));
1✔
102
                }
103
            }
104
        }
105
        if (elem instanceof ImOperatorCall) {
1✔
106
            ImOperatorCall opc = (ImOperatorCall) elem;
1✔
107
            optimizeOpCall(opc);
1✔
108
        } else if (elem instanceof ImIf) {
1✔
109
            ImIf imIf = (ImIf) elem;
1✔
110
            optimizeIf(imIf);
1✔
111
        } else if (elem instanceof ImExitwhen) {
1✔
112
            ImExitwhen imExitwhen = (ImExitwhen) elem;
1✔
113
            optimizeExitwhen(imExitwhen);
1✔
114
        }
115

116
    }
1✔
117

118
    private void optimizeConsecutiveExitWhen(ImExitwhen lookback, ImExitwhen element) {
119
        element.getCondition().setParent(null);
1✔
120
        lookback.setCondition(JassIm.ImOperatorCall(WurstOperator.OR, JassIm.ImExprs(lookback.getCondition().copy(), element.getCondition())));
1✔
121
        element.replaceBy(ImHelper.nullExpr());
1✔
122
        totalRewrites++;
1✔
123
    }
1✔
124

125
    private void optimizeExitwhen(ImExitwhen imExitwhen) {
126
        ImExpr expr = imExitwhen.getCondition();
1✔
127
        if (expr instanceof ImBoolVal) {
1✔
128
            boolean b = ((ImBoolVal) expr).getValB();
1✔
129
            if (!b) {
1✔
130
                imExitwhen.replaceBy(ImHelper.nullExpr());
1✔
131
                totalRewrites++;
1✔
132
            }
133
        }
134

135
    }
1✔
136

137
    /**
138
     * Rewrites if statements that only contain an exitwhen statement
139
     * so that the if's condition is combined with the exitwhen
140
     * <p>
141
     * if expr1
142
     * exitwhen expr2
143
     * <p>
144
     * to:
145
     * <p>
146
     * exitwhen expr1 and expr2
147
     */
148
    private void optimizeIfExitwhen(ImIf imIf) {
149
        ImExitwhen imStmt = (ImExitwhen) imIf.getThenBlock().get(0);
1✔
150
        imStmt.getCondition().setParent(null);
1✔
151
        imIf.getCondition().setParent(null);
1✔
152

153
        imStmt.setCondition((JassIm.ImOperatorCall(WurstOperator.AND, JassIm.ImExprs(imIf.getCondition(), imStmt.getCondition()))));
1✔
154
        imStmt.setParent(null);
1✔
155
        imIf.replaceBy(imStmt);
1✔
156
        totalRewrites++;
1✔
157
    }
1✔
158

159
    private void optimizeOpCall(ImOperatorCall opc) {
160
        // Binary
161
        boolean wasViable = true;
1✔
162
        if (opc.getArguments().size() > 1) {
1✔
163
            ImExpr left = opc.getArguments().get(0);
1✔
164
            ImExpr right = opc.getArguments().get(1);
1✔
165
            if (left instanceof ImBoolVal && right instanceof ImBoolVal) {
1✔
166
                boolean b1 = ((ImBoolVal) left).getValB();
1✔
167
                boolean b2 = ((ImBoolVal) right).getValB();
1✔
168
                boolean result;
169
                switch (opc.getOp()) {
1✔
170
                    case OR:
171
                        result = b1 || b2;
1✔
172
                        break;
1✔
173
                    case AND:
174
                        result = b1 && b2;
1✔
175
                        break;
1✔
176
                    case EQ:
177
                        result = b1 == b2;
×
178
                        break;
×
179
                    case NOTEQ:
180
                        result = b1 != b2;
×
181
                        break;
×
182
                    default:
183
                        result = false;
×
184
                        break;
185
                }
186
                opc.replaceBy(JassIm.ImBoolVal(result));
1✔
187
            } else if (left instanceof ImBoolVal) {
1✔
188
                boolean b1 = ((ImBoolVal) left).getValB();
1✔
189
                wasViable = replaceBoolTerm(opc, right, b1);
1✔
190
            } else if (right instanceof ImBoolVal) {
1✔
191
                boolean b2 = ((ImBoolVal) right).getValB();
1✔
192
                wasViable = replaceBoolTerm(opc, left, b2);
1✔
193
            } else if (isNumberLiteral(left) && isNumberLiteral(right)) {
1✔
194
                // If any side is real (or the op is a real op), fold as real; otherwise fold as int.
195
                boolean foldAsReal =
1✔
196
                    (left instanceof ImRealVal) ||
197
                        (right instanceof ImRealVal) ||
198
                        opc.getOp() == WurstOperator.DIV_REAL ||
1✔
199
                        opc.getOp() == WurstOperator.MOD_REAL;
1✔
200

201
                if (foldAsReal) {
1✔
202
                    wasViable = optimizeRealRealMixed(opc, wasViable, left, right);
1✔
203
                } else if (left instanceof ImIntVal && right instanceof ImIntVal) {
1✔
204
                    wasViable = optimizeIntInt(opc, wasViable, (ImIntVal) left, (ImIntVal) right);
1✔
205
                } else {
206
                    wasViable = false; // unknown numeric combo
×
207
                }
208
            } else if (left instanceof ImStringVal) {
1✔
209
                // Fold "" + expr  =>  expr
210
                if (opc.getOp() == WurstOperator.PLUS
1✔
211
                    && ((ImStringVal) left).getValS().isEmpty()) {
1✔
212
                    right.setParent(null);
1✔
213
                    opc.replaceBy(right);
1✔
214
                    wasViable = true;
1✔
215
                } else {
216
                    wasViable = false;
1✔
217
                }
218
            } else if (right instanceof ImStringVal) {
1✔
219
                if (left instanceof ImStringVal) {
1✔
220
                    wasViable = optimizeStringString(opc, (ImStringVal) left, (ImStringVal) right);
×
221
                } else if (((ImStringVal) right).getValS().equalsIgnoreCase("") && opc.getOp() == WurstOperator.PLUS) {
1✔
222
                    left.setParent(null);
1✔
223
                    opc.replaceBy(left);
1✔
224
                    wasViable = true;
1✔
225
                } else {
226
                    wasViable = false;
1✔
227
                }
228
            } else {
229
                wasViable = false;
1✔
230
            }
231
        }
1✔
232

233
        // Unary
234
        else {
235
            ImExpr expr = opc.getArguments().get(0);
1✔
236
            if (opc.getOp() == WurstOperator.UNARY_MINUS && expr instanceof ImIntVal) {
1✔
237
                int v = ((ImIntVal) expr).getValI();
1✔
238
                if (v != Integer.MIN_VALUE && v <= 0) {
1✔
239
                    opc.replaceBy(JassIm.ImIntVal(-v));
×
240
                    wasViable = false;
×
241
                }
242
            } else if (expr instanceof ImBoolVal) {
1✔
243
                boolean b1 = ((ImBoolVal) expr).getValB();
1✔
244
                boolean result;
245
                switch (opc.getOp()) {
1✔
246
                    case NOT:
247
                        result = !b1;
1✔
248
                        break;
1✔
249
                    default:
250
                        result = false;
×
251
                        break;
252
                }
253
                opc.replaceBy(JassIm.ImBoolVal(result));
1✔
254
            } else if (opc.getOp() == WurstOperator.NOT && expr instanceof ImOperatorCall) {
1✔
255
                // optimize negation of some operators
256
                ImOperatorCall inner = (ImOperatorCall) expr;
1✔
257
                switch (inner.getOp()) {
1✔
258
                    case NOT:
259
                        opc.replaceBy(inner.getArguments().remove(0));
1✔
260
                        break;
1✔
261
                    case EQ:
262
                    case NOTEQ:
263
                    case LESS:
264
                    case LESS_EQ:
265
                    case GREATER:
266
                    case GREATER_EQ:
267
                        opc.replaceBy(JassIm.ImOperatorCall(oppositeOperator(inner.getOp()), JassIm.ImExprs(inner.getArguments().removeAll())));
1✔
268
                        break;
1✔
269
                    case OR:
270
                    case AND:
271
                        // DeMorgan not(a and b) => not a or not b; not(a or b) => not a and not b
272
                        List<ImExpr> args = inner.getArguments().removeAll();
1✔
273
                        ImExprs imExprs = JassIm.ImExprs();
1✔
274
                        args.forEach((e) ->
1✔
275
                            imExprs.add(JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(e.copy()))));
1✔
276

277
                        ImOperatorCall opCall = JassIm.ImOperatorCall(oppositeOperator(inner.getOp()), imExprs);
1✔
278
                        opc.replaceBy(opCall);
1✔
279
                        break;
1✔
280
                    default:
281
                        wasViable = false;
×
282
                        break;
283
                }
284
            } else {
1✔
285
                wasViable = false;
1✔
286
            }
287
        }
288
        if (wasViable) {
1✔
289
            totalRewrites++;
1✔
290
            if (showRewrites) {
291
                WLogger.info("opcall rewrite: " + opc);
292
            }
293
        }
294

295
    }
1✔
296

297
    private boolean optimizeRealRealMixed(ImOperatorCall opc, boolean wasViable, ImExpr left, ImExpr right) {
298
        float f1 = asFloat(left);
1✔
299
        float f2 = asFloat(right);
1✔
300
        boolean isConditional = false;
1✔
301
        boolean isArithmetic = false;
1✔
302
        boolean result = false;
1✔
303
        float resultVal = 0f;
1✔
304

305
        switch (opc.getOp()) {
1✔
306
            case GREATER:
307
                result = f1 > f2;
1✔
308
                isConditional = true;
1✔
309
                break;
1✔
310
            case GREATER_EQ:
311
                result = f1 >= f2;
1✔
312
                isConditional = true;
1✔
313
                break;
1✔
314
            case LESS:
315
                result = f1 < f2;
1✔
316
                isConditional = true;
1✔
317
                break;
1✔
318
            case LESS_EQ:
319
                result = f1 <= f2;
1✔
320
                isConditional = true;
1✔
321
                break;
1✔
322
            case EQ:
323
                result = f1 == f2;
1✔
324
                isConditional = true;
1✔
325
                break;
1✔
326
            case NOTEQ:
327
                result = f1 != f2;
1✔
328
                isConditional = true;
1✔
329
                break;
1✔
330

331
            case PLUS:
332
                resultVal = f1 + f2;
1✔
333
                isArithmetic = true;
1✔
334
                break;
1✔
335
            case MINUS:
336
                resultVal = f1 - f2;
1✔
337
                isArithmetic = true;
1✔
338
                break;
1✔
339
            case MULT:
340
                resultVal = f1 * f2;
1✔
341
                isArithmetic = true;
1✔
342
                break;
1✔
343
            case MOD_REAL:
344
                if (f2 != 0f) {
1✔
345
                    resultVal = f1 % f2;
1✔
346
                    isArithmetic = true;
1✔
347
                }
348
                break;
349
            case DIV_INT:
350
            case DIV_REAL:
351
                if (f2 != 0f) {
1✔
352
                    resultVal = f1 / f2;
1✔
353
                    isArithmetic = true;
1✔
354
                }
355
                break;
356

357
            default:
358
                return false;
×
359
        }
360

361
        if (isConditional) {
1✔
362
            opc.replaceBy(JassIm.ImBoolVal(result));
1✔
363
            return true;
1✔
364
        } else if (isArithmetic) {
1✔
365
            String s = floatToStringWithDecimalDigits(resultVal, 4);
1✔
366
            if (Float.parseFloat(s) != resultVal) {
1✔
367
                s = floatToStringWithDecimalDigits(resultVal, 9);
1✔
368
                if (Float.parseFloat(s) != resultVal) {
1✔
369
                    return false;
×
370
                }
371
            }
372
            opc.replaceBy(JassIm.ImRealVal(s));
1✔
373
            return true;
1✔
374
        } else {
375
            return false;
1✔
376
        }
377
    }
378

379

380
    private boolean optimizeStringString(ImOperatorCall opc, ImStringVal left, ImStringVal right) {
381
        String f1 = left.getValS();
×
382
        String f2 = right.getValS();
×
383
        switch (opc.getOp()) {
×
384
            case PLUS:
385
                opc.replaceBy(JassIm.ImStringVal(f1 + f2));
×
386
                return true;
×
387
            default:
388
                break;
389
        }
390
        return false;
×
391
    }
392

393
    private boolean optimizeRealReal(ImOperatorCall opc, boolean wasViable, ImRealVal left, ImRealVal right) {
394
        float f1 = Float.parseFloat(left.getValR());
×
395
        float f2 = Float.parseFloat(right.getValR());
×
396
        boolean isConditional = false;
×
397
        boolean isArithmetic = false;
×
398
        boolean result = false;
×
399
        float resultVal = 0;
×
400
        switch (opc.getOp()) {
×
401
            case GREATER:
402
                result = f1 > f2;
×
403
                isConditional = true;
×
404
                break;
×
405
            case GREATER_EQ:
406
                result = f1 >= f2;
×
407
                isConditional = true;
×
408
                break;
×
409
            case LESS:
410
                result = f1 < f2;
×
411
                isConditional = true;
×
412
                break;
×
413
            case LESS_EQ:
414
                result = f1 <= f2;
×
415
                isConditional = true;
×
416
                break;
×
417
            case EQ:
418
                result = f1 == f2;
×
419
                isConditional = true;
×
420
                break;
×
421
            case NOTEQ:
422
                result = f1 != f2;
×
423
                isConditional = true;
×
424
                break;
×
425
            case PLUS:
426
                resultVal = f1 + f2;
×
427
                isArithmetic = true;
×
428
                break;
×
429
            case MINUS:
430
                resultVal = f1 - f2;
×
431
                isArithmetic = true;
×
432
                break;
×
433
            case MULT:
434
                resultVal = f1 * f2;
×
435
                isArithmetic = true;
×
436
                break;
×
437
            case MOD_REAL:
438
                if (f2 != 0) {
×
439
                    resultVal = f1 % f2;
×
440
                    isArithmetic = true;
×
441
                }
442
                break;
443
            case DIV_INT:
444
                if (f2 != 0) {
×
445
                    resultVal = f1 / f2;
×
446
                    isArithmetic = true;
×
447
                }
448
                break;
449
            case DIV_REAL:
450
                if (f2 != 0) {
×
451
                    resultVal = f1 / f2;
×
452
                    isArithmetic = true;
×
453
                }
454
                break;
455
            default:
456
                result = false;
×
457
                isConditional = false;
×
458
                isArithmetic = false;
×
459
                break;
460
        }
461
        if (isConditional) {
×
462
            opc.replaceBy(JassIm.ImBoolVal(result));
×
463
        } else if (isArithmetic) {
×
464
            // convert result to string, using 4 decimal digits
465
            String s = floatToStringWithDecimalDigits(resultVal, 4);
×
466
            // String s = new BigDecimal(resultVal).toPlainString();
467
            // check if the string representation is exact
468
            if (Float.parseFloat(s) == resultVal) {
×
469
                opc.replaceBy(JassIm.ImRealVal(s));
×
470
            } else {
471
                s = floatToStringWithDecimalDigits(resultVal, 9);
×
472
                if (Float.parseFloat(s) == resultVal) {
×
473
                    opc.replaceBy(JassIm.ImRealVal(s));
×
474
                } else {
475
                    wasViable = false;
×
476
                }
477
            }
478
        } else {
×
479
            wasViable = false;
×
480
        }
481
        return wasViable;
×
482
    }
483

484
    private boolean optimizeIntInt(ImOperatorCall opc, boolean wasViable, ImIntVal left, ImIntVal right) {
485
        int i1 = left.getValI();
1✔
486
        int i2 = right.getValI();
1✔
487
        boolean isConditional = false;
1✔
488
        boolean isArithmetic = false;
1✔
489
        boolean result = false;
1✔
490
        int resultVal = 0;
1✔
491
        switch (opc.getOp()) {
1✔
492
            case GREATER:
493
                result = i1 > i2;
1✔
494
                isConditional = true;
1✔
495
                break;
1✔
496
            case GREATER_EQ:
497
                result = i1 >= i2;
1✔
498
                isConditional = true;
1✔
499
                break;
1✔
500
            case LESS:
501
                result = i1 < i2;
1✔
502
                isConditional = true;
1✔
503
                break;
1✔
504
            case LESS_EQ:
505
                result = i1 <= i2;
1✔
506
                isConditional = true;
1✔
507
                break;
1✔
508
            case EQ:
509
                result = i1 == i2;
1✔
510
                isConditional = true;
1✔
511
                break;
1✔
512
            case NOTEQ:
513
                result = i1 != i2;
1✔
514
                isConditional = true;
1✔
515
                break;
1✔
516
            case PLUS:
517
                resultVal = i1 + i2;
1✔
518
                isArithmetic = true;
1✔
519
                break;
1✔
520
            case MINUS:
521
                resultVal = i1 - i2;
1✔
522
                isArithmetic = true;
1✔
523
                break;
1✔
524
            case MULT:
525
                resultVal = i1 * i2;
1✔
526
                isArithmetic = true;
1✔
527
                break;
1✔
528
            case MOD_INT:
529
                if (i2 != 0) {
1✔
530
                    resultVal = i1 % i2;
1✔
531
                    isArithmetic = true;
1✔
532
                }
533
                break;
534
            case MOD_REAL: {
535
                float f1 = i1;
×
536
                float f2 = i2;
×
537
                if (f2 != 0f) {
×
538
                    float resultF = f1 % f2;
×
539
                    String s = floatToStringWithDecimalDigits(resultF, 4);
×
540
                    if (Float.parseFloat(s) != resultF) {
×
541
                        s = floatToStringWithDecimalDigits(resultF, 9);
×
542
                        if (Float.parseFloat(s) != resultF) {
×
543
                            wasViable = false;
×
544
                            break;
×
545
                        }
546
                    }
547
                    opc.replaceBy(JassIm.ImRealVal(s));
×
548
                    // keep wasViable as-is (true) so the caller counts this rewrite
549
                } else {
×
550
                    wasViable = false; // don’t fold div-by-zero
×
551
                }
552
                break;
×
553
            }
554
            case DIV_REAL: {
555
                float f1 = i1;
×
556
                float f2 = i2;
×
557
                if (f2 != 0f) {
×
558
                    float resultF = f1 / f2;
×
559
                    String s = floatToStringWithDecimalDigits(resultF, 4);
×
560
                    if (Float.parseFloat(s) != resultF) {
×
561
                        s = floatToStringWithDecimalDigits(resultF, 9);
×
562
                        if (Float.parseFloat(s) != resultF) {
×
563
                            wasViable = false;
×
564
                            break;
×
565
                        }
566
                    }
567
                    opc.replaceBy(JassIm.ImRealVal(s));
×
568
                    // keep wasViable as-is (true) so the caller counts this rewrite
569
                } else {
×
570
                    wasViable = false; // don’t fold div-by-zero
×
571
                }
572
                break;
×
573
            }
574
            case DIV_INT:
575
                if (i2 != 0) {
1✔
576
                    resultVal = i1 / i2;
1✔
577
                    isArithmetic = true;
1✔
578
                }
579
                break;
580
            default:
581
                result = false;
×
582
                isConditional = false;
×
583
                isArithmetic = false;
×
584
                break;
585
        }
586
        if (isConditional) {
1✔
587
            opc.replaceBy(JassIm.ImBoolVal(result));
1✔
588
        } else if (isArithmetic) {
1✔
589
            opc.replaceBy(JassIm.ImIntVal(resultVal));
1✔
590
        } else {
591
            wasViable = false;
1✔
592
        }
593
        return wasViable;
1✔
594
    }
595

596
    private boolean replaceBoolTerm(ImOperatorCall opc, ImExpr expr, boolean b2) {
597
        switch (opc.getOp()) {
1✔
598
            case OR:
599
                if (b2) {
1✔
600
                    opc.replaceBy(JassIm.ImBoolVal(true));
1✔
601
                } else {
602
                    expr.setParent(null);
1✔
603
                    opc.replaceBy(expr);
1✔
604
                }
605
                break;
1✔
606
            case AND:
607
                if (b2) {
1✔
608
                    expr.setParent(null);
1✔
609
                    opc.replaceBy(expr);
1✔
610
                } else {
611
                    opc.replaceBy(JassIm.ImBoolVal(false));
1✔
612
                }
613
                break;
1✔
614
            default:
615
                return false;
1✔
616
        }
617
        return true;
1✔
618
    }
619

620
    /**
621
     * returns the opposite of an operator
622
     */
623
    private WurstOperator oppositeOperator(WurstOperator op) {
624
        switch (op) {
1✔
625
            case EQ:
626
                return WurstOperator.NOTEQ;
1✔
627
            case GREATER:
628
                return WurstOperator.LESS_EQ;
1✔
629
            case GREATER_EQ:
630
                return WurstOperator.LESS;
1✔
631
            case LESS:
632
                return WurstOperator.GREATER_EQ;
1✔
633
            case LESS_EQ:
634
                return WurstOperator.GREATER;
1✔
635
            case NOTEQ:
636
                return WurstOperator.EQ;
1✔
637
            case AND:
638
                return WurstOperator.OR;
1✔
639
            case OR:
640
                return WurstOperator.AND;
1✔
641
            default:
642
                throw new Error("operator " + op + " does not have an opposite.");
×
643
        }
644
    }
645

646
    private static String floatToStringWithDecimalDigits(float resultVal, int digits) {
647
        DecimalFormat format = new DecimalFormat();
1✔
648
        // use a fixed locale, so that it does not randomly replace a dot by
649
        // comma on German PCs
650
        // hope this works
651
        format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
1✔
652
        format.setMinimumIntegerDigits(1);
1✔
653
        format.setMaximumFractionDigits(digits);
1✔
654
        format.setMinimumFractionDigits(1);
1✔
655
        format.setGroupingUsed(false);
1✔
656
        String s = format.format(resultVal);
1✔
657
        return s;
1✔
658
    }
659

660
    private void optimizeIf(ImIf imIf) {
661
        if (imIf.getThenBlock().isEmpty() && imIf.getElseBlock().isEmpty()) {
1✔
662
            totalRewrites++;
1✔
663
            imIf.replaceBy(imIf.getCondition().copy());
1✔
664
        } else if (imIf.getCondition() instanceof ImBoolVal) {
1✔
665
            ImBoolVal boolVal = (ImBoolVal) imIf.getCondition();
1✔
666
            if (boolVal.getValB()) {
1✔
667
                // we have something like 'if true ...'
668
                // replace the if statement with the then-block
669
                // we have to use ImStatementExpr to get multiple statements
670
                // into one statement as needed
671
                // for the replaceBy function
672
                // we need to copy the thenBlock because otherwise it would have
673
                // two parents (we have not removed it from the old if-block)
674
                imIf.replaceBy(ImHelper.statementExprVoid(imIf.getThenBlock().copy()));
1✔
675
                totalRewrites++;
1✔
676
            } else {
677
                if (!imIf.getElseBlock().isEmpty()) {
1✔
678
                    imIf.replaceBy(ImHelper.statementExprVoid(imIf.getElseBlock().copy()));
1✔
679
                    totalRewrites++;
1✔
680
                } else {
681
                    imIf.replaceBy(ImHelper.nullExpr());
1✔
682
                    totalRewrites++;
1✔
683
                }
684
            }
685
        } else if (imIf.getElseBlock().isEmpty() && imIf.getThenBlock().size() == 1 && imIf.getThenBlock().get(0) instanceof ImExitwhen) {
1✔
686
            optimizeIfExitwhen(imIf);
1✔
687
        }
688
    }
1✔
689

690
    /**
691
     * Optimizes
692
     * <p>
693
     * set x = expr1
694
     * set x = x ⊕ expr2
695
     * <p>
696
     * into:
697
     * <p>
698
     * set x  = expr1 ⊕ expr2
699
     * <p>
700
     * like code that is created by the branch merger
701
     */
702
    private void optimizeConsecutiveSet(ImSet imSet1, ImSet imSet2) {
703
        ImVar leftVar1;
704
        if (imSet1.getLeft() instanceof ImVarAccess) {
1✔
705
            leftVar1 = ((ImVarAccess) imSet1.getLeft()).getVar();
1✔
706
        } else {
707
            return;
1✔
708
        }
709
        ImVar leftVar2;
710
        if (imSet2.getLeft() instanceof ImVarAccess) {
1✔
711
            leftVar2 = ((ImVarAccess) imSet2.getLeft()).getVar();
1✔
712
        } else {
713
            return;
1✔
714
        }
715

716
        ImExpr rightExpr1 = imSet1.getRight();
1✔
717
        ImExpr rightExpr2 = imSet2.getRight();
1✔
718

719
        if (leftVar1 == leftVar2) {
1✔
720
            if (rightExpr2 instanceof ImOperatorCall) {
1✔
721
                ImOperatorCall rightOpCall2 = (ImOperatorCall) rightExpr2;
1✔
722
                if (rightOpCall2.getArguments().size() == 2) {
1✔
723
                    if (rightOpCall2.getArguments().get(0) instanceof ImVarAccess) {
1✔
724
                        ImVarAccess imVarAccess2 = (ImVarAccess) rightOpCall2.getArguments().get(0);
1✔
725
                        if (imVarAccess2.getVar() == leftVar2) {
1✔
726
                            if (sideEffectAnalysis.cannotUseVar(rightOpCall2.getArguments().get(1), leftVar1)) {
1✔
727
                                rightExpr1.setParent(null);
1✔
728
                                imVarAccess2.replaceBy(rightExpr1);
1✔
729
                                imSet1.replaceBy(ImHelper.nullExpr());
1✔
730
                                totalRewrites++;
1✔
731
                            }
732
                        }
733
                    }
734
                }
735
            }
736
        }
737
    }
1✔
738

739
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc