Хорошо, вот код для реализации очень простой with() {...}
эмуляции на Haxe:
//simple.hx
class Simple
{
@:macro public static function with(subject:Expr, block:Expr)
{
return with_impl(subject, block);
}
#if macro
static function with_impl(subject:Expr, block:Expr)
{
function mk(e, pos) return { expr:e, pos:pos };
//this is the main function here. It's going to find a variable the expression so it uses our subject
function changeIdent(identExpr:Expr, changeExpr)
{
return switch(identExpr.expr)
{
case EConst(c):
switch(c)
{
case CIdent(s):
mk(EField(changeExpr, s), identExpr.pos);
default:
identExpr;
}
case EField(e, f):
mk(EField(changeIdent(e, changeExpr), f), identExpr.pos);
case EType(e, f):
mk(EType(changeIdent(e, changeExpr), f), identExpr.pos);
default: //fallba
identExpr;
}
}
return switch(block.expr)
{
case EBlock(exprs):
var newblock = [];
for (statement in exprs)
{
switch(statement.expr)
{
case ECall(e, params):
newblock.push(mk(ECall(changeIdent(e, subject), params), statement.pos));
default:
newblock.push(statement);
}
}
mk(EBlock(newblock), block.pos);
case EDisplay(e, iscall):
mk(EDisplay(with_impl(subject, e), iscall), block.pos);
default:
changeIdent(block, subject);
}
}
#end
}
вы используете это так:
//Main.hx
class Main
{
static function main()
{
Simple.with (Lib.current.graphics,
{
beginFill(0xC0FFEE, 1);
drawRect( -10, -10, 20, 20 );
endFill();
});
}
}
Вместо изменения области видимости, он будет искать выражения вызова и просто добавлять первый аргумент функции (субъекта) в каждое выражение. Таким образом, приведенный выше код эквивалентен:
{
Lib.current.graphics.beginFill(0xC0FFEE, 1);
Lib.current.graphics.drawRect( -10, -10, 20, 20 );
Lib.current.graphics.endFill();
}
макросы - это так весело!