123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package h3d.impl;
- #if macro
- import haxe.macro.Expr;
- using haxe.macro.ExprTools;
- #end
- @:autoBuild(h3d.impl.VarBinding.Macros.build())
- interface VarBinding {
- }
- #if macro
- class Init {
- public var path : Array<String>;
- public var expr : Expr;
- public var dependsOn : Array<Init>;
- public var dependedBy : Array<Init>;
- public var mark : Bool;
- public var isInit : Bool;
- public var parent : Init;
- public function new(path,expr, isInit=false) {
- this.path = path;
- this.expr = expr;
- this.isInit = isInit;
- this.dependsOn = [];
- this.dependedBy = [];
- }
- }
- class Macros {
- static function hasIdent( e ) {
- var has = false;
- function loop(e : Expr) {
- switch( e.expr ) {
- case EConst(CIdent(i)):
- has = true;
- default:
- }
- if( !has )
- e.iter(loop);
- }
- loop(e);
- return has;
- }
- public static function build() {
- var fields = haxe.macro.Context.getBuildFields();
- var inits = [];
- function initExpr( path : Array<String>, e : Expr, isInit ) : Expr {
- return switch( e.expr ) {
- case EObjectDecl(fields):
- if( !isInit )
- inits.push(new Init(path.copy(), null));
- for( f in fields ) {
- path.push(f.field);
- f.expr = initExpr(path, f.expr, isInit);
- path.pop();
- }
- e;
- case EBinop(OpArrow, e1, e2):
- if( isInit )
- haxe.macro.Context.error("Can't chain initializers", e.pos);
- if( !e2.expr.match(EObjectDecl(_)) )
- haxe.macro.Context.error("Object declaration required", e2.pos);
- initExpr(path, e2, true); // ignore returned expr : all inits has been pushed in path
- initExpr(path, e1, isInit);
- default:
- if( isInit || hasIdent(e) ) {
- inits.push(new Init(path.copy(), e, isInit));
- macro if( false ) $e else cast null; // type inference + delayed
- } else {
- inits.push(new Init(path.copy(), null));
- e;
- }
- }
- }
- var constructor = null;
- for( f in fields.copy() )
- switch( f.kind ) {
- case FVar(t, e) if( e != null ):
- var e = initExpr([f.name], e, false);
- f.kind = FVar(t, e);
- case FFun(m) if( f.name == "new" ):
- constructor = m;
- default:
- }
- if( constructor == null )
- haxe.macro.Context.error("Requires a declared constructor", haxe.macro.Context.currentPos());
- var initsMap = new Map();
- for( i in inits )
- initsMap.set(i.path.join("."), i);
- for( i in inits ) {
- if( i.path.length > 1 ) {
- var p = i.path.copy();
- while( p.length > 0 ) {
- p.pop();
- i.parent = initsMap.get(p.join("."));
- if( i.parent != null ) break;
- }
- }
- if( i.expr != null ) {
- function addDep( path : Array<String> ) {
- while( path.length > 0 ) {
- var idep = initsMap.get(path.join("."));
- if( idep != null ) {
- if( i.dependsOn.indexOf(idep) < 0 ) {
- i.dependsOn.push(idep);
- idep.dependedBy.push(i);
- }
- return;
- }
- path.pop();
- }
- }
- function browseExpr( path : Array<String>, e : Expr ) {
- switch( e.expr ) {
- case EField(eobj, field):
- path.unshift(field);
- browseExpr(path, eobj);
- path.shift();
- case EConst(CIdent(id)) if( initsMap.exists(id) ):
- path.unshift(id);
- addDep(path.copy());
- path.shift();
- default:
- e.iter(browseExpr.bind([]));
- }
- }
- browseExpr([], i.expr);
- }
- }
- var order = [];
- function makeAssign( path : Array<String>, expr : Expr ) : Expr {
- path = path.copy();
- var epath : Expr = { expr : EConst(CIdent(path.shift())), pos : expr.pos };
- while( path.length > 0 ) {
- var f = path.shift();
- var index;
- if( f.charCodeAt(0) == "_".code && (index = Std.parseInt(f.substr(1))) != null )
- epath = { expr : EArray(epath, { expr : EConst(CInt(""+index)), pos : expr.pos }), pos : expr.pos };
- else
- epath = { expr : EField(epath, f), pos : expr.pos };
- }
- return { expr : EBinop(OpAssign, epath, expr), pos : expr.pos };
- }
- function markRec( i : Init ) {
- if( i.mark )
- return;
- i.mark = true;
- if( i.parent != null )
- markRec(i.parent);
- for( d in i.dependsOn )
- markRec(d);
- if( i.expr != null )
- order.push(makeAssign(i.path,i.expr));
- }
- for( i in inits )
- markRec(i);
- switch( constructor.expr.expr ) {
- case EBlock(el):
- constructor.expr = { expr : EBlock(order.concat(el)), pos : constructor.expr.pos };
- default:
- constructor.expr = { expr : EBlock(order.concat([constructor.expr])), pos : constructor.expr.pos };
- }
- return fields;
- }
- }
- #end
|