-
-
Save Dr-Emann/4472084 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ; | |
/** | |
* ... | |
* @author Zachary Dremann | |
*/ | |
#if macro | |
import haxe.macro.Expr; | |
import haxe.macro.Context; | |
import haxe.macro.Type; | |
#end | |
class MacroCollection | |
{ | |
private var storage:IntHash<Dynamic>; | |
public function new() | |
{ | |
storage = new IntHash(); | |
} | |
@:macro public function add(eThis:Expr, component:Expr) | |
{ | |
// Use untyped to get around addComponentWithID being private, but being called from outside | |
var id = MyMacro.getComponentID(component); | |
return macro (untyped $eThis.addComponentWithID)($component, $id); | |
} | |
@:keep | |
function addComponentWithID(component:Dynamic, id:Int):Int | |
{ | |
trace(id + " => " + component); | |
storage.set(id, component); | |
return id; | |
} | |
public function get<T>(id:Int):T | |
{ | |
return this.storage.get(id); | |
} | |
} | |
class MyMacro | |
{ | |
#if macro | |
static var nextId:Int; | |
static var components:Hash<Int>; | |
#end | |
#if macro | |
public static function getComponentID(component:Expr):Expr | |
{ | |
if (components == null) | |
{ | |
nextId = 0; | |
components = new Hash<Int>(); | |
} | |
var type:Type = Context.follow(Context.typeof(component), false); | |
var typeName = getTypeName(type); | |
var index = 0; | |
if (components.exists(typeName)) | |
index = components.get(typeName); | |
else | |
{ | |
index = nextId++; | |
components.set(typeName, index); | |
} | |
return Context.makeExpr(index, component.pos); | |
} | |
public static function getTypeName(type:Type):String | |
{ | |
var str = new StringBuf(); | |
type = Context.follow(type, false); | |
switch(type) | |
{ | |
case TInst(classRef, params): | |
var typeDef = classRef.get(); | |
var nameArr = typeDef.pack.copy(); | |
nameArr.push(typeDef.name); | |
str.add(nameArr.join(".")); | |
if (params.length > 0) | |
{ | |
str.addChar("<".code); | |
var pStrArr = Lambda.map(params, getTypeName); | |
str.add(pStrArr.join(",")); | |
str.addChar(">".code); | |
} | |
case TEnum(enumRef, params): | |
var typeDef = enumRef.get(); | |
var nameArr = typeDef.pack.copy(); | |
nameArr.push(typeDef.name); | |
str.add(nameArr.join(".")); | |
if (params.length > 0) | |
{ | |
str.addChar("<".code); | |
var pStrArr = Lambda.map(params, getTypeName); | |
str.add(pStrArr.join(",")); | |
str.addChar(">".code); | |
} | |
case TAnonymous(fieldsRef): | |
var fields:Array<ClassField> = fieldsRef.get().fields; | |
str.addChar("{".code); | |
var fieldStrArr = Lambda.map(fields, function(field) { return field.name + ":" + getTypeName(field.type); } ); | |
str.add(fieldStrArr.join(",")); | |
str.addChar("}".code); | |
case TFun(args, ret): | |
var argsList = Lambda.map(args, function(arg) { return arg.t; } ); | |
argsList.add(ret); | |
var argsStrList = Lambda.map(argsList, getTypeName); | |
if (argsStrList.length == 1) | |
argsStrList.push("Void"); | |
str.add(argsStrList.join("->")); | |
case TLazy(f): | |
str.add(getTypeName(f())); | |
case TMono(ref): | |
var val = ref.get(); | |
if (val == null) | |
{ | |
Context.error("Untyped value. Cannot use, try storing in a manually typed variable", Context.currentPos()); | |
} | |
else | |
str.add(getTypeName(val)); | |
case TDynamic(t): | |
Context.error("Cannot store dynamic values", Context.currentPos()); | |
case TType(defRef, params): | |
Context.error("Something went wrong, typedefs should be followed", Context.currentPos()); | |
} | |
Context.warning(str.toString(), Context.currentPos()); | |
return str.toString(); | |
} | |
#end | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package; | |
import massive.munit.Assert; | |
#if macro | |
import haxe.macro.Expr; | |
import haxe.macro.Context; | |
import haxe.macro.Type; | |
#end | |
typedef MyArray<T> = Array<T>; | |
typedef MyList = List<String>; | |
class MacroTest | |
{ | |
private var collection:MacroCollection; | |
@Before | |
public function setUp() | |
{ | |
collection = new MacroCollection(); | |
} | |
@Test | |
public function twoInstancesOfTheSameTypeShareACommonID() | |
{ | |
var first = collection.add(new MacroExampleA()); | |
var second = collection.add(new MacroExampleA()); | |
Assert.areEqual(first, second); | |
} | |
@Test | |
public function differentTypesHaveDifferentIDs() | |
{ | |
var first = collection.add(new MacroExampleA()); | |
var second = collection.add(new MacroExampleB()); | |
Assert.areNotEqual(first, second); | |
} | |
@Test | |
public function stringsAlsoShareACommonID() | |
{ | |
var first = collection.add("hello"); | |
var second = collection.add("other"); | |
Assert.areEqual(first, second); | |
} | |
@Test | |
public function boolsShareCommonId() | |
{ | |
var first = collection.add(true); | |
var second = collection.add(false); | |
Assert.areEqual(first, second); | |
} | |
@Test | |
public function twoInstancesWithDifferentParamsOfSameTypeHaveDifferentIDs() | |
{ | |
var first = collection.add(new Array<String>()); | |
var second = collection.add(new Array<Int>()); | |
Assert.areNotEqual(first, second); | |
} | |
@Test | |
public function testAnnonStructures() | |
{ | |
var first = collection.add({name:"bob", age:31}); | |
var second = collection.add({name:"sue", age:15}); | |
Assert.areEqual(first, second); | |
second = collection.add( { name:"joe" } ); | |
Assert.areNotEqual(first, second); | |
} | |
@Test | |
public function testFunctions() | |
{ | |
var f1 = function(a:String) { return 1; }; | |
var f2 = function(a:String) { return 2; }; | |
var first = collection.add(f1); | |
var second = collection.add(f2); | |
Assert.areEqual(first, second); | |
second = collection.add(testAnnonStructures); | |
Assert.areNotEqual(first, second); | |
} | |
@Test | |
public function typedefsAreFollowed() | |
{ | |
var first = collection.add(new MyArray<Float>()); | |
var second = collection.add(new Array<Float>()); | |
Assert.areEqual(first, second); | |
first = collection.add(new List<String>()); | |
second = collection.add(new MyList()); | |
Assert.areEqual(first, second); | |
second = collection.add(new MyArray<MyList>()); | |
Assert.areNotEqual(first, second); | |
} | |
@Test | |
public function actuallyStoreValue() | |
{ | |
var id1 = collection.add("Hello"); | |
var id2 = collection.add([1,2,3,4]); | |
var str:String = collection.get(id1); | |
var arr:Array<Int> = collection.get(id2); | |
Assert.areEqual("Hello", str); | |
Assert.areEqual(4, arr.length); | |
collection.add("Other String"); | |
str = collection.get(id1); | |
Assert.areEqual("Other String", str); | |
} | |
} | |
class MacroExampleA | |
{ | |
public function new() {} | |
} | |
class MacroExampleB | |
{ | |
public function new() {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment