Created
June 9, 2014 19:45
-
-
Save nadako/b70da59d3c68a66ef5ee to your computer and use it in GitHub Desktop.
Runtime type check generator.
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.Context; | |
import haxe.macro.Expr; | |
using haxe.macro.Tools; | |
#end | |
@:enum abstract MyEnum<T>(T) {} | |
typedef A = MyEnum<Float>; | |
typedef B = Array<{a:Int, ?b:Array<{?a:String}>}>; | |
typedef C = Dynamic<Int>; | |
class Main | |
{ | |
static function main() | |
{ | |
var a:Dynamic = 1; | |
check(a, Int); | |
check(a, Float); | |
check(a, Bool); | |
check(a, A); | |
check(a, String); | |
check(a, B); | |
check(a, C); | |
// check(a, Dynamic); | |
} | |
static macro function check(eo, et) | |
{ | |
var type = Context.getType(et.toString()); | |
return generateCheck(eo, type, et.pos, macro "args"); | |
} | |
#if macro | |
static function generateCheck(e:Expr, type:haxe.macro.Type, pos:Position, errorPrefix:Expr):Expr | |
{ | |
switch (type.follow()) | |
{ | |
case TAbstract(_.get() => ab, params): | |
switch [ab, params] | |
{ | |
case [{pack: [], name: "Int"}, []]: | |
return macro if (!Std.is($e, Int)) throw $errorPrefix + ': not an integer'; | |
case [{pack: [], name: "Float"}, []]: | |
return macro if (!Std.is($e, Float)) throw $errorPrefix + ': not a float'; | |
case [{pack: [], name: "Bool"}, []]: | |
return macro if (!Std.is($e, Bool)) throw $errorPrefix + ': not a boolean'; | |
default: | |
var type = ab.type.applyTypeParameters(ab.params, params); | |
return generateCheck(e, type, pos, errorPrefix); | |
} | |
case TInst(_.get() => cl, params): | |
switch [cl, params] | |
{ | |
case [{pack: [], name: "String"}, []]: | |
return macro if (!Std.is($e, String)) throw $errorPrefix + ': not a string'; | |
case [{pack: [], name: "Array"}, [elemType]]: | |
var ct = type.toComplexType(); | |
return macro { | |
if (!Std.is($e, Array)) throw $errorPrefix + ': not an array'; | |
var i = 0; | |
for (v in ($e : $ct)) | |
{ | |
${generateCheck(macro v, elemType, pos, macro $errorPrefix + "[" + i + "]")}; | |
} | |
} | |
default: | |
} | |
case TAnonymous(_.get().fields => fields): | |
var checks = []; | |
for (field in fields) | |
{ | |
var fieldName = field.name; | |
var optional = field.meta.has(":optional"); | |
var check = generateCheck(macro $e.$fieldName, field.type, field.pos, macro $errorPrefix + "." + $v{fieldName}); | |
var hasField = macro Reflect.hasField($e, $v{fieldName}); | |
if (!optional) | |
{ | |
checks.push(macro if (!$hasField) throw $v{"missing field " + fieldName}); | |
checks.push(check); | |
} | |
else | |
{ | |
switch (check.expr) | |
{ | |
case EIf(econd, error, null): | |
checks.push(macro if ($hasField && $econd) $error); | |
default: | |
checks.push(macro if ($hasField) $check); | |
} | |
} | |
} | |
return macro $b{checks}; | |
case TDynamic(null): | |
return macro {}; | |
case TDynamic(t): | |
return macro { | |
var tmp = $e; | |
for (field in Reflect.fields(tmp)) | |
{ | |
var value = Reflect.field(tmp, field); | |
${generateCheck(macro value, t, pos, macro $errorPrefix + "." + field)} | |
} | |
} | |
default: | |
} | |
throw new Error("Unsupported type " + type.toString(), pos); | |
} | |
#end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment