Instantly share code, notes, and snippets.

@dpeek /FTW.hx
Last active Apr 1, 2018

Embed
What would you like to do?
import haxe.macro.Expr;
class FTW
{
public static function build()
{
return haxe.macro.Context.getBuildFields().map(transformField);
}
static function transformField(field:Field)
{
switch (field.kind)
{
case FFun(f): transformExpr(f.expr);
default:
}
return field;
}
static function transformExpr(expr:Expr) switch (expr)
{
case macro @for($init, $cond, $incr) $block:
transformExpr(block);
expr.expr = makeLoop(init, cond, incr, block).expr;
default:
haxe.macro.ExprTools.iter(expr, transformExpr);
}
static function makeLoop(init:Expr, cond:Expr, incr:Expr, block:Expr) return macro
{
$init;
while ($cond)
{
$block;
$incr;
}
}
}
@:build(FTW.build())
class Main
{
static function main()
{
@for(var i = 0, i < 10, i++)
{
trace('ftw: $i');
}
}
}
@nadako

This comment has been minimized.

Show comment
Hide comment
@nadako

nadako Nov 15, 2013

case macro @for($init, $cond, $incr) $block:

this is just too epic

nadako commented Nov 15, 2013

case macro @for($init, $cond, $incr) $block:

this is just too epic

@skial

This comment has been minimized.

Show comment
Hide comment
@skial

skial Nov 15, 2013

Very smooth 👏

skial commented Nov 15, 2013

Very smooth 👏

@dpeek

This comment has been minimized.

Show comment
Hide comment
@dpeek

dpeek Nov 15, 2013

All credit goes to @Simn for pointing out it was possible.

Magical.

Owner

dpeek commented Nov 15, 2013

All credit goes to @Simn for pointing out it was possible.

Magical.

@saumya

This comment has been minimized.

Show comment
Hide comment
@saumya

saumya Nov 15, 2013

while it seems nice. Still trying to understand Macro. Are all the code in FTW just boilerplate for any macro?

saumya commented Nov 15, 2013

while it seems nice. Still trying to understand Macro. Are all the code in FTW just boilerplate for any macro?

@jdonaldson

This comment has been minimized.

Show comment
Hide comment
@jdonaldson

jdonaldson Nov 15, 2013

Is this "for the win", or "for the whiners"?

jdonaldson commented Nov 15, 2013

Is this "for the win", or "for the whiners"?

@nadako

This comment has been minimized.

Show comment
Hide comment
@nadako

nadako Nov 15, 2013

while it seems nice. Still trying to understand Macro. Are all the code in FTW just boilerplate for any macro?

it's not a boilerplate. this is a build-macro, its entry point is the "build" function (as specified in @:build meta), as a build macro it should return a list of generated fields for given class.

what this particular macro does is iterate over already defined fields and maps them to the transformField field, which in calls transformExpr for the expression in every function field (TFun)

the transformExpr matches given expression for a @for(var i = 0, i < 10, i++) syntax and if finds one, calls makeLoop on that expression, which translates it to while loop.

if matching for @for fails (it's some other expression), then it calls ExprTools.iter on this expression to recursively walk over sub-expressions of this one and check if there is some @for syntax.

this is a form of meta-programming if you like

nadako commented Nov 15, 2013

while it seems nice. Still trying to understand Macro. Are all the code in FTW just boilerplate for any macro?

it's not a boilerplate. this is a build-macro, its entry point is the "build" function (as specified in @:build meta), as a build macro it should return a list of generated fields for given class.

what this particular macro does is iterate over already defined fields and maps them to the transformField field, which in calls transformExpr for the expression in every function field (TFun)

the transformExpr matches given expression for a @for(var i = 0, i < 10, i++) syntax and if finds one, calls makeLoop on that expression, which translates it to while loop.

if matching for @for fails (it's some other expression), then it calls ExprTools.iter on this expression to recursively walk over sub-expressions of this one and check if there is some @for syntax.

this is a form of meta-programming if you like

@YellowAfterlife

This comment has been minimized.

Show comment
Hide comment
@YellowAfterlife

YellowAfterlife Nov 16, 2013

While technically pretty cool, current implementation of makeLoop breaks the expected behaviour of continue statement. I've made a fork with that part fixed: https://gist.github.com/YellowAfterlife/7494363
One can also workaround with code like

    static function makeLoop(init:Expr, cond:Expr, incr:Expr, block:Expr) return macro
    {
        $init;
        while ($cond) {
            do { $block; } while (false);
            $incr;
        }
    }

at the price of loosing ability to use break statements.

But, as said, very interesting. Now I'm starting to get curious whether it's possible to autoinsert @:build into all classes on project level.

YellowAfterlife commented Nov 16, 2013

While technically pretty cool, current implementation of makeLoop breaks the expected behaviour of continue statement. I've made a fork with that part fixed: https://gist.github.com/YellowAfterlife/7494363
One can also workaround with code like

    static function makeLoop(init:Expr, cond:Expr, incr:Expr, block:Expr) return macro
    {
        $init;
        while ($cond) {
            do { $block; } while (false);
            $incr;
        }
    }

at the price of loosing ability to use break statements.

But, as said, very interesting. Now I'm starting to get curious whether it's possible to autoinsert @:build into all classes on project level.

@saumya

This comment has been minimized.

Show comment
Hide comment
@saumya

saumya Nov 17, 2013

@nadako thanks for the explanation. Never knew Macro is a meta programming thing.

saumya commented Nov 17, 2013

@nadako thanks for the explanation. Never knew Macro is a meta programming thing.

@dpeek

This comment has been minimized.

Show comment
Hide comment
@dpeek

dpeek Nov 20, 2013

@YellowAfterlife we could add a helper that allowed you to do: --macro FTW.build('package.name')

Owner

dpeek commented Nov 20, 2013

@YellowAfterlife we could add a helper that allowed you to do: --macro FTW.build('package.name')

@Laurens-Bamtang

This comment has been minimized.

Show comment
Hide comment
@Laurens-Bamtang

Laurens-Bamtang Jun 3, 2014

One question in the line: macro @for($init , $cond , $incr) $block:
is there some way to tell Haxe to accept: @for($init EConst(';') $cond EConst(';') $incr) $block:

Laurens-Bamtang commented Jun 3, 2014

One question in the line: macro @for($init , $cond , $incr) $block:
is there some way to tell Haxe to accept: @for($init EConst(';') $cond EConst(';') $incr) $block:

@player-03

This comment has been minimized.

Show comment
Hide comment
@player-03

player-03 Dec 23, 2014

Possible workaround allowing both continue and break:

static function makeLoop(init:Expr, cond:Expr, incr:Expr, block:Expr) return macro
{
    $init;
    if ($cond)
    {
        do
        {
            $block;
        } while ({ $incr; $cond; });
    }
}

(Untested.)

player-03 commented Dec 23, 2014

Possible workaround allowing both continue and break:

static function makeLoop(init:Expr, cond:Expr, incr:Expr, block:Expr) return macro
{
    $init;
    if ($cond)
    {
        do
        {
            $block;
        } while ({ $incr; $cond; });
    }
}

(Untested.)

@player-03

This comment has been minimized.

Show comment
Hide comment
@player-03

player-03 commented Dec 24, 2014

@YellowAfterlife

This comment has been minimized.

Show comment
Hide comment
@YellowAfterlife

YellowAfterlife Dec 24, 2014

player-03: Your version combines duplicating code (slightly larger output size) with creating anonymous functions (or even whole classes, if it's C#/Java) on most targets. It's an arguable advancement. Example: http://try.haxe.org/#7F556

YellowAfterlife commented Dec 24, 2014

player-03: Your version combines duplicating code (slightly larger output size) with creating anonymous functions (or even whole classes, if it's C#/Java) on most targets. It's an arguable advancement. Example: http://try.haxe.org/#7F556

@player-03

This comment has been minimized.

Show comment
Hide comment
@player-03

player-03 Jun 14, 2016

A year and a half later, I finally noticed your comment. Sorry for the late response!

Anyway, I figured out that you meant to include a continue statement in your example, because that's what causes duplicate code. (Or maybe you did include one and try.haxe.org didn't save it. Or maybe Haxe improved over the past 1.5 years, and now it optimizes your example better.)

Whichever it was, I've fixed the issue. My code checks for the existence of continue and break statements, and uses the simplest possible loop. Even in the most complicated case, the only code that gets duplicated is "shouldBreak = false," not any anonymous functions or classes.

player-03 commented Jun 14, 2016

A year and a half later, I finally noticed your comment. Sorry for the late response!

Anyway, I figured out that you meant to include a continue statement in your example, because that's what causes duplicate code. (Or maybe you did include one and try.haxe.org didn't save it. Or maybe Haxe improved over the past 1.5 years, and now it optimizes your example better.)

Whichever it was, I've fixed the issue. My code checks for the existence of continue and break statements, and uses the simplest possible loop. Even in the most complicated case, the only code that gets duplicated is "shouldBreak = false," not any anonymous functions or classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment