Optimize val + 0 -> val, val ? true : false -> val

Bug fixes with printing
Cleaner print output
This commit is contained in:
NeeEoo
2024-04-24 00:15:46 +02:00
parent df1c5ad97c
commit 0ca6eab16e
7 changed files with 187 additions and 45 deletions
+74 -10
View File
@@ -13,6 +13,13 @@ class Optimizer {
var e = Tools.expr(s);
// TODO: Convert EBinOp(+, a, EUnop(-, b)) to EBinOp(-, a, b) aka (a - b)
// TODO: Make it so if EFor & EForKeyValue iterator variable isnt used its replaced with a $, which the code handles as doesnt exist
// TODO: Make it so <PURE_VAR|CONST> == ?; gets removed, IF it isnt stored
// TODO: Optimize ?. and ??
// TODO: Optimize Std.string(CONSTANT) to "CONSTANT"
// TODO: Remove Std.string if its the right side of a binop of a string
// TODO: Add EForComprehension, EWhileComprehension, EDoWhileComprehension for better optimization
// TODO: Optimize EFor(v, it, EBlock([e1])) to EFor(v, it, e1)
switch(e) {
// Parse all expressions and recreate the AST
@@ -26,15 +33,7 @@ class Optimizer {
for(e in exprs) {
switch(Tools.expr(e)) {
case EBlock(iex):
var declares = false;
for(ie in iex) {
declares = switch(Tools.expr(ie)) {
case EVar(_): true;// has a declaration
case EFunction(_): true;// has a declaration
default: declares;
}
if(declares) break;
}
var declares = Lambda.exists(iex, ie -> hasDecl(ie));
if(!declares) {
for(ex in iex)
newExprs.push(ex);
@@ -84,6 +83,27 @@ class Optimizer {
return mk(EParent(e2), s);
}
}
if(isConstant(e1) && isConstant(e2)) {
var c1 = getConstant(e1);
var c2 = getConstant(e2);
if(c1 == false && c2 == true) { // (VAR ? false : true)
return optimize(mk(EUnop("!", true, econd), s));
}
if(c1 == true && c2 == false) { // (VAR ? true : false)
return optimize(mk(Tools.expr(econd), s));
}
if(isConstant(econd)) {
if(c1 == true && c2 == true) { // (CONST ? true : true)
return mk(convertConstant(true), s);
}
if(c1 == false && c2 == false) { // (CONST ? false : false)
return mk(convertConstant(false), s);
}
// TODO: Check if local variables are used in the condition
// Since they cant have side effects, they can be optimized
}
}
return mk(ETernary(econd, e1, e2), s);
case EWhile(econd, e):
var econd = optimize(econd);
@@ -260,12 +280,30 @@ class Optimizer {
}
}
if(isNumber(e1) && !isConstant(e2)) {
var c1 = getNumber(e1);
if(c1 == 0 && op == "+")
return mk(Tools.expr(e2), s);
//if(c1 == 0 && op == "*")
// return mk(convertConstant(0), s);
}
if(!isConstant(e1) && isNumber(e2)) {
var c2 = getNumber(e2);
if(op == "+" && c2 == 0)
return mk(Tools.expr(e1), s);
if(op == "/" && c2 == 1)
return mk(Tools.expr(e1), s);
//if(op == "*" && c2 == 0)
// return mk(convertConstant(0), s);
}
return mk(EBinop(op, e1, e2), s);
case EUnop(op, prefix, e):
e = optimize(e);
if(isConstant(e)) {
if(isConstant(e) && prefix) {
var constant:Dynamic = getConstant(e);
switch(op) {
case "-": return mk(convertConstant(-constant), s);
@@ -283,6 +321,14 @@ class Optimizer {
return s;
}
static function hasDecl(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EVar(_): true;// has a declaration
case EFunction(_): true;// has a declaration
default: false;
}
}
static function isConstant(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true") | EIdent("false") | EIdent("null"): true;
@@ -292,6 +338,24 @@ class Optimizer {
}
}
static function isNumber(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EConst(CInt(_)): true;
case EConst(CFloat(_)): true;
case EParent(e): isNumber(e);
default: false;
}
}
static function getNumber(e:Expr):Dynamic {
return switch(Tools.expr(e)) {
case EConst(CInt(value)): value;
case EConst(CFloat(value)): value;
case EParent(e): getNumber(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function getStringConstant(e:Expr):String {
return switch(Tools.expr(e)) {
case EConst(CString(value)): value;
+69 -19
View File
@@ -37,6 +37,11 @@ class Printer {
return buf.toString();
}
public static function convertExprToString( e : Expr ) {
var printer = new Printer();
return printer.exprToString(e);
}
public function typeToString( t : CType ) {
buf = new StringBuf();
tabs = "";
@@ -108,11 +113,53 @@ class Printer {
}
}
function block(e : Expr, addSpaceIfBlock : Bool = true) {
var isBlock = Tools.expr(e).match(EBlock(_));
if(isBlock) {
if(addSpaceIfBlock) add(" ");
} else {
add("\n");
tabs += "\t";
add(tabs);
}
expr(e);
if(!isBlock) {
//add("\n");
tabs = tabs.substr(1);
//add(tabs);
}
return isBlock;
}
function isJson(s:String) {
var len = s.length;
var i = 0;
while (i < len) {
switch (StringTools.fastCodeAt(s, i++)) {
// [a-zA-Z0-9_]+
case "a".code | "b".code | "c".code | "d".code | "e".code | "f".code | "g".code | "h".code | "i".code | "j".code | "k".code | "l".code | "m".code
| "n".code | "o".code | "p".code | "q".code | "r".code | "s".code | "t".code | "u".code | "v".code | "w".code | "x".code | "y".code | "z".code
| "A".code | "B".code | "C".code | "D".code | "E".code | "F".code | "G".code | "H".code | "I".code | "J".code | "K".code | "L".code | "M".code
| "N".code | "O".code | "P".code | "Q".code | "R".code | "S".code | "T".code | "U".code | "V".code | "W".code | "X".code | "Y".code | "Z".code
| "0".code | "1".code | "2".code | "3".code | "4".code | "5".code | "6".code | "7".code | "8".code | "9".code | "_".code:
case _:
return true;
}
}
return false;
}
function expr( e : Expr ) {
if( e == null ) {
add("??NULL??");
return;
}
// TODO: make else if print correctly
switch( Tools.expr(e) ) {
case EImport(c, n):
add("import " + c);
@@ -229,33 +276,35 @@ class Printer {
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" ) ");
expr(e1);
add(" )");
var wasBlock = block(e1, true);
if( e2 != null ) {
if(!wasBlock) add("\n" + tabs);
add(" else ");
expr(e2);
block(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" ) ");
expr(e);
add(" )");
block(e, true);
case EDoWhile(cond,e):
add("do ");
expr(e);
add("do");
block(e, true);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+v+" in ");
expr(it);
add(" ) ");
expr(e);
add(" )");
block(e, true);
case EForKeyValue(v, it, e, ithv):
add("for( "+ithv+" => "+v+" in ");
expr(it);
add(" ) ");
expr(e);
add(" )");
block(e, true);
case EBreak:
add("break");
case EContinue:
@@ -307,12 +356,12 @@ class Printer {
add("throw ");
expr(e);
case ETry(e, v, t, ecatch):
add("try ");
expr(e);
add("try");
block(e);
add(" catch( " + v);
addType(t);
add(") ");
expr(ecatch);
add(")");
block(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
@@ -321,7 +370,8 @@ class Printer {
add("{\n");
for( i=>f in fl ) {
add(tabs);
add(f.name+" : ");
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
add(name+" : ");
expr(f.e);
if( i != fl.length - 1 ) add(",");
add("\n");
@@ -349,14 +399,14 @@ class Printer {
if( first ) first = false else add(", ");
expr(v);
}
add(": ");
expr(c.expr);
add(":");
block(c.expr, true);
add(";\n");
}
if( def != null ) {
add(tabs);
add("default: ");
expr(def);
block(def, true);
add(";\n");
}
tabs = tabs.substr(1);
+13 -8
View File
@@ -1,5 +1,8 @@
package;
import hscript.Interp;
import hscript.Expr;
@:access(hscript.Interp)
@:access(hscript.Parser)
class HScriptRunner {
@@ -23,27 +26,29 @@ class HScriptRunner {
public function run() {}
public function teardown() {}
private var lastExpr:Expr;
public function execute(script:String):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
var expr = Util.parse(headerCode + script + tailCode);
if(expr == null)
lastExpr = Util.parse(headerCode + script + tailCode);
if(lastExpr == null)
return "ERROR";
if (clearPrevious)
return interp.execute(expr);
return interp.execute(lastExpr);
else
return interp.exprReturn(expr);
return interp.exprReturn(lastExpr);
}
public function executeWithVars(script:String, vars:Dynamic):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
var expr = Util.parse(headerCode + script + tailCode);
if(expr == null)
lastExpr = Util.parse(headerCode + script + tailCode);
if(lastExpr == null)
return "ERROR";
for(v in Reflect.fields(vars))
interp.variables.set(v, Reflect.field(vars, v));
if (clearPrevious)
return interp.execute(expr);
return interp.execute(lastExpr);
else
return interp.exprReturn(expr);
return interp.exprReturn(lastExpr);
}
}
+6
View File
@@ -6,10 +6,12 @@ class Util {
public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) {
if (value) {
passedTestUnits++;
return true;
} else {
Sys.println("Assertion failed: " + message);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
failedTestUnits++;
return false;
}
}
@@ -26,10 +28,12 @@ class Util {
if (passed) {
passedTestUnits++;
return true;
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
failedTestUnits++;
return false;
}
}
@@ -44,10 +48,12 @@ class Util {
if (passed) {
passedTestUnits++;
return true;
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
failedTestUnits++;
return false;
}
}
+6 -4
View File
@@ -9,11 +9,12 @@ class TestBenchmark extends Benchmark {
//var hscript = "var a:Array<Float> = []; for (i in 0...1000) a.push(i * 2 + 1 / 6); if(true == true) a.push(1);";
var hscript = "
var a:Array<Float> = [];
for(i in 0...1000)
for(i in 0...1000) {
switch(5) {
case true: a.push(1);
default: a.push(2);
default: a.push(i / 1);
}
}
if(('a' == 'a') ? true : false) a.push(1);
if(true == true)
a.push(1);";
@@ -40,11 +41,12 @@ class TestBenchmark extends Benchmark {
var a:Array<Float> = [];
//for (i in 0...1000) a.push(i * 2 + 1 / 6);
//for (i in 0...1000) a.push(i / 2 + 3 / 6);
for(i in 0...1000)
for(i in 0...1000) {
switch(5) {
case 0: a.push(1);
default: a.push(2);
default: a.push(i / 1);
}
}
if(('a' == 'a') ? true : false) a.push(1);
if(true == true) a.push(1);
}
+7 -2
View File
@@ -22,8 +22,6 @@ class FinalCase extends TestCase {
var i = 5; var x = 10;
hscript.Parser.optimize = false;
assertDisplay("(i - x) * 30 + 90", ((i-x) * 30) + 90);
assertDisplay("i - x * 30 + 90", i-x * 30 + 90);
@@ -33,6 +31,13 @@ class FinalCase extends TestCase {
assertDisplay("x * 30 / i + 90", x * 30 / i + 90);
//assertEq("(x * 30) / i + 90", x * 30 / i + 90);
// (_) -> {a = false;}
// Shouldnt be converted to (_) -> return {a = false;}
Util.parse("openCodesList(false, codesOpened ? false : true, previousOpen != open ? true : false);");
Util.parse("openCodesList(false, 'HELLO' ? false : false, 'WORLD' ? true : true);");
Util.parse('FlxTween.tween(newSprite, {"scale.x": 1, "scale.y": 1, alpha: 1, angle: 0}, 0.3, {ease: FlxEase.qaudInOut});');
}
function assertDisplay(script:String, expected:Dynamic) {
+12 -2
View File
@@ -1,5 +1,7 @@
package tests;
import hscript.Printer;
@:access(hscript.Interp)
@:access(hscript.Parser)
class TestCase extends HScriptRunner {
@@ -10,7 +12,11 @@ class TestCase extends HScriptRunner {
executeWithVars(script, vars);
else
execute(script);
Util.assertEq(result, expected, message, pos);
if(!Util.assertEq(result, expected, message, pos)) {
Sys.println("> " + Printer.convertExprToString(lastExpr));
return false;
}
return true;
}
public function assertNeq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
@@ -20,6 +26,10 @@ class TestCase extends HScriptRunner {
executeWithVars(script, vars);
else
execute(script);
Util.assertNeq(result, expected, message, pos);
if(!Util.assertNeq(result, expected, message, pos)) {
Sys.println("> " + Printer.convertExprToString(lastExpr));
return false;
}
return true;
}
}