@@ -18,6 +18,18 @@ public class SimpleRewrites implements OptimizerPass {
1818 private int totalRewrites = 0 ;
1919 private final boolean showRewrites = false ;
2020
21+ private static boolean isNumberLiteral (ImExpr e ) {
22+ return e instanceof ImIntVal || e instanceof ImRealVal ;
23+ }
24+
25+ private static float asFloat (ImExpr e ) {
26+ if (e instanceof ImRealVal ) {
27+ return Float .parseFloat (((ImRealVal ) e ).getValR ());
28+ } else {
29+ return ((ImIntVal ) e ).getValI ();
30+ }
31+ }
32+
2133 @ Override
2234 public int optimize (ImTranslator trans ) {
2335 ImProg prog = trans .getImProg ();
@@ -178,10 +190,31 @@ private void optimizeOpCall(ImOperatorCall opc) {
178190 } else if (right instanceof ImBoolVal ) {
179191 boolean b2 = ((ImBoolVal ) right ).getValB ();
180192 wasViable = replaceBoolTerm (opc , left , b2 );
181- } else if (left instanceof ImIntVal && right instanceof ImIntVal ) {
182- wasViable = optimizeIntInt (opc , wasViable , (ImIntVal ) left , (ImIntVal ) right );
183- } else if (left instanceof ImRealVal && right instanceof ImRealVal ) {
184- wasViable = optimizeRealReal (opc , wasViable , (ImRealVal ) left , (ImRealVal ) right );
193+ } else if (isNumberLiteral (left ) && isNumberLiteral (right )) {
194+ // If any side is real (or the op is a real op), fold as real; otherwise fold as int.
195+ boolean foldAsReal =
196+ (left instanceof ImRealVal ) ||
197+ (right instanceof ImRealVal ) ||
198+ opc .getOp () == WurstOperator .DIV_REAL ||
199+ opc .getOp () == WurstOperator .MOD_REAL ;
200+
201+ if (foldAsReal ) {
202+ wasViable = optimizeRealRealMixed (opc , wasViable , left , right );
203+ } else if (left instanceof ImIntVal && right instanceof ImIntVal ) {
204+ wasViable = optimizeIntInt (opc , wasViable , (ImIntVal ) left , (ImIntVal ) right );
205+ } else {
206+ wasViable = false ; // unknown numeric combo
207+ }
208+ } else if (left instanceof ImStringVal ) {
209+ // Fold "" + expr => expr
210+ if (opc .getOp () == WurstOperator .PLUS
211+ && ((ImStringVal ) left ).getValS ().isEmpty ()) {
212+ right .setParent (null );
213+ opc .replaceBy (right );
214+ wasViable = true ;
215+ } else {
216+ wasViable = false ;
217+ }
185218 } else if (right instanceof ImStringVal ) {
186219 if (left instanceof ImStringVal ) {
187220 wasViable = optimizeStringString (opc , (ImStringVal ) left , (ImStringVal ) right );
@@ -201,13 +234,11 @@ private void optimizeOpCall(ImOperatorCall opc) {
201234 else {
202235 ImExpr expr = opc .getArguments ().get (0 );
203236 if (opc .getOp () == WurstOperator .UNARY_MINUS && expr instanceof ImIntVal ) {
204- ImIntVal imIntVal = (ImIntVal ) expr ;
205- if (imIntVal .getValI () <= 0 ) {
206- int inverseVal = imIntVal .getValI () * -1 ;
207- ImIntVal newVal = JassIm .ImIntVal (inverseVal );
208- opc .replaceBy (newVal );
237+ int v = ((ImIntVal ) expr ).getValI ();
238+ if (v != Integer .MIN_VALUE && v <= 0 ) {
239+ opc .replaceBy (JassIm .ImIntVal (-v ));
240+ wasViable = false ;
209241 }
210- wasViable = false ;
211242 } else if (expr instanceof ImBoolVal ) {
212243 boolean b1 = ((ImBoolVal ) expr ).getValB ();
213244 boolean result ;
@@ -241,7 +272,7 @@ private void optimizeOpCall(ImOperatorCall opc) {
241272 List <ImExpr > args = inner .getArguments ().removeAll ();
242273 ImExprs imExprs = JassIm .ImExprs ();
243274 args .forEach ((e ) ->
244- imExprs .add (JassIm .ImOperatorCall (WurstOperator .NOT , JassIm .ImExprs (e .copy ()))));
275+ imExprs .add (JassIm .ImOperatorCall (WurstOperator .NOT , JassIm .ImExprs (e .copy ()))));
245276
246277 ImOperatorCall opCall = JassIm .ImOperatorCall (oppositeOperator (inner .getOp ()), imExprs );
247278 opc .replaceBy (opCall );
@@ -263,6 +294,89 @@ private void optimizeOpCall(ImOperatorCall opc) {
263294
264295 }
265296
297+ private boolean optimizeRealRealMixed (ImOperatorCall opc , boolean wasViable , ImExpr left , ImExpr right ) {
298+ float f1 = asFloat (left );
299+ float f2 = asFloat (right );
300+ boolean isConditional = false ;
301+ boolean isArithmetic = false ;
302+ boolean result = false ;
303+ float resultVal = 0f ;
304+
305+ switch (opc .getOp ()) {
306+ case GREATER :
307+ result = f1 > f2 ;
308+ isConditional = true ;
309+ break ;
310+ case GREATER_EQ :
311+ result = f1 >= f2 ;
312+ isConditional = true ;
313+ break ;
314+ case LESS :
315+ result = f1 < f2 ;
316+ isConditional = true ;
317+ break ;
318+ case LESS_EQ :
319+ result = f1 <= f2 ;
320+ isConditional = true ;
321+ break ;
322+ case EQ :
323+ result = f1 == f2 ;
324+ isConditional = true ;
325+ break ;
326+ case NOTEQ :
327+ result = f1 != f2 ;
328+ isConditional = true ;
329+ break ;
330+
331+ case PLUS :
332+ resultVal = f1 + f2 ;
333+ isArithmetic = true ;
334+ break ;
335+ case MINUS :
336+ resultVal = f1 - f2 ;
337+ isArithmetic = true ;
338+ break ;
339+ case MULT :
340+ resultVal = f1 * f2 ;
341+ isArithmetic = true ;
342+ break ;
343+ case MOD_REAL :
344+ if (f2 != 0f ) {
345+ resultVal = f1 % f2 ;
346+ isArithmetic = true ;
347+ }
348+ break ;
349+ case DIV_INT :
350+ case DIV_REAL :
351+ if (f2 != 0f ) {
352+ resultVal = f1 / f2 ;
353+ isArithmetic = true ;
354+ }
355+ break ;
356+
357+ default :
358+ return false ;
359+ }
360+
361+ if (isConditional ) {
362+ opc .replaceBy (JassIm .ImBoolVal (result ));
363+ return true ;
364+ } else if (isArithmetic ) {
365+ String s = floatToStringWithDecimalDigits (resultVal , 4 );
366+ if (Float .parseFloat (s ) != resultVal ) {
367+ s = floatToStringWithDecimalDigits (resultVal , 9 );
368+ if (Float .parseFloat (s ) != resultVal ) {
369+ return false ;
370+ }
371+ }
372+ opc .replaceBy (JassIm .ImRealVal (s ));
373+ return true ;
374+ } else {
375+ return false ;
376+ }
377+ }
378+
379+
266380 private boolean optimizeStringString (ImOperatorCall opc , ImStringVal left , ImStringVal right ) {
267381 String f1 = left .getValS ();
268382 String f2 = right .getValS ();
@@ -417,28 +531,52 @@ private boolean optimizeIntInt(ImOperatorCall opc, boolean wasViable, ImIntVal l
417531 isArithmetic = true ;
418532 }
419533 break ;
420- case MOD_REAL :
534+ case MOD_REAL : {
421535 float f1 = i1 ;
422536 float f2 = i2 ;
423- if (f2 != 0 ) {
537+ if (f2 != 0f ) {
424538 float resultF = f1 % f2 ;
425- opc .replaceBy (JassIm .ImRealVal (String .valueOf (resultF )));
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
426551 }
427552 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+ }
428574 case DIV_INT :
429575 if (i2 != 0 ) {
430576 resultVal = i1 / i2 ;
431577 isArithmetic = true ;
432578 }
433579 break ;
434- case DIV_REAL :
435- float f3 = i1 ;
436- float f4 = i2 ;
437- if (f4 != 0 ) {
438- float resultF = f3 / f4 ;
439- opc .replaceBy (JassIm .ImRealVal (String .valueOf (resultF )));
440- }
441- break ;
442580 default :
443581 result = false ;
444582 isConditional = false ;
0 commit comments