EMapDecl + Map Comprehension

This commit is contained in:
NeeEoo
2024-04-25 04:14:41 +02:00
parent c0d4540d4b
commit b6df96a4b0
9 changed files with 432 additions and 127 deletions
+10 -1
View File
@@ -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<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
EMapDecl( type: MapType, keys: Array<Expr>, values: Array<Expr> );
EArrayDecl( e : Array<Expr> );
ENew( cl : String, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
+45 -66
View File
@@ -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<Dynamic> = [];
var values:Array<Dynamic> = [];
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<Dynamic> = [];
var values:Array<Dynamic> = [];
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<Dynamic, Dynamic> = {
if (isAllInt)
new haxe.ds.IntMap<Dynamic>();
else if (isAllString)
new haxe.ds.StringMap<Dynamic>();
else if (isAllEnum)
new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
else if (isAllObject)
new haxe.ds.ObjectMap<Dynamic, Dynamic>();
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<Dynamic, Dynamic> = switch(type) {
case IntMap: new haxe.ds.IntMap<Dynamic>();
case StringMap: new haxe.ds.StringMap<Dynamic>();
case EnumMap: new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
case ObjectMap: new haxe.ds.ObjectMap<Dynamic, Dynamic>();
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;
}
+6
View File
@@ -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):
+29 -10
View File
@@ -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);
+160 -24
View File
@@ -115,8 +115,6 @@ class Parser {
var startIdents : Array<Bool>;
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<Expr> = [];
var values:Array<Expr> = [];
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<Argument>, 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 ) {
+31 -3
View File
@@ -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 ) {
+2
View File
@@ -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));