diff --git a/Test.hx b/Test.hx index 692de16..b443d06 100644 --- a/Test.hx +++ b/Test.hx @@ -1,3 +1,5 @@ +import haxe.ds.EnumValueMap; +import haxe.ds.Option; import hscript.Macro; import haxe.unit.*; @@ -94,6 +96,55 @@ class Test extends TestCase { assertScript("/**/0", 0); assertScript("x=1;x*=-2", -2); } + + function testMap():Void { + var objKey = { ok:true }; + var vars = { + stringMap: ["foo" => "Foo", "bar" => "Bar"], + intMap:[100 => "one hundred"], + objKey: objKey, + objMap:[objKey => "ok"], + enumKey:Option.Some("some"), + enumMap:new EnumValueMap, String>(), + stringIntMap: ["foo" => 100] + } + vars.enumMap.set(vars.enumKey, "ok"); + + assertScript('stringMap["foo"]', "Foo", vars); + assertScript('intMap[100]', "one hundred", vars); + assertScript('objMap[objKey]', "ok", vars); + assertScript('enumMap[enumKey]', "ok", vars); + assertScript('stringMap["a"] = "A"; stringMap["a"]', "A", vars); + assertScript('intMap[200] = objMap[{foo:false}] = enumMap[enumKey] = "A"', "A", vars); + assertEquals('A', vars.intMap[200]); + assertEquals('A', vars.enumMap.get(vars.enumKey)); + for (key in vars.objMap.keys()) { + if (key != objKey) { + assertEquals(false, (key:Dynamic).foo); + assertEquals('A', vars.objMap[key]); + } + } + + #if (!php || (haxe_ver >= 3.3)) + assertScript(' + var keys = []; + for (key in stringMap.keys()) keys.push(key); + keys.sort(function(s1, s2) return s1 > s2 ? 1 : (s2 > s1 ? -1 : 0)); + keys.join("_"); + ', 'a_bar_foo', vars); + #end + assertScript('stringMap.remove("foo"); stringMap.exists("foo");', false, vars); + assertScript('stringMap["foo"] = "a"; stringMap["foo"] += "b"', 'ab', vars); + assertEquals('ab', vars.stringMap['foo']); + assertScript('stringIntMap["foo"]++', 100, vars); + assertEquals(101, vars.stringIntMap['foo']); + assertScript('++stringIntMap["foo"]', 102, vars); + assertScript('var newMap = ["foo"=>"foo"]; newMap["foo"];', 'foo', vars); + #if (!php || (haxe_ver >= 3.3)) + assertScript('var newMap = [enumKey=>"foo"]; newMap[enumKey];', 'foo', vars); + #end + assertScript('var newMap = [{a:"a"}=>"foo", objKey=>"bar"]; newMap[objKey];', 'bar', vars); + } static function main() { var runner = new TestRunner(); diff --git a/hscript/Interp.hx b/hscript/Interp.hx index f61521d..62cea31 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -119,8 +119,16 @@ class Interp { l.r = v; case EField(e,f): v = set(expr(e),f,v); - case EArray(e,index): - expr(e)[expr(index)] = v; + case EArray(e, index): + var arr:Dynamic = expr(e); + var index:Dynamic = expr(index); + if (isMap(arr)) { + setMapValue(arr, index, v); + } + else { + arr[index] = v; + } + default: error(EInvalidOp("=")); } @@ -146,11 +154,17 @@ class Interp { var obj = expr(e); v = fop(get(obj,f),expr(e2)); v = set(obj,f,v); - case EArray(e,index): - var arr = expr(e); - var index = expr(index); - v = fop(arr[index],expr(e2)); - arr[index] = v; + case EArray(e, index): + var arr:Dynamic = expr(e); + var index:Dynamic = expr(index); + if (isMap(arr)) { + v = fop(getMapValue(arr, index), expr(e2)); + setMapValue(arr, index, v); + } + else { + v = fop(arr[index],expr(e2)); + arr[index] = v; + } default: return error(EInvalidOp(op)); } @@ -181,16 +195,29 @@ class Interp { } else set(obj,f,v + delta); return v; - case EArray(e,index): - var arr = expr(e); - var index = expr(index); - var v = arr[index]; - if( prefix ) { - v += delta; - arr[index] = v; - } else - arr[index] = v + delta; - return v; + case EArray(e, index): + var arr:Dynamic = expr(e); + var index:Dynamic = expr(index); + if (isMap(arr)) { + var v = getMapValue(arr, index); + if (prefix) { + v += delta; + setMapValue(arr, index, v); + } + else { + setMapValue(arr, index, v + delta); + } + return v; + } + else { + var v = arr[index]; + if( prefix ) { + v += delta; + arr[index] = v; + } else + arr[index] = v + delta; + return v; + } default: return error(EInvalidOp((delta > 0)?"++":"--")); } @@ -417,12 +444,56 @@ class Interp { } return f; case EArrayDecl(arr): - var a = new Array(); - for( e in arr ) - a.push(expr(e)); - return a; - case EArray(e,index): - return expr(e)[expr(index)]; + if (arr.length > 0 && arr[0].match(Expr.EBinop("=>", _))) { + 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(e) { + case Expr.EBinop("=>", eKey, eValue): { + var key:Dynamic = expr(eKey); + var value:Dynamic = expr(eValue); + isAllString = isAllString && Std.is(key, String); + isAllInt = isAllInt && Std.is(key, Int); + isAllObject = isAllObject && Reflect.isObject(key); + isAllEnum = isAllEnum && Reflect.isEnumValue(key); + keys.push(key); + values.push(value); + } + default: throw("=> expected"); + } + } + var map:Dynamic = { + 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) { + setMapValue(map, keys[n], values[n]); + } + return map; + } + else { + var a = new Array(); + for ( e in arr ) { + a.push(expr(e)); + } + return a; + } + case EArray(e, index): + var arr:Dynamic = expr(e); + var index:Dynamic = expr(index); + if (isMap(arr)) { + return getMapValue(arr, index); + } + else { + return arr[index]; + } case ENew(cl,params): var a = new Array(); for( e in params ) @@ -498,8 +569,8 @@ class Interp { } function makeIterator( v : Dynamic ) : Iterator { - #if (flash && !flash9) - if( v.iterator != null ) v = v.iterator(); + #if ((flash && !flash9) || php) + if ( v.iterator != null ) v = v.iterator(); #else try v = v.iterator() catch( e : Dynamic ) {}; #end @@ -526,18 +597,32 @@ class Interp { restore(old); } + inline function isMap(o:Dynamic):Bool { + return Std.is(o, haxe.Constraints.IMap); + } + + inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic { + return cast(map, haxe.Constraints.IMap).get(key); + } + + inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void { + cast(map, haxe.Constraints.IMap).set(key, value); + } + function get( o : Dynamic, f : String ) : Dynamic { - if( o == null ) error(EInvalidAccess(f)); - #if php - // https://github.com/HaxeFoundation/haxe/issues/4915 - return try { + if ( o == null ) error(EInvalidAccess(f)); + return { + #if php + // https://github.com/HaxeFoundation/haxe/issues/4915 + try { + Reflect.getProperty(o, f); + } catch (e:Dynamic) { + Reflect.field(o, f); + } + #else Reflect.getProperty(o, f); - } catch (e:Dynamic) { - Reflect.field(o, f); - } - #else - return Reflect.getProperty(o, f); - #end + #end + } } function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic { diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 33f0c7e..df38cbd 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -111,7 +111,7 @@ class Parser { ["..."], ["&&"], ["||"], - ["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^="], + ["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>"], ]; #if haxe3 opPriority = new Map(); @@ -1062,6 +1062,8 @@ class Parser { char = readChar(); if( char == '='.code ) return TOp("=="); + else if ( char == '>'.code ) + return TOp("=>"); this.char = char; return TOp("="); default: