Created
October 31, 2014 14:38
-
-
Save nadako/928b6dfbd9a569b291b7 to your computer and use it in GitHub Desktop.
Structure initialization helper macro
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
(function () { "use strict"; | |
var Main = function() { }; | |
Main.main = function() { | |
var tower = { a : 0, b : "", c : 0, d : false, e : [1,2,3], f : { g : []}}; | |
console.log(tower); | |
}; | |
Main.main(); | |
})(); |
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
import StructHelper.make; | |
typedef A = { | |
a:Int, | |
b:String, | |
c:Float, | |
d:Bool, | |
e:Array<Int>, | |
f:{ | |
g:Array<Bool>, | |
?h:String, | |
}, | |
?i:Int, | |
} | |
class Main { | |
static function main() { | |
var tower = make(A, e = [1, 2, 3]); | |
trace(tower); | |
} | |
} |
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
#if macro | |
import haxe.macro.Expr; | |
import haxe.macro.Context; | |
import haxe.macro.Type; | |
using haxe.macro.Tools; | |
#end | |
@:dce | |
class StructHelper { | |
public static macro function make(typeExpr:Expr, fieldExprs:Array<Expr>):Expr { | |
var type = try | |
Context.getType(typeExpr.toString()) | |
catch (e:Dynamic) | |
throw new Error(e.toString(), typeExpr.pos); | |
switch (type.follow()) { | |
case TAnonymous(_.get() => anon): | |
var fieldInits = new Map(); | |
for (e in fieldExprs) { | |
switch (e) { | |
case macro $i{fieldName} = $value: | |
fieldInits[fieldName] = {expr: value.expr, pos: e.pos}; | |
default: | |
throw new Error("Invalid field initialization expression: should be fieldName = value", e.pos); | |
} | |
} | |
var pos = Context.currentPos(); | |
var objectDecl = getAnonDefaultValueExpr(anon, pos, fieldInits); | |
var objectType = type.toComplexType(); | |
return macro @:pos(pos) ($objectDecl : $objectType); | |
default: | |
throw new Error("Make should only be used on structure types", typeExpr.pos); | |
} | |
} | |
#if macro | |
static function getDefaultValueExpr(type:Type, pos:Position):Expr { | |
var e = switch (type.follow()) { | |
case TAbstract(_.get() => {pack: [], name: "Int" | "Float"}, []): | |
macro 0; | |
case TAbstract(_.get() => {pack: [], name: "Bool"}, []): | |
macro false; | |
case TInst(_.get() => {pack: [], name: "Array"}, [_]): | |
macro []; | |
case TInst(_.get() => {pack: [], name: "String"}, []): | |
macro ""; | |
case TAnonymous(_.get() => anon): | |
getAnonDefaultValueExpr(anon, pos); | |
default: | |
throw new Error("Unsupported type for generating default value: " + type.toString(), pos); | |
} | |
e.pos = pos; | |
return e; | |
} | |
static function getAnonDefaultValueExpr(anon:AnonType, pos:Position, ?fieldInits:Map<String,Expr>):Expr { | |
var declFields = []; | |
for (field in anon.fields) { | |
var fieldValue = null; | |
if (fieldInits != null) { | |
fieldValue = fieldInits[field.name]; | |
fieldInits.remove(field.name); | |
} | |
if (fieldValue == null) { | |
if (field.meta.has(":optional")) | |
continue; | |
fieldValue = getDefaultValueExpr(field.type, pos); | |
} | |
declFields.push({field: field.name, expr: fieldValue}); | |
} | |
if (fieldInits != null) { | |
for (fieldName in fieldInits.keys()) | |
throw new Error("Unknown field: " + fieldName, fieldInits[fieldName].pos); | |
} | |
return {expr: EObjectDecl(declFields), pos: pos}; | |
} | |
#end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment