VarBinding.hx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package h3d.impl;
  2. #if macro
  3. import haxe.macro.Expr;
  4. using haxe.macro.ExprTools;
  5. #end
  6. @:autoBuild(h3d.impl.VarBinding.Macros.build())
  7. interface VarBinding {
  8. }
  9. #if macro
  10. class Init {
  11. public var path : Array<String>;
  12. public var expr : Expr;
  13. public var dependsOn : Array<Init>;
  14. public var dependedBy : Array<Init>;
  15. public var mark : Bool;
  16. public var isInit : Bool;
  17. public var parent : Init;
  18. public function new(path,expr, isInit=false) {
  19. this.path = path;
  20. this.expr = expr;
  21. this.isInit = isInit;
  22. this.dependsOn = [];
  23. this.dependedBy = [];
  24. }
  25. }
  26. class Macros {
  27. static function hasIdent( e ) {
  28. var has = false;
  29. function loop(e : Expr) {
  30. switch( e.expr ) {
  31. case EConst(CIdent(i)):
  32. has = true;
  33. default:
  34. }
  35. if( !has )
  36. e.iter(loop);
  37. }
  38. loop(e);
  39. return has;
  40. }
  41. public static function build() {
  42. var fields = haxe.macro.Context.getBuildFields();
  43. var inits = [];
  44. function initExpr( path : Array<String>, e : Expr, isInit ) : Expr {
  45. return switch( e.expr ) {
  46. case EObjectDecl(fields):
  47. if( !isInit )
  48. inits.push(new Init(path.copy(), null));
  49. for( f in fields ) {
  50. path.push(f.field);
  51. f.expr = initExpr(path, f.expr, isInit);
  52. path.pop();
  53. }
  54. e;
  55. case EBinop(OpArrow, e1, e2):
  56. if( isInit )
  57. haxe.macro.Context.error("Can't chain initializers", e.pos);
  58. if( !e2.expr.match(EObjectDecl(_)) )
  59. haxe.macro.Context.error("Object declaration required", e2.pos);
  60. initExpr(path, e2, true); // ignore returned expr : all inits has been pushed in path
  61. initExpr(path, e1, isInit);
  62. default:
  63. if( isInit || hasIdent(e) ) {
  64. inits.push(new Init(path.copy(), e, isInit));
  65. macro if( false ) $e else cast null; // type inference + delayed
  66. } else {
  67. inits.push(new Init(path.copy(), null));
  68. e;
  69. }
  70. }
  71. }
  72. var constructor = null;
  73. for( f in fields.copy() )
  74. switch( f.kind ) {
  75. case FVar(t, e) if( e != null ):
  76. var e = initExpr([f.name], e, false);
  77. f.kind = FVar(t, e);
  78. case FFun(m) if( f.name == "new" ):
  79. constructor = m;
  80. default:
  81. }
  82. if( constructor == null )
  83. haxe.macro.Context.error("Requires a declared constructor", haxe.macro.Context.currentPos());
  84. var initsMap = new Map();
  85. for( i in inits )
  86. initsMap.set(i.path.join("."), i);
  87. for( i in inits ) {
  88. if( i.path.length > 1 ) {
  89. var p = i.path.copy();
  90. while( p.length > 0 ) {
  91. p.pop();
  92. i.parent = initsMap.get(p.join("."));
  93. if( i.parent != null ) break;
  94. }
  95. }
  96. if( i.expr != null ) {
  97. function addDep( path : Array<String> ) {
  98. while( path.length > 0 ) {
  99. var idep = initsMap.get(path.join("."));
  100. if( idep != null ) {
  101. if( i.dependsOn.indexOf(idep) < 0 ) {
  102. i.dependsOn.push(idep);
  103. idep.dependedBy.push(i);
  104. }
  105. return;
  106. }
  107. path.pop();
  108. }
  109. }
  110. function browseExpr( path : Array<String>, e : Expr ) {
  111. switch( e.expr ) {
  112. case EField(eobj, field):
  113. path.unshift(field);
  114. browseExpr(path, eobj);
  115. path.shift();
  116. case EConst(CIdent(id)) if( initsMap.exists(id) ):
  117. path.unshift(id);
  118. addDep(path.copy());
  119. path.shift();
  120. default:
  121. e.iter(browseExpr.bind([]));
  122. }
  123. }
  124. browseExpr([], i.expr);
  125. }
  126. }
  127. var order = [];
  128. function makeAssign( path : Array<String>, expr : Expr ) : Expr {
  129. path = path.copy();
  130. var epath : Expr = { expr : EConst(CIdent(path.shift())), pos : expr.pos };
  131. while( path.length > 0 ) {
  132. var f = path.shift();
  133. var index;
  134. if( f.charCodeAt(0) == "_".code && (index = Std.parseInt(f.substr(1))) != null )
  135. epath = { expr : EArray(epath, { expr : EConst(CInt(""+index)), pos : expr.pos }), pos : expr.pos };
  136. else
  137. epath = { expr : EField(epath, f), pos : expr.pos };
  138. }
  139. return { expr : EBinop(OpAssign, epath, expr), pos : expr.pos };
  140. }
  141. function markRec( i : Init ) {
  142. if( i.mark )
  143. return;
  144. i.mark = true;
  145. if( i.parent != null )
  146. markRec(i.parent);
  147. for( d in i.dependsOn )
  148. markRec(d);
  149. if( i.expr != null )
  150. order.push(makeAssign(i.path,i.expr));
  151. }
  152. for( i in inits )
  153. markRec(i);
  154. switch( constructor.expr.expr ) {
  155. case EBlock(el):
  156. constructor.expr = { expr : EBlock(order.concat(el)), pos : constructor.expr.pos };
  157. default:
  158. constructor.expr = { expr : EBlock(order.concat([constructor.expr])), pos : constructor.expr.pos };
  159. }
  160. return fields;
  161. }
  162. }
  163. #end