diff --git a/hscript/Optimizer.hx b/hscript/Optimizer.hx index c4ee4b2..2614045 100644 --- a/hscript/Optimizer.hx +++ b/hscript/Optimizer.hx @@ -10,11 +10,10 @@ class Optimizer { if(s == null) return null; - #if hscriptPos - var e = s.e; - #else - var e = s; - #end + var e = Tools.expr(s); + + // TODO: Convert EBinOp(+, a, EUnop(-, b)) to EBinOp(-, a, b) aka (a - b) + switch(e) { // Parse all expressions and recreate the AST case EBlock(exprs): @@ -22,7 +21,34 @@ class Optimizer { if(Tools.expr(v).match(EBlock([]))) return false; // Remove empty blocks return true; }); - return mk(EBlock(exprs), s); + + var newExprs = []; + 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; + } + if(!declares) { + for(ex in iex) + newExprs.push(ex); + } else { + newExprs.push(e); + } + case EReturn(_): // remove stuff that are after a return + newExprs.push(e); + break; + default: + newExprs.push(e); + } + } + return mk(EBlock(newExprs), s); case EIf(econd, e1, e2): var econd = optimize(econd); @@ -41,6 +67,24 @@ class Optimizer { } } return mk(EIf(econd, e1, e2), s); + + case ETernary(econd, e1, e2): + var econd = optimize(econd); + var e1 = optimize(e1); + var e2 = optimize(e2); + if(isConstant(econd)) { + var econd = getConstant(econd); + if(econd == true) { + if(e1 == null) + return mk(EBlock([]), s); + return mk(EParent(e1), s); + } else { + if(e2 == null) + return mk(EBlock([]), s); + return mk(EParent(e2), s); + } + } + return mk(ETernary(econd, e1, e2), s); case EWhile(econd, e): var econd = optimize(econd); var e = optimize(e); @@ -105,7 +149,16 @@ class Optimizer { case EParent(e): e = optimize(e); - return mk(EParent(e), s); + //return mk(EParent(e), s); + return mk(Tools.expr(e), s); + + case ECheckType(e, t): + e = optimize(e); + return mk(ECheckType(e, t), s); + + case EMeta(name, args, e): + e = optimize(e); + return mk(EMeta(name, args, e), s); case ENew(cl, args): args = args.map((v) -> optimize(v)); @@ -117,6 +170,13 @@ class Optimizer { }); return mk(EObject(fl), s); + case EImport(c, n): + return mk(EImport(c, n), s); + + case EClass(name, fields, extend, interfaces): // Possible code not working + fields = fields.map((v) -> optimize(v)); + return mk(EClass(name, fields, extend, interfaces), s); + case EFunction(args, e, name, ret, isPublic, isStatic, isOverride): args.map((v) -> { v.name = v.name; @@ -132,6 +192,40 @@ class Optimizer { v.expr = optimize(v.expr); }); def = optimize(def); + + if(isConstant(e)) { + var econd = getConstant(e); + + for(c in cases) { + for(v in c.values) { + if(isConstant(v)) { + var value = getConstant(v); + if(value == econd) + return mk(Tools.expr(c.expr), s); + } + } + } + + var isAllCasesConstant = true; + for(c in cases) { + for(v in c.values) { + if(!isConstant(v)) { + isAllCasesConstant = false; + break; + } + } + } + if(isAllCasesConstant) { + #if debug + //trace("Didnt find any cases that match"); + #end + if(def != null) + return mk(Tools.expr(def), s); + else + return mk(EBlock([]), s); + } + } + return mk(ESwitch(e, cases, def), s); case EArrayDecl(arr, wantedType): diff --git a/hscript/Parser.hx b/hscript/Parser.hx index b058ca5..58f2429 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -44,7 +44,7 @@ enum Token { } class Parser { - public static var optimize = true; + public static var optimize = #if NO_HSCRIPT_OPTIMIZE false #else true #end; // config / variables public var line : Int; @@ -474,7 +474,7 @@ class Parser { if( opPriority.get(op) < 0 ) return makeUnop(op,parseExpr()); return unexpected(tk); - case TBkOpen: + case TBkOpen: // [ var a = new Array(); tk = token(); while( tk != TBkClose && (!resumeErrors || tk != TEof) ) { @@ -487,7 +487,7 @@ class Parser { if( tk == TComma ) tk = token(); } - if( a.length == 1 && a[0] != null ) // What is this for??? + if( a.length == 1 && a[0] != null ) // Checks if its a for comprehension switch( expr(a[0]) ) { case EFor(_), EForKeyValue(_), EWhile(_), EDoWhile(_): var tmp = "__a_" + (uid++); diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 3db0b0f..0e6cf1f 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -113,7 +113,7 @@ class Printer { add("??NULL??"); return; } - switch( #if hscriptPos e.e #else e #end ) { + switch( Tools.expr(e) ) { case EImport(c, n): add("import " + c); if(n != null) @@ -127,6 +127,7 @@ class Printer { } add(' {\n'); tabs += "\t"; + // TODO: Print fields //for(field in fields) { // expr(field); //} @@ -162,15 +163,43 @@ class Printer { add(";\n"); } tabs = tabs.substr(1); + add(tabs); add("}"); } case EField(e, f, s): expr(e); add((s == true ? "?." : ".") + f); case EBinop(op, e1, e2): - expr(e1); + var shouldParen = false; + var op1 = switch(Tools.expr(e1)) { + case EBinop(op, _, _): op; + case EConst(_): "_"; + case EIdent(_): "_"; + default: null; + } + var op2 = switch(Tools.expr(e2)) { + case EBinop(op, _, _): op; + case EConst(_): "_"; + case EIdent(_): "_"; + default: null; + } + var paran = Tools.checkOpPrecedence(op, op1, op2); + + if(paran == 0 || paran == 2) { + add("("); + expr(e1); + add(")"); + } else { + expr(e1); + } add(" " + op + " "); - expr(e2); + if(paran == 1 || paran == 2) { + add("("); + expr(e2); + add(")"); + } else { + expr(e2); + } case EUnop(op, pre, e): if( pre ) { add(op); @@ -182,13 +211,13 @@ class Printer { case ECall(e, args): if( e == null ) expr(e); - else switch( #if hscriptPos e.e #else e #end ) { - case EField(_), EIdent(_), EConst(_): - expr(e); - default: - add("("); - expr(e); - add(")"); + else switch( Tools.expr(e) ) { + case EField(_), EIdent(_), EConst(_): + expr(e); + default: + add("("); + expr(e); + add(")"); } add("("); var first = true; @@ -290,13 +319,15 @@ class Printer { } else { tabs += "\t"; add("{\n"); - for( f in fl ) { + for( i=>f in fl ) { add(tabs); add(f.name+" : "); expr(f.e); - add(",\n"); + if( i != fl.length - 1 ) add(","); + add("\n"); } tabs = tabs.substr(1); + add(tabs); add("}"); } case ETernary(c,e1,e2): @@ -308,8 +339,10 @@ class Printer { case ESwitch(e, cases, def): add("switch( "); expr(e); - add(") {"); + add(" ) {\n"); + tabs += "\t"; for( c in cases ) { + add(tabs); add("case "); var first = true; for( v in c.values ) { @@ -321,10 +354,13 @@ class Printer { add(";\n"); } if( def != null ) { + add(tabs); add("default: "); expr(def); add(";\n"); } + tabs = tabs.substr(1); + add(tabs); add("}"); case EMeta(name, args, e): add("@"); diff --git a/hscript/Tools.hx b/hscript/Tools.hx index 6855773..504a80b 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -122,4 +122,76 @@ class Tools { } } + static var priorities = [ + ["%"], + ["*", "/"], + ["+", "-"], + ["<<", ">>", ">>>"], + ["|", "&", "^"], + ["==", "!=", ">", "<", ">=", "<="], + ["..."], + ["&&"], + ["||"], + ["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>","??"+"="], + ["->", "??"], + ["is"] + ]; + public static function checkOpPrecedence(mainOp:String, leftOp:String, rightOp:String):Int { + var mainOpGroup = getOpGroup(mainOp); + var leftOpGroup = getOpGroup(leftOp); + var rightOpGroup = getOpGroup(rightOp); + + var leftParam = false; + var rightParam = false; + + if(leftOpGroup > mainOpGroup && leftOpGroup != -1) leftParam = true; + if(rightOpGroup > mainOpGroup && rightOpGroup != -1) rightParam = true; + + if(!leftParam && !rightParam) { + var mainOpIndex = getOpIndex(mainOp); + var leftOpIndex = getOpIndex(leftOp); + var rightOpIndex = getOpIndex(rightOp); + + if(mainOpGroup == rightOpGroup) { + if(rightOpIndex < mainOpIndex) rightParam = true; + } + if(leftOpGroup == mainOpGroup) { + if(leftOpIndex < mainOpIndex) leftParam = true; + } + } + + // Convert to index + if(!leftParam && !rightParam) return -1; + if(leftParam && rightParam) return 2; + if(leftParam) return 0; + if(rightParam) return 1; + return -1; + } + + public static function getOpIndex(op:String):Int { + if(op == "_") return -1; + var i = 0; + for(p in priorities) { + for(pp in p) { + if(op == pp) + return i; + i++; + } + } + return -1; + } + + public static function getOpGroup(op:String):Int { + if(op == "_") return -1; + var i = 0; + for(p in priorities) { + for(pp in p) { + if(op == pp) + return i; + } + i++; + } + return -1; + } + } \ No newline at end of file diff --git a/tests/mac.hxml b/tests/mac.hxml index fc3b7ef..3ec74d6 100644 --- a/tests/mac.hxml +++ b/tests/mac.hxml @@ -13,12 +13,19 @@ --define HXCPP_CHECK_POINTER --define HXCPP_STACK_LINE --define HXCPP_DEBUG_LINK +# -D HXCPP_M64 +# -D HXCPP_ARM64 --dce no # File Specific Defines --define macos --define mac +--macro keep('IntIterator') +# --macro hscript.macros.UsingHandler.init() +# --macro hscript.macros.ClassExtendMacro.init() + + # Copying the assets from source code to bin folder --cmd rm -rf bin/cpp/assets --cmd cp -r assets bin/cpp/assets diff --git a/tests/src/Main.hx b/tests/src/Main.hx index c4276bf..3a7acd4 100644 --- a/tests/src/Main.hx +++ b/tests/src/Main.hx @@ -38,6 +38,7 @@ class Main { runTest("SwitchStatement", new SwitchCase()); // TODO: UnicodeCase.hx? // TODO: UnicodeStringCase.hx? + runTest("Final", new FinalCase()); Util.printTestResults(); #end } diff --git a/tests/src/benchmark/TestBenchmark.hx b/tests/src/benchmark/TestBenchmark.hx index 76433c5..00d9e93 100644 --- a/tests/src/benchmark/TestBenchmark.hx +++ b/tests/src/benchmark/TestBenchmark.hx @@ -6,20 +6,46 @@ class TestBenchmark extends Benchmark { super("Test", 10000); } - var hscript = "var a:Array = []; for (i in 0...1000) a.push(i * 2 + 1 / 6); if(true == true) a.push(1);"; + //var hscript = "var a:Array = []; for (i in 0...1000) a.push(i * 2 + 1 / 6); if(true == true) a.push(1);"; + var hscript = " + var a:Array = []; + for(i in 0...1000) + switch(5) { + case true: a.push(1); + default: a.push(2); + } + if(('a' == 'a') ? true : false) a.push(1); + if(true == true) + a.push(1);"; + + function test() { + return 5; + } + + var aaa = null; public override function reset() { super.reset(); - a = []; - if(interp != null) - interp.variables.remove("a"); + aaa = { + test: test + }; if(expr == null) cacheExpr(hscript); + interp.variables.remove("a"); + //interp.variables.set("aaa", aaa); + interp.variables.set("test", test); } - public var a:Array = []; public override function haxeBenchmark() { - for (i in 0...1000) a.push(i * 2 + 1 / 6); + var a:Array = []; + //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) + switch(5) { + case 0: a.push(1); + default: a.push(2); + } + if(('a' == 'a') ? true : false) a.push(1); if(true == true) a.push(1); } diff --git a/tests/src/tests/FinalCase.hx b/tests/src/tests/FinalCase.hx new file mode 100644 index 0000000..9355b66 --- /dev/null +++ b/tests/src/tests/FinalCase.hx @@ -0,0 +1,47 @@ +package tests; + +class FinalCase extends TestCase { + override function setup() { + super.setup(); + } + + override function getNewInterp() { + var interp = super.getNewInterp(); + interp.variables.set("Std", Std); + interp.variables.set("String", String); + interp.variables.set("Bool", Bool); + interp.variables.set("Float", Float); + interp.variables.set("Array", Array); + interp.variables.set("Int", Int); + interp.variables.set("IntIterator", IntIterator); + return interp; + } + + override function run() { + headerCode = "var i = 5; var x = 10;"; + + 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); + + // Test operator precedence + assertDisplay("i / x * 30 + 90", i / x * 30 + 90); + assertDisplay("i / (x * 30) + 90", i / (x * 30) + 90); + + assertDisplay("x * 30 / i + 90", x * 30 / i + 90); + //assertEq("(x * 30) / i + 90", x * 30 / i + 90); + } + + function assertDisplay(script:String, expected:Dynamic) { + var _script = script; + script = '"${script}";\n' + script; + assertEq(script, expected, _script); + } + + override function teardown() { + super.teardown(); + } +} \ No newline at end of file