Переглянути джерело

- added haxe.ds.BalancedTree
- added haxe.ds.EnumValueMap
- added Reflect.isEnumValue

Simon Krajewski 12 роки тому
батько
коміт
00824f9a12

+ 6 - 1
std/Map.hx

@@ -25,6 +25,7 @@ import haxe.ds.IntMap;
 import haxe.ds.HashMap;
 import haxe.ds.ObjectMap;
 import haxe.ds.WeakMap;
+import haxe.ds.EnumValueMap;
 
  /**
 	Map allows key to value mapping for arbitrary value types, and many key
@@ -136,6 +137,10 @@ abstract Map< K, V > (IMap< K, V > ) {
 	@:to static inline function toIntMap(t:IMap < Int, V > ):IntMap<V> {
 		return new IntMap<V>();
 	}
+	
+	@:to static inline function toEnumValueMapMap<K:EnumValue>(t:IMap<K, V>):EnumValueMap<K,V> {
+		return new EnumValueMap<K, V>();
+	}
 
 	@:to static inline function toObjectMap<K:{ }>(t:IMap < K, V >):ObjectMap<K,V> {
 		return new ObjectMap<K, V>();
@@ -151,7 +156,7 @@ abstract Map< K, V > (IMap< K, V > ) {
 
 	@:from static inline function fromObjectMap < K: { }, V > (map:ObjectMap< K, V > ):Map< K, V > {
 		return map;
-	}	
+	}
 }
 
 interface IMap < K, V > {

+ 7 - 2
std/Reflect.hx

@@ -76,7 +76,7 @@ extern class Reflect {
 		If the field is not a property, this function behaves like
 		[Reflect.setField], but might be slower.
 		
-		If [field] is null, the result is unspecified.		
+		If [field] is null, the result is unspecified.
 	**/
 	public static function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void;
 
@@ -117,12 +117,17 @@ extern class Reflect {
 	**/
 	public static function isObject( v : Dynamic ) : Bool;
 
+	/**
+		Tells if [v] is an enum value.
+	**/
+	public static function isEnumValue( v : Dynamic ) : Bool;
+	
 	/**
 		Removes the field named [field] from structure [o].
 		
 		This method is only guaranteed to work on anonymous structures.
 		
-		If [o] or [field] are null, the result is unspecified. 
+		If [o] or [field] are null, the result is unspecified.
 	**/
 	public static function deleteField( o : Dynamic, field : String ) : Bool;
 

+ 7 - 0
std/cpp/_std/Reflect.hx

@@ -79,6 +79,13 @@
 		return t ==  __global__.vtObject || t==__global__.vtClass || t==__global__.vtString ||
 				t==__global__.vtArray;
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return switch(Type.typeof(v)) {
+			case TEnum(_): true;
+			case _: false;
+		}
+	}
 
 	public static function deleteField( o : Dynamic, field : String ) : Bool untyped {
 		if (o==null) return false;

+ 7 - 0
std/cs/_std/Reflect.hx

@@ -213,6 +213,13 @@ import cs.internal.Function;
 	{
 		return false;
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return switch(Type.typeof(v)) {
+			case TEnum(_): true;
+			case _: false;
+		}
+	}
 
 	/**
 		Delete an object field.

+ 4 - 0
std/flash/_std/Reflect.hx

@@ -107,6 +107,10 @@
 		}
 		return (t == "string");
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return try v.__enum__ == true catch ( e : Dynamic) false;
+	}
 
 	public static function deleteField( o : Dynamic, field : String ) : Bool untyped {
 		if( o.hasOwnProperty(field) != true ) return false;

+ 4 - 0
std/flash8/_std/Reflect.hx

@@ -92,6 +92,10 @@
 		var t = __typeof__(v);
 		return (t == "string" || (t == "object" && !v.__enum__) || (t == "function" && v.__name__ != null));
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return v.__enum__;
+	}
 
 	public static function deleteField( o : Dynamic, field : String ) : Bool untyped {
 		if( __this__["hasOwnProperty"]["call"](o,field) != true ) return false;

+ 179 - 0
std/haxe/ds/BalancedTree.hx

@@ -0,0 +1,179 @@
+/*
+ * Copyright (C)2005-2013 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.ds;
+
+class BalancedTree<K,V> {
+	var root:TreeNode<K,V>;
+	
+	public function new() { }
+	
+	public function set(k:K, v:V) {
+		root = setLoop(k, v, root);
+	}
+	
+	public function get(k:K):Null<V> {
+		var node = root;
+		while (node != null) {
+			var c = compare(k, node.key);
+			if (c == 0) return node.value;
+			if (c < 0) node = node.left;
+			else node = node.right;
+		}
+		return null;
+	}
+	
+	public function remove(k:K) {
+		return try {
+			root = removeLoop(k, root);
+			true;
+		}
+		catch (e:String) {
+			false;
+		}
+	}
+	
+	public function exists(k:K) {
+		var node = root;
+		while (node != null) {
+			var c = compare(k, node.key);
+			if (c == 0) return true;
+			else if (c < 0) node = node.left;
+			else node = node.right;
+		}
+		return false;
+	}
+	
+	public function iterator():Iterator<V> {
+		var ret = [];
+		iteratorLoop(root, ret);
+		return ret.iterator();
+	}
+	
+	public function keys():Iterator<K> {
+		var ret = [];
+		keysLoop(root, ret);
+		return ret.iterator();
+	}
+	
+	function setLoop(k:K, v:V, node:TreeNode<K,V>) {
+		if (node == null) return new TreeNode<K,V>(null, k, v, null);
+		var c = compare(k, node.key);
+		return if (c == 0) new TreeNode<K,V>(node.left, k, v, node.right, node.height);
+		else if (c < 0) {
+			var nl = setLoop(k, v, node.left);
+			balance(nl, node.key, node.value, node.right);
+		} else {
+			var nr = setLoop(k, v, node.right);
+			balance(node.left, node.key, node.value, nr);
+		}
+	}
+		
+	function removeLoop(k:K, node:TreeNode<K,V>) {
+		if (node == null) throw "Not_found";
+		var c = compare(k, node.key);
+		return if (c == 0) merge(node.left, node.right);
+		else if (c < 0) balance(removeLoop(k, node.left), node.key, node.value, node.right);
+		else balance(node.left, node.key, node.value, removeLoop(k, node.right));
+	}
+	
+	function iteratorLoop(node:TreeNode<K,V>, acc:Array<V>) {
+		if (node != null) {
+			acc.push(node.value);
+			iteratorLoop(node.left, acc);
+			iteratorLoop(node.right, acc);
+		}
+	}
+	
+	function keysLoop(node:TreeNode<K,V>, acc:Array<K>) {
+		if (node != null) {
+			acc.push(node.key);
+			keysLoop(node.left, acc);
+			keysLoop(node.right, acc);
+		}
+	}
+	
+	function merge(t1, t2) {
+		if (t1 == null) return t2;
+		if (t2 == null) return t1;
+		var t = minBinding(t2);
+		return balance(t1, t.key, t.value, removeMinBinding(t2));
+	}
+	
+	function minBinding(t:TreeNode<K,V>) {
+		return if (t == null) throw "Not_found";
+		else if (t.left == null) t;
+		else minBinding(t.left);
+	}
+	
+	function removeMinBinding(t:TreeNode<K,V>) {
+		return if (t.left == null) t.right;
+		else balance(removeMinBinding(t.left), t.key, t.value, t.right);
+	}
+		
+	function balance(l:TreeNode<K,V>, k:K, v:V, r:TreeNode<K,V>):TreeNode<K,V> {
+		var hl = l.height;
+		var hr = r.height;
+		return if (hl > hr + 2) {
+			if (l.left.height >= l.right.height) new TreeNode<K,V>(l.left, l.key, l.value, new TreeNode<K,V>(l.right, k, v, r));
+			else new TreeNode<K,V>(new TreeNode<K,V>(l.left,l.key, l.value, l.right.left), l.right.key, l.right.value, new TreeNode<K,V>(l.right.right, k, v, r));
+		} else if (hr > hl + 2) {
+			if (r.right.height > r.left.height) new TreeNode<K,V>(new TreeNode<K,V>(l, k, v, r.left), r.key, r.value, r.right);
+			else new TreeNode<K,V>(new TreeNode<K,V>(l, k, v, r.left.left), r.left.key, r.left.value, new TreeNode<K,V>(r.left.right, r.key, r.value, r.right));
+		} else {
+			new TreeNode<K,V>(l, k, v, r, (hl > hr ? hl : hr) + 1);
+		}
+	}
+	
+	function compare(k1:K, k2:K) {
+		return Reflect.compare(k1, k2);
+	}
+	
+	public function toString() {
+		return '{${root.toString()}}';
+	}
+}
+
+class TreeNode<K,V> {
+	public var left : TreeNode<K,V>;
+	public var right : TreeNode<K,V>;
+	public var key : K;
+	public var value : V;
+	public var height(get, null) : Int;
+	
+	public function new(l, k, v, r, h = -1) {
+		left = l;
+		key = k;
+		value = v;
+		right = r;
+		if (h == -1)
+			height = (left.height > right.height ? left.height : right.height) + 1;
+		else
+			height = h;
+	}
+	
+	@:extern public inline function get_height() return this == null ? 0 : height;
+	
+	public function toString() {
+		return (left == null ? "" : left.toString() + ", ") + '$key=$value' + (right == null ? "" : ", " +right.toString());
+	}
+}

+ 47 - 0
std/haxe/ds/EnumValueMap.hx

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C)2005-2013 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.ds;
+
+class EnumValueMap<K:EnumValue, V> extends haxe.ds.BalancedTree<K, V> implements Map.IMap<K,V> {
+	
+	override function compare(k1:EnumValue, k2:EnumValue) {
+		var d = k1.getIndex() - k2.getIndex();
+		if (d != 0) return d;
+		var p1 = k1.getParameters();
+		var p2 = k2.getParameters();
+		if (p1.length == 0 && p2.length == 0) return 0;
+		return compareArgs(p1, p2);
+	}
+	
+	function compareArgs(a1:Array<Dynamic>, a2:Array<Dynamic>) {
+		var ld = a1.length - a2.length;
+		if (ld != 0) return ld;
+		for (i in 0...a1.length) {
+			var v1:Dynamic = a1[i], v2:Dynamic = a2[i];
+			var d = if (Reflect.isEnumValue(v1) && Reflect.isEnumValue(v2)) compare(v1, v2);
+			else Reflect.compare(v1, v2);
+			if (d != 0) return d;
+		}
+		return 0;
+	}
+}

+ 7 - 0
std/java/_std/Reflect.hx

@@ -215,6 +215,13 @@ import java.Boot;
 	{
 		return false;
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return switch(Type.typeof(v)) {
+			case TEnum(_): true;
+			case _: false;
+		}
+	}
 
 	/**
 		Delete an object field.

+ 4 - 0
std/js/_std/Reflect.hx

@@ -85,6 +85,10 @@
 		var t = __js__("typeof(v)");
 		return (t == "string" || (t == "object" && !v.__enum__) || (t == "function" && (js.Boot.isClass(v) || js.Boot.isEnum(v))));
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return v != null && v.__enum__;
+	}
 
 	public static function deleteField( o : Dynamic, field : String ) : Bool untyped {
 		if( !hasField(o,field) ) return false;

+ 4 - 0
std/neko/_std/Reflect.hx

@@ -85,6 +85,10 @@
 	public static function isObject( v : Dynamic ) : Bool untyped {
 		return $typeof(v) == $tobject && v.__enum__ == null;
 	}
+	
+	public static function isEnumValue( v : Dynamic ) : Bool untyped {
+		return $typeof(v) == $tobject && v.__enum__ != null;
+	}
 
 	public inline static function deleteField( o : Dynamic, field : String ) : Bool untyped {
 		return $objremove(o,$fasthash(field.__s));

+ 8 - 1
std/php/_std/Reflect.hx

@@ -92,7 +92,14 @@
 			return untyped __php__("$v instanceof _hx_anonymous") || Type.getClass(v) != null;
 		return untyped __php__("is_string($v) && !_hx_is_lambda($v)");
 	}
-
+	
+	public static function isEnumValue( v : Dynamic ) : Bool {
+		return switch(Type.typeof(v)) {
+			case TEnum(_): true;
+			case _: false;
+		}
+	}
+		
 	public static function deleteField( o : Dynamic, field : String ) : Bool {
 		if(!hasField(o,field)) return false;
 		untyped __php__("if(isset($o->__dynamics[$field])) unset($o->__dynamics[$field]); else if($o instanceof _hx_anonymous) unset($o->$f); else $o->$f = null");

+ 11 - 1
tests/unit/TestSpecification.hx

@@ -1,5 +1,7 @@
 package unit;
 
+import haxe.macro.Expr;
+
 typedef T = {
 	function func():Void;
 	var v:String;
@@ -40,7 +42,7 @@ typedef T = {
 	
 	public function set_propAcc(v) {
 		return this.propAcc = v.toUpperCase();
-	}	
+	}
 }
 
 class CChild extends C { }
@@ -109,6 +111,14 @@ enum EnumFlagTest {
 	EC;
 }
 
+enum EVMTest {
+	EVMA;
+	EVMB(?s:String);
+	EVMC(s:String, ?i:Int);
+	EVMD(n:EVMTest);
+	EVME(?n:EVMTest);
+}
+
 #if !macro
 @:build(unit.UnitBuilder.build("unitstd"))
 #end

+ 1 - 1
tests/unit/compile.hxml

@@ -61,7 +61,7 @@ unit.Test
 --next
 -main unit.Test
 -as3 as3
--cmd mxmlc -static-link-runtime-shared-libraries=true -debug as3/__main__.as --output unit9_as3.swf
+#-cmd mxmlc -static-link-runtime-shared-libraries=true -debug as3/__main__.as --output unit9_as3.swf
 
 #cpp
 --next

+ 1 - 1
tests/unit/unit.hxproj

@@ -67,7 +67,7 @@
   <options>
     <option showHiddenPaths="False" />
     <option testMovie="OpenDocument" />
-    <option testMovieCommand="http://dev.unit-tests/unit.html" />
+    <option testMovieCommand="" />
   </options>
   <!-- Plugin storage -->
   <storage />

+ 30 - 0
tests/unit/unitstd/haxe/ds/BalancedTree.unit.hx

@@ -0,0 +1,30 @@
+var test = [
+	13 => 1,
+	8 => 2,
+	17 => 3,
+	1 => 4,
+	11 => 5,
+	15 => 6,
+	25 => 7,
+	6 => 8,
+	22 => 9,
+	27 => 10
+];
+var otherKeys = [for (i in 0...30) if (!test.exists(i)) i];
+var m = new haxe.ds.BalancedTree<Int, Int>();
+for (k in test.keys()) {
+	m.set(k, test[k]);
+}
+for (k in test.keys()) {
+	eq(test[k], m.get(k));
+}
+for (k in test.keys()) {
+	eq(true, m.exists(k));
+}
+for (k in otherKeys) {
+	eq(false, m.exists(k));
+}
+for (k in test.keys()) {
+	eq(true, m.remove(k));
+	eq(false, m.exists(k));
+}

+ 92 - 0
tests/unit/unitstd/haxe/ds/EnumValueMap.unit.hx

@@ -0,0 +1,92 @@
+var em = new haxe.ds.EnumValueMap();
+var test = [
+	1 => EContinue,
+	2 => EBreak,
+	3 => EConst(CString("bar")),
+	4 => EConst(CString("foo")),
+	5 => EArray(null, null),
+];
+for (k in test.keys()) {
+	em.set(test[k],k);
+}
+for (k in test.keys()) {
+	eq(k, em.get(test[k]));
+}
+for (k in test.keys()) {
+	eq(true, em.exists(test[k]));
+}
+for (k in test.keys()) {
+	eq(true, em.remove(test[k]));
+}
+for (k in test.keys()) {
+	eq(false, em.exists(test[k]));
+}
+
+var em = [
+	EConst(CIdent("test")) => "test",
+	EArray(null,null) => "bar",
+	EBreak => "baz"
+];
+em.exists(EConst(CIdent("test"))) == true;
+em.exists(EConst(CIdent("test2"))) == false;
+em.get(EConst(CIdent("test"))) == "test";
+em.remove(EConst(CIdent("test"))) == true;
+em.exists(EConst(CIdent("test"))) == false;
+em.get(EConst(CIdent("test"))) == null;
+
+em.exists(EArray(null, null)) == true;
+em.get(EArray(null, null)) == "bar";
+em.remove(EArray(null, null)) == true;
+em.exists(EArray(null, null)) == false;
+em.get(EArray(null, null)) == null;
+
+em.exists(EBreak) == true;
+em.get(EBreak) == "baz";
+em.remove(EBreak) == true;
+em.exists(EBreak) == false;
+em.get(EBreak) == null;
+
+var evm = new haxe.ds.EnumValueMap();
+evm.set(EVMA,1);
+evm.set(EVMA,2);
+evm.exists(EVMA) == true;
+evm.get(EVMA) == 2;
+evm.remove(EVMA) == true;
+evm.exists(EVMA) == false;
+
+evm.set(EVMB(), 8);
+evm.set(EVMB(), 9);
+evm.set(EVMB(null), 10);
+evm.exists(EVMB()) == true;
+evm.exists(EVMB(null)) == true;
+evm.get(EVMB()) == 10;
+evm.get(EVMB(null)) == 10;
+evm.remove(EVMB()) == true;
+evm.remove(EVMB()) == false;
+evm.exists(EVMB()) == false;
+evm.exists(EVMB(null)) == false;
+
+evm.set(EVMC("foo"), 4);
+evm.set(EVMC("foo"), 5);
+evm.exists(EVMC("foo")) == true;
+evm.get(EVMC("foo")) == 5;
+evm.remove(EVMC("foo")) == true;
+evm.exists(EVMC("foo")) == false;
+
+evm.set(EVMD(null),91);
+evm.exists(EVMD(null)) == true;
+evm.get(EVMD(null)) == 91;
+evm.remove(EVMD(null)) == true;
+evm.exists(EVMD(null)) == false;
+
+evm.set(EVMD(EVMA), 12);
+evm.exists(EVMD(EVMA)) == true;
+evm.get(EVMD(EVMA)) == 12;
+evm.remove(EVMD(EVMA)) == true;
+evm.exists(EVMD(EVMA)) == false;
+
+evm.set(EVME(null),99);
+evm.exists(EVME(null)) == true;
+evm.exists(EVME()) == true;
+evm.get(EVME(null)) == 99;
+evm.get(EVME()) == 99;