Files
tink_macro/src/tink/macro/Constructor.hx
2013-10-20 12:38:09 +02:00

139 lines
3.7 KiB
Haxe

package tink.macro;
import haxe.macro.Expr;
import tink.core.Pair;
using tink.MacroApi;
enum FieldInit {
Value(e:Expr);
Arg(?t:ComplexType, ?noPublish:Bool);
OptArg(?e:Expr, ?t:ComplexType, ?noPublish:Bool);
}
class Constructor {
var oldStatements:Array<Expr>;
var nuStatements:Array<Expr>;
var beforeArgs:Array<FunctionArg>;
var args:Array<FunctionArg>;
var afterArgs:Array<FunctionArg>;
var pos:Position;
var onGenerateHooks:Array<Function->Void>;
var superCall:Expr;
public var isPublic:Null<Bool>;
public function new(f:Function, ?isPublic:Null<Bool> = null, ?pos:Position, ?meta:Metadata) {
this.nuStatements = [];
this.isPublic = isPublic;
this.pos = pos.sanitize();
this.onGenerateHooks = [];
this.args = [];
this.beforeArgs = [];
this.afterArgs = [];
this.oldStatements =
if (f == null) [];
else {
for (i in 0...f.args.length) {
var a = f.args[i];
if (a.name == '_') {
afterArgs = f.args.slice(i + 1);
break;
}
beforeArgs.push(a);
}
if (f.expr == null) [];
else
switch (f.expr.expr) {
case EBlock(exprs): exprs;
default: oldStatements = [f.expr];
}
}
superCall =
if (oldStatements.length == 0) [].toBlock();
else switch oldStatements[0] {
case macro super($a{_}): oldStatements.shift();
default: [].toBlock();
}
}
public function addStatement(e:Expr, ?prepend)
if (prepend)
this.nuStatements.unshift(e)
else
this.nuStatements.push(e);
public function addArg(name:String, ?t:ComplexType, ?e:Expr, ?opt = false)
args.push( { name : name, opt : opt || e != null, type : t, value: e } );
public function init(name:String, pos:Position, with:FieldInit, ?options:{ ?prepend:Bool, ?bypass:Bool }) {
if (options == null)
options = {};
var e =
switch with {
case Arg(t, noPublish):
if (noPublish != true)
publish();
args.push( { name : name, opt : false, type : t } );
name.resolve(pos);
case OptArg(e, t, noPublish):
if (noPublish != true)
publish();
args.push( { name : name, opt : true, type : t, value: e } );
name.resolve(pos);
case Value(e): e;
}
var tmp = MacroApi.tempName();
if (options.bypass) {
if (haxe.macro.Context.defined('java')) {
addStatement(
macro @:pos(pos)
if (Math.random() < .0) {
//if this is false, then it gets thrown out before reaching the backend which will then generate invalid code
var $tmp = this.$name;
$i{tmp} = $e;
this.$name = $i{tmp};
},
true
);
addStatement(macro @:pos(pos) (cast this).$name = $e, options.prepend);
}
else {
addStatement(macro @:pos(pos) if (false) { var $tmp = this.$name; $i{tmp} = $e; }, true);
addStatement(macro @:pos(pos) (cast this).$name = $e, options.prepend);
}
}
else
addStatement(macro @:pos(pos) this.$name = $e, options.prepend);
}
public inline function publish()
if (isPublic == null)
isPublic = true;
function toBlock()
return [superCall].concat(nuStatements).concat(oldStatements).toBlock(pos);
public function onGenerate(hook)
this.onGenerateHooks.push(hook);
public function toHaxe():Field {
var f:Function = {
args: this.beforeArgs.concat(this.args).concat(this.afterArgs),
ret: 'Void'.asComplexType(),
expr: toBlock(),
params: []
};
for (hook in onGenerateHooks) hook(f);
onGenerateHooks = [];
return {
name: 'new',
doc : null,
access : isPublic ? [APublic] : [],
kind : FFun(f),
pos : pos,
meta : []
}
}
}