From b6df96a4b0dffdf95e93c30fef66a0fc0d96cb0d Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Thu, 25 Apr 2024 04:14:41 +0200 Subject: [PATCH] EMapDecl + Map Comprehension --- hscript/Expr.hx | 11 ++- hscript/Interp.hx | 111 +++++++++------------ hscript/Macro.hx | 6 ++ hscript/Optimizer.hx | 39 ++++++-- hscript/Parser.hx | 184 ++++++++++++++++++++++++++++++----- hscript/Printer.hx | 34 ++++++- hscript/Tools.hx | 2 + tests/src/Util.hx | 143 ++++++++++++++++++++++----- tests/src/tests/FinalCase.hx | 29 ++++++ 9 files changed, 432 insertions(+), 127 deletions(-) diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 8f690a8..bb51ec1 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -30,6 +30,14 @@ enum Const { #end } +enum MapType { + IntMap; + StringMap; + EnumMap; + ObjectMap; + UnknownMap; +} + #if hscriptPos class Expr { public var e : ExprDef; @@ -69,7 +77,8 @@ enum Expr EFunction( args : Array, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool ); EReturn( ?e : Expr ); EArray( e : Expr, index : Expr ); - EArrayDecl( e : Array, ?wantedType: CType ); + EMapDecl( type: MapType, keys: Array, values: Array ); + EArrayDecl( e : Array ); ENew( cl : String, params : Array ); EThrow( e : Expr ); ETry( e : Expr, v : String, t : Null, ecatch : Expr ); diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 406347d..704eb2d 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -789,77 +789,54 @@ class Interp { } } return f; - case EArrayDecl(arr, wantedType): - var isTypeMap = wantedType != null; - var isMap = if(isTypeMap) { - wantedType.match(CTPath(["Map"], [_, _])); - } else { - arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _)); - } - if (isMap) { - var isAllString:Bool = true; - var isAllInt:Bool = true; - var isAllObject:Bool = true; - var isAllEnum:Bool = true; - var keys:Array = []; - var values:Array = []; - for (e in arr) { - switch (Tools.expr(e)) { - case EBinop("=>", eKey, eValue): { - var key:Dynamic = expr(eKey); - var value:Dynamic = expr(eValue); - if(isAllString) - isAllString = (key is String); - if(isAllInt) - isAllInt = (key is Int); - if(isAllObject) - isAllObject = Reflect.isObject(key); - if(isAllEnum) - isAllEnum = Reflect.isEnumValue(key); - keys.push(key); - values.push(value); - } - default: throw("=> expected"); - } + case EMapDecl(type, _keys, _values): + var keys:Array = []; + var values:Array = []; + if(type == UnknownMap) { + var isKeyString:Bool = false; + var isKeyInt:Bool = false; + var isKeyObject:Bool = false; + var isKeyEnum:Bool = false; + for (i in 0..._keys.length) { + var key:Dynamic = expr(_keys[i]); + var value:Dynamic = expr(_values[i]); + + if(!isKeyString) isKeyString = (key is String); + if(!isKeyInt) isKeyInt = (key is Int); + if(!isKeyObject) isKeyObject = Reflect.isObject(key); + if(!isKeyEnum) isKeyEnum = Reflect.isEnumValue(key); + + keys.push(key); + values.push(value); } - if(isTypeMap) { - isAllString = wantedType.match(CTPath(["Map"], [CTPath(["String"], _), _])); - isAllInt = wantedType.match(CTPath(["Map"], [CTPath(["Int"], _), _])); - if(isAllString || isAllInt) { - isAllObject = false; - isAllEnum = false; - } else { - if(!isAllObject && !isAllEnum) { - throw("Unknown Type Key"); - } - } - } + var t = b2i(isKeyString) + b2i(isKeyInt) + b2i(isKeyObject) + b2i(isKeyEnum); - var map:IMap = { - if (isAllInt) - new haxe.ds.IntMap(); - else if (isAllString) - new haxe.ds.StringMap(); - else if (isAllEnum) - new haxe.ds.EnumValueMap(); - else if (isAllObject) - new haxe.ds.ObjectMap(); - else - throw 'Inconsistent key types'; - } - for (n in 0...keys.length) { - map.set(keys[n], values[n]); - } - return map; + if(t != 1) + error(ECustom("Unknown Map Type")); + else if(isKeyInt) type = IntMap; + else if(isKeyString) type = StringMap; + else if(isKeyEnum) type = EnumMap; + else if(isKeyObject) type = ObjectMap; } else { - //var a = new Array(); - //for (e in arr) { - // a.push(expr(e)); - //} - //return a; - return [for (j in 0...arr.length) expr(arr[j])]; + for(i in 0..._keys.length) { + keys.push(expr(_keys[i])); + values.push(expr(_values[i])); + } } + var map:IMap = switch(type) { + case IntMap: new haxe.ds.IntMap(); + case StringMap: new haxe.ds.StringMap(); + case EnumMap: new haxe.ds.EnumValueMap(); + case ObjectMap: new haxe.ds.ObjectMap(); + default: null; + } + for (n in 0...keys.length) { + map.set(keys[n], values[n]); + } + return map; + case EArrayDecl(arr): + return [for (j in 0...arr.length) expr(arr[j])]; case EArray(e, index): var arr:Dynamic = expr(e); var index:Dynamic = expr(index); @@ -1163,4 +1140,6 @@ class Interp { c = Type.resolveClass(cl); return (c is IHScriptCustomConstructor) ? cast(c, IHScriptCustomConstructor).hnew(args) : Type.createInstance(c, args); } + + static inline function b2i(b:Bool) return b ? 1 : 0; } diff --git a/hscript/Macro.hx b/hscript/Macro.hx index d49f466..dc51cdb 100644 --- a/hscript/Macro.hx +++ b/hscript/Macro.hx @@ -239,6 +239,12 @@ class Macro { EReturn(e == null ? null : convert(e)); case EArray(e, index): EArray(convert(e), convert(index)); + case EMapDecl(type, keys, values): // port to array decl, haxe still uses EArrayDecl but with => as a binop + var el = []; + for( i in 0...keys.length ) { + el.push( hscript.Expr.EBinop("=>", keys[i], values[i]) ); + } + EArrayDecl(map(el,convert)); case EArrayDecl(el): EArrayDecl(map(el,convert)); case ENew(cl, params): diff --git a/hscript/Optimizer.hx b/hscript/Optimizer.hx index 3886d0c..8e9e234 100644 --- a/hscript/Optimizer.hx +++ b/hscript/Optimizer.hx @@ -20,6 +20,7 @@ class Optimizer { // 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) + // TODO: Optimize EIf(cond, e1, e2) to EBlock([cond, e1]) if e1 == e2, if cond is constant or side effect free then convert to EParent(e1) switch(e) { // Parse all expressions and recreate the AST @@ -247,22 +248,40 @@ class Optimizer { return mk(ESwitch(e, cases, def), s); - case EArrayDecl(arr, wantedType): + case EMapDecl(type, keys, values): + keys = keys.map((v) -> optimize(v)); + values = values.map((v) -> optimize(v)); + return mk(EMapDecl(type, keys, values), s); + + case EArrayDecl(arr): arr = arr.map((v) -> optimize(v)); - return mk(EArrayDecl(arr, wantedType), s); + return mk(EArrayDecl(arr), s); case EArray(e, index): e = optimize(e); index = optimize(index); - if(isConstant(index) && Tools.expr(e).match(EArrayDecl(_, _))) { - var arr = switch(Tools.expr(e)) { - case EArrayDecl(arr, _): arr; - default: null; - }; + if(isConstant(index)) { + if(Tools.expr(e).match(EArrayDecl(_))) { + var arr = switch(Tools.expr(e)) { + case EArrayDecl(arr): arr; + default: null; + }; - var constant = Lambda.exists(arr, isConstant); - if(constant) { - return mk(Tools.expr(arr[getConstant(index)]), s); + var constant = Lambda.exists(arr, isConstant); + if(constant) { + return mk(Tools.expr(arr[getConstant(index)]), s); + } + } + if(Tools.expr(e).match(EMapDecl(_))) { + var map = switch(Tools.expr(e)) { + case EMapDecl(type, keys, vals): [keys, vals]; + default: null; + }; + + var constant = Lambda.exists(map, (v) -> isConstant(v[0]) && isConstant(v[1])); + if(constant) { + return mk(Tools.expr(map[getConstant(index)][1]), s); + } } } return mk(EArray(e, index), s); diff --git a/hscript/Parser.hx b/hscript/Parser.hx index e148bf3..6a3822d 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -115,8 +115,6 @@ class Parser { var startIdents : Array; var uid : Int = 0; - var disableOrOp : Bool = false; - #if hscriptPos var origin : String; var tokenMin : Int; @@ -481,10 +479,7 @@ class Parser { return mk(EFunction([], mk(EReturn(eret),p1)), p1); } push(tk); - //var oldoo = disableOrOp; - //disableOrOp = false; var e = parseExpr(); - //disableOrOp = oldoo; tk = token(); switch( tk ) { case TPClose: @@ -556,10 +551,7 @@ class Parser { case TOp(op): if( op == "-" ) { var start = tokenMin; - //var oldoo = disableOrOp; - //disableOrOp = false; var e = parseExpr(); - //disableOrOp = oldoo; if( e == null ) return makeUnop(op,e); switch( Tools.expr(e) ) { @@ -579,27 +571,104 @@ class Parser { tk = token(); while( tk != TBkClose && (!resumeErrors || tk != TEof) ) { push(tk); - //var oldoo = disableOrOp; - //disableOrOp = false; a.push(parseExpr()); - //disableOrOp = oldoo; tk = token(); if( tk == TComma ) tk = token(); } - if( a.length == 1 && a[0] != null ) // Checks if its a for comprehension + if( a.length == 1 && a[0] != null ) {// Checks if its a for comprehension switch( Tools.expr(a[0]) ) { - case EFor(_), EForKeyValue(_), EWhile(_), EDoWhile(_): + case EFor(_, _, e), EForKeyValue(_, _, e, _), EWhile(_, e), EDoWhile(_, e): + var e = Tools.expr(e); + + if(isMapCompr(a[0])) { + var tmp = "__m_" + (uid++); + var tmp2 = "__m_" + (uid++); + var e = mk(EBlock([ + // TODO: Make it detect simple map comprehensions so we can optimize the map type + mk(EVar(tmp, null, mk(EMapDecl(ObjectMap, [], []), p1)), p1), // Assume ObjectMap, since it supports dynamic keys + mk(EVar(tmp2, null, mk(EField(mk(EIdent(tmp), p1), "set"), p1)), p1), + mapMapCompr(tmp2, a[0]), + mk(EIdent(tmp),p1), + ]),p1); + return parseExprNext(e); + } + var tmp = "__a_" + (uid++); + var tmp2 = "__a_" + (uid++); var e = mk(EBlock([ mk(EVar(tmp, null, mk(EArrayDecl([]), p1)), p1), - mapCompr(tmp, a[0]), + mk(EVar(tmp2, null, mk(EField(mk(EIdent(tmp), p1), "push"), p1)), p1), + mapArrCompr(tmp2, a[0]), mk(EIdent(tmp),p1), ]),p1); return parseExprNext(e); default: } - return parseExprNext(mk(EArrayDecl(a, nextType), p1)); + } + + var isTypeMap = (nextType != null) && nextType.match(CTPath(["Map"], [_, _])); + var isMap = isTypeMap; + if(!isMap) { + isMap = Lambda.exists(a, (e) -> Tools.expr(e).match(EBinop("=>", _))); // Check if any element is a => b + } + if(isMap) { + // TODO: clean up this code more + var isKeyString:Bool = false; + var isKeyInt:Bool = false; + var isKeyObject:Bool = false; + var isKeyEnum:Bool = false; + var keys:Array = []; + var values:Array = []; + for (e in a) { + switch (Tools.expr(e)) { + case EBinop("=>", eKey, eValue): { + switch(Tools.expr(eKey)) { + case EConst(CInt(_)):// | EConst(CFloat(_)): + isKeyInt = true; + case EConst(CString(_)): + isKeyString = true; + case EIdent(_): + isKeyObject = true; + // TODO: add more stuffs for isKeyObject + default: + } + keys.push(eKey); + values.push(eValue); + } + default: + error(ECustom("Expected a => b"), p1, p1); + } + } + + if(isTypeMap) { + isKeyString = nextType.match(CTPath(["Map"], [CTPath(["String"], _), _])); + isKeyInt = nextType.match(CTPath(["Map"], [CTPath(["Int"], _), _])); + if(isKeyString || isKeyInt) { + isKeyObject = false; + isKeyEnum = false; + } else { + if(!isKeyObject && !isKeyEnum) { + error(ECustom("Unknown Type Key"), p1, p1); + } + } + } + + var t = b2i(isKeyString) + b2i(isKeyInt) + b2i(isKeyObject) + b2i(isKeyEnum); + + var type:MapType = null; + if(t != 1) type = UnknownMap; + else if(isKeyInt) type = IntMap; + else if(isKeyString) type = StringMap; + else if(isKeyEnum) type = EnumMap; + else if(isKeyObject) type = ObjectMap; + + if(type == null) + error(ECustom("Unknown Map Type"), p1, p1); + + return parseExprNext(mk(EMapDecl(type, keys, values), p1)); + } + return parseExprNext(mk(EArrayDecl(a), p1)); case TMeta(id) if( allowMetadata ): var args = parseMetaArgs(); return mk(EMeta(id, args, parseExpr()),p1); @@ -608,6 +677,8 @@ class Parser { } } + static inline function b2i(b:Bool) return b ? 1 : 0; + function parseLambda( args : Array, pmin ) { while( true ) { var id = getIdent(); @@ -652,29 +723,92 @@ class Parser { return args; } - function mapCompr( tmp : String, e : Expr ) { + function mapArrCompr( tmp : String, e : Expr ) { if( e == null ) return null; var edef = switch( Tools.expr(e) ) { case EFor(v, it, e2): - EFor(v, it, mapCompr(tmp, e2)); + EFor(v, it, mapArrCompr(tmp, e2)); case EForKeyValue(v, it, e2, ithv): - EForKeyValue(v, it, mapCompr(tmp, e2), ithv); + EForKeyValue(v, it, mapArrCompr(tmp, e2), ithv); case EWhile(cond, e2): - EWhile(cond, mapCompr(tmp, e2)); + EWhile(cond, mapArrCompr(tmp, e2)); case EDoWhile(cond, e2): - EDoWhile(cond, mapCompr(tmp, e2)); + EDoWhile(cond, mapArrCompr(tmp, e2)); case EIf(cond, e1, e2) if( e2 == null ): - EIf(cond, mapCompr(tmp, e1), null); + EIf(cond, mapArrCompr(tmp, e1), null); + case EIf(cond, e1, e2) if( e2 != null ): + EIf(cond, mapArrCompr(tmp, e1), mapArrCompr(tmp, e2)); case EBlock([e]): - EBlock([mapCompr(tmp, e)]); + EBlock([mapArrCompr(tmp, e)]); case EParent(e2): - EParent(mapCompr(tmp, e2)); + EParent(mapArrCompr(tmp, e2)); default: - ECall( mk(EField(mk(EIdent(tmp), pmin(e), pmax(e)), "push"), pmin(e), pmax(e)), [e]); + // tmp.push(v); + //ECall( mk(EField(mk(EIdent(tmp), pmin(e), pmax(e)), "push"), pmin(e), pmax(e)), [e]); + ECall( mk(EIdent(tmp), pmin(e), pmax(e)), [e]); } return mk(edef, pmin(e), pmax(e)); } + function mapMapCompr( tmp : String, e : Expr ) { + if( e == null ) return null; + var edef = switch( Tools.expr(e) ) { + case EFor(v, it, e2): + EFor(v, it, mapMapCompr(tmp, e2)); + case EForKeyValue(v, it, e2, ithv): + EForKeyValue(v, it, mapMapCompr(tmp, e2), ithv); + case EWhile(cond, e2): + EWhile(cond, mapMapCompr(tmp, e2)); + case EDoWhile(cond, e2): + EDoWhile(cond, mapMapCompr(tmp, e2)); + case EIf(cond, e1, e2) if( e2 == null ): + EIf(cond, mapMapCompr(tmp, e1), null); + case EIf(cond, e1, e2) if( e2 != null ): + EIf(cond, mapMapCompr(tmp, e1), mapMapCompr(tmp, e2)); + case EBlock([e]): + EBlock([mapMapCompr(tmp, e)]); + case EParent(e2): + EParent(mapMapCompr(tmp, e2)); + default: + // tmp.set(k, v); + switch( Tools.expr(e) ) { + case EBinop("=>", e1, e2): + //ECall( mk(EField(mk(EIdent(tmp), pmin(e), pmax(e)), "set"), pmin(e), pmax(e)), [e1, e2]); + ECall( mk(EIdent(tmp), pmin(e), pmax(e)), [e1, e2]); + default: // default incase of error + //ECall( mk(EField(mk(EIdent(tmp), pmin(e), pmax(e)), "push"), pmin(e), pmax(e)), [e]); + ECall( mk(EIdent(tmp), pmin(e), pmax(e)), [e, mk(EIdent("null"), pmin(e), pmax(e))]); + } + } + return mk(edef, pmin(e), pmax(e)); + } + + function isMapCompr( e : Expr ) { + if( e == null ) throw "Invalid map comprehension"; + //if( e == null ) return true; + return switch( Tools.expr(e) ) { + case EFor(v, it, e2): + isMapCompr(e2); + case EForKeyValue(v, it, e2, ithv): + isMapCompr(e2); + case EWhile(cond, e2): + isMapCompr(e2); + case EDoWhile(cond, e2): + isMapCompr(e2); + case EIf(cond, e1, e2) if( e2 == null ): + isMapCompr(e1); + case EIf(cond, e1, e2) if( e2 != null ): + isMapCompr(e1) && isMapCompr(e1); + case EBlock([e]): + isMapCompr(e); + case EParent(e2): + isMapCompr(e2); + default: + // tmp.set(k, v); + return Tools.expr(e).match(EBinop("=>", _)); + } + } + function makeUnop( op, e ) { if( e == null && resumeErrors ) return null; @@ -1151,6 +1285,8 @@ class Parser { } } + var disableOrOp:Bool = false; + function parseExprNext( e1 : Expr ) { var tk = token(); switch( tk ) { diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 281dd46..cb209fa 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -238,7 +238,22 @@ class Printer { add(v); case EVar(n, t, e): // TODO: static, public, override add("var " + n); - addType(t); + if( t != null ) + addType(t); + else + switch( Tools.expr(e) ) { + case EMapDecl(type, _, _): + add(" : Map<"); + switch( type ) { + case ObjectMap: add("Dynamic"); + case StringMap: add("String"); + case EnumMap: add("EnumValue"); + case IntMap: add("Int"); + case UnknownMap: add("?"); + } + add(", Dynamic>"); + default: + } if( e != null ) { add(" = "); expr(e); @@ -286,7 +301,10 @@ class Printer { } else { expr(e1); } - add(" " + op + " "); + if(op == "...") + add(op); + else + add(" " + op + " "); if(paran == 1 || paran == 2) { add("("); expr(e2); @@ -383,7 +401,17 @@ class Printer { add("["); expr(index); add("]"); - case EArrayDecl(el, _): + case EMapDecl(type, keys, values): + add("["); + var first = true; + for( i in 0...keys.length ) { + if( first ) first = false else add(", "); + expr(keys[i]); + add(" => "); + expr(values[i]); + } + add("]"); + case EArrayDecl(el): add("["); var first = true; for( e in el ) { diff --git a/hscript/Tools.hx b/hscript/Tools.hx index 504a80b..5e30f1d 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -45,6 +45,7 @@ class Tools { case EFunction(_, e, _, _): f(e); case EReturn(e): if( e != null ) f(e); case EArray(e, i): f(e); f(i); + case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e); case EArrayDecl(el): for( e in el ) f(e); case ENew(_,el): for( e in el ) f(e); case EThrow(e): f(e); @@ -82,6 +83,7 @@ class Tools { case EFunction(args, e, name, t): EFunction(args, f(e), name, t); case EReturn(e): EReturn(if( e != null ) f(e) else null); case EArray(e, i): EArray(f(e),f(i)); + case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]); case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]); case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]); case EThrow(e): EThrow(f(e)); diff --git a/tests/src/Util.hx b/tests/src/Util.hx index fa7c48c..3be93d1 100644 --- a/tests/src/Util.hx +++ b/tests/src/Util.hx @@ -1,9 +1,101 @@ import hscript.Expr.Error; import hscript.Printer; +import haxe.Constraints.IMap; using StringTools; class Util { + // TODO: check this for bugs + static function deepEqual(a:Dynamic, b:Dynamic):Bool { + if (a == b) { + return true; + } + + if (a == null || b == null) { + return false; + } + + if ((a is Array) && (b is Array)) { + var aArray:Array = cast a; + var bArray:Array = cast b; + + if (aArray.length != bArray.length) { + return false; + } + + for (i in 0...aArray.length) { + if (!deepEqual(aArray[i], bArray[i])) { + return false; + } + } + + return true; + } + + if(Std.isOfType(a, Enum) && Std.isOfType(b, Enum)) { + if(Type.enumEq(a, b)) + return true; + } + + // Check Map Equality + if (Std.isOfType(a, IMap) && Std.isOfType(b, IMap)) { + var aMap:IMap = cast a; + var bMap:IMap = cast b; + + var aFields = [for(v in aMap.keys()) v]; + var bFields = [for(v in bMap.keys()) v]; + + if (aFields.length != bFields.length) { + return false; + } + + // Sort fields to ensure consistent comparison + aFields.sort(Reflect.compare); + bFields.sort(Reflect.compare); + + for (key in aMap.keys()) { + if (!bMap.exists(key) || !deepEqual(aMap.get(key), bMap.get(key))) { + return false; + } + } + return true; + } + + if (Reflect.isObject(a) && Reflect.isObject(b)) { + var aFields = Reflect.fields(a); + var bFields = Reflect.fields(b); + + if (aFields.length != bFields.length) { + return false; + } + + // Sort fields to ensure consistent comparison + aFields.sort(Reflect.compare); + bFields.sort(Reflect.compare); + + for (i in 0...aFields.length) { + var field = aFields[i]; + if (field != bFields[i] || !deepEqual(Reflect.field(a, field), Reflect.field(b, field))) { + return false; + } + } + + return true; + } + + if(!Type.enumEq(Type.typeof(a), Type.typeof(b))) { + if(Type.typeof(a) == TInt && Type.typeof(b) == TFloat) { + return cast(a, Int) == cast(b, Float); + } + if(Type.typeof(a) == TFloat && Type.typeof(b) == TInt) { + return cast(a, Float) == cast(b, Int); + } + } + + return false; + } + + public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) { if (value) { passedTestUnits++; @@ -17,15 +109,16 @@ class Util { } public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) { - var passed = value == expected; - if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { - if (deepCompareArrays(value, expected)) - passed = true; - } - else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) { - if(Type.enumEq(value, expected)) - passed = true; - } + var equals = deepEqual(value, expected); + var passed = equals; + //if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { + // if (deepCompareArrays(value, expected)) + // passed = true; + //} + //else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) { + // if(Type.enumEq(value, expected)) + // passed = true; + //} if (passed) { passedTestUnits++; @@ -39,15 +132,17 @@ class Util { } public static function assertEqPrintable(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) { - var passed = value == expected; - if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { - if (deepCompareArrays(value, expected)) - passed = true; - } - else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) { - if(Type.enumEq(value, expected)) - passed = true; - } + //var passed = value == expected; + //if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { + // if (deepCompareArrays(value, expected)) + // passed = true; + //} + //else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) { + // if(Type.enumEq(value, expected)) + // passed = true; + //} + var equals = deepEqual(value, expected); + var passed = equals; if (passed) { passedTestUnits++; @@ -61,11 +156,13 @@ class Util { } public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) { - var passed = value != expected; - if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { - if (!deepCompareArrays(value, expected)) - passed = true; - } + //var passed = value != expected; + //if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { + // if (!deepCompareArrays(value, expected)) + // passed = true; + //} + var equals = deepEqual(value, expected); + var passed = !equals; // WARNING THIS MIGHT NOT WORK diff --git a/tests/src/tests/FinalCase.hx b/tests/src/tests/FinalCase.hx index ac99a91..f1e0623 100644 --- a/tests/src/tests/FinalCase.hx +++ b/tests/src/tests/FinalCase.hx @@ -55,6 +55,35 @@ class FinalCase extends TestCase { Util.parse('test ? 0xFF343434 : 0xFF92A2FF'); Util.parse('test ? 1 : 0.75;'); + Util.runKnownBug("Function then array causes error, unless the function ends with ;", function() { + headerCode = ""; + function isEven(i:Int):Bool { return i % 2 == 0; } + assertEq("function isEven(i:Int):Bool { return i % 2 == 0; }[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]); + }); + + assertEq("[for(i in 0...10) i]", [for(i in 0...10) i]); + assertEq("[for(i in 0...10) i => null]", [for(i in 0...10) i => null]); + assertEq("[for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']", [for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']); + assertEq("[for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']", [for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']); + assertEq("[for(i in 0...10) for(j in 0...10) i * j]", [for(i in 0...10) for(j in 0...10) i * j]); + + headerCode = "function isEven(i:Int):Bool { return i % 2 == 0; };"; + function isEven(i:Int):Bool { return i % 2 == 0; } + + assertEq("[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]); + assertEq("[for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]", [for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]); + + headerCode = "function area(a:Int, b:Int):Int { return a * b; };"; + function area(a:Int, b:Int):Int { return a * b; } + + assertEq("[for(i in 0...10) for(j in 0...10) area(i, j)]", [for(i in 0...10) for(j in 0...10) area(i, j)]); + + + headerCode = ""; + + assertEq("[0=>'hello', 1=>'world']", [0=>'hello', 1=>'world']); + assertEq("[0=>'hello', 1=>'world'][1]", [0=>'hello', 1=>'world'][1]); + // Test EOF with preprocessor }