Macros autoBuild

126 views
Skip to first unread message

Dima Granetchi

unread,
Mar 5, 2012, 4:48:25 PM3/5/12
to haxe...@googlegroups.com
Hi.

I need help with macros and autoBuild. I tried to use @:autoBuild for my overload-operator library and get error.


When I compile I get error
src/Main.hx:38: characters 2-4 : Unknown identifier : c1.

Why autoBuild macros doesn't see c1 id? What can I do?

Simon Krajewski

unread,
Mar 5, 2012, 5:54:35 PM3/5/12
to haxe...@googlegroups.com

The problem is that your context is the class you're building and both
the identifier and the containing function don't exists in that context,
which is why Context.typeof cannot know of these identifiers. I had a
similar problem a while ago and solved based on an idea from Juraj:
Create your own context structure that holds an Array suitable to be
passed to EVars. The structure for that is
typedef IdentDef = Array<{ name : String, type : Null<ComplexType>,
expr : Null<Expr> }>;

Pass this as argument to your parseExpr() function, like
static function parseExpr(e:Expr, ctx:IdentDef)

Then treat it like this:
- if you encounter an EVars (and some others, see below), add each
var's type to your context array.
- if you encounter an EBlock, clone your context array and call
parseExpr for the block members with that cloned array as ctx. This is
to ensure you don't get name clashes when working with several blocks.
- if you encounter an EConst(CIdent)) that you want to get the type
of, you can do this:
Context.typeof(EBlock([EVars(ctx.idents), yourExpr]))

You have to make sure to not miss any declared variables though. We
talked about this here:
https://groups.google.com/forum/?fromgroups#!topic/haxelang/uUVa3_ZMdmc
<https://groups.google.com/forum/?fromgroups#%21topic/haxelang/uUVa3_ZMdmc>

Simon

Dima Granetchi

unread,
Mar 5, 2012, 6:05:53 PM3/5/12
to haxe...@googlegroups.com
Thanks.
You have confirmed my worst fears :(
I thought about manual define variables in current context, but it's greatly complicates the parseExpr method.
Probably I refuse to support @:autoBuild.

Simon Krajewski

unread,
Mar 5, 2012, 7:06:23 PM3/5/12
to haxe...@googlegroups.com

Dima Granetchi

unread,
Mar 6, 2012, 3:23:01 AM3/6/12
to haxe...@googlegroups.com
Thanks!

Looks simple. But I am not sure what it's correct. In haxe variables are visible only in current block, but your IdentDef contains all variables in method. Plus properties are global for all contexts.

Thanks for help!

Dima

Simon Krajewski

unread,
Mar 6, 2012, 5:32:29 AM3/6/12
to haxe...@googlegroups.com
Am 06.03.2012 09:23, schrieb Dima Granetchi:
Thanks!

Looks simple. But I am not sure what it's correct. In haxe variables are visible only in current block, but your IdentDef contains all variables in method.

Actually, a variable is visible in its current block and all sub blocks. That's reflected in the EBlock case:

case EBlock(exprs):
var nexprs = new Array<Expr>();
var innerCtx = Lambda.array(ctx);
for (i in exprs)
nexprs.push(parseExpr(i, innerCtx));
return { expr:EBlock(nexprs), pos:pos };

Note the assignment to innerCtx, it's a _copy_ of the current context, so the inner context has all outside variables, but cannot add any to the outside context. That's exactly what's needed.


Plus properties are global for all contexts.

Yea, that's a little tricky to get right. You can employ two passes for your iteration over the class fields. In the first you push all the field names + types onto your ctx, in the second you actually evaluate the expressions. I've outlined it here:

https://github.com/Simn/overload-operator/blob/autoBuild/src/deep/macro/math/OverloadOperator.hx
https://github.com/Simn/overload-operator/blob/autoBuild/src/Main.hx

I've encountered problems when not explicitely typing getCp1() to Complex. In fact, I'm not sure how to create a TFunction type when a FFun's ret type is null because creating it will null will cause an Interp.Invalid_expr. Maybe Juraj or Nicolas have an idea here.

The rest works well enough to assume that it's a viable concept though. ;)

Simon

Dima Granetchi

unread,
Mar 6, 2012, 5:41:33 AM3/6/12
to haxe...@googlegroups.com
You made my work :)
Thanks!

btw, 'if', 'for', etc has self context blocks too, but I can resolve it problem like EBlock in you exemple.

And I thought about 2 passes too. You beat me again :)

Simon Krajewski

unread,
Mar 6, 2012, 5:51:59 AM3/6/12
to haxe...@googlegroups.com
Am 06.03.2012 11:41, schrieb Dima Granetchi:
> You made my work :)
> Thanks!
>
> btw, 'if', 'for', etc has self context blocks too, but I can resolve
> it problem like EBlock in you exemple.

It's not a special case in regards to scoping. EIf, EFor etc. don't have
their own block definition, they just have Exprs. These Exprs might be
an EBlock, but that is then handled by your EBlock case after the
recursion anyway.

Note though that I'm not handling the variable declaration cases other
than EVars. Typing EFunction and ETry is easy because you get the
ComplexType along with them, but you might have to double check on
ESwitch and EFor because their variable types evaluate from expressions.

Simon

Dima Granetchi

unread,
Mar 6, 2012, 5:55:39 AM3/6/12
to haxe...@googlegroups.com
It's not a special case in regards to scoping. EIf, EFor etc. don't have their own block definition, they just have Exprs. These Exprs might be an EBlock, but that is then handled by your EBlock case after the recursion anyway.

hm, you are right. 
 
Note though that I'm not handling the variable declaration cases other than EVars. Typing EFunction and ETry is easy because you get the ComplexType along with them, but you might have to double check on ESwitch and EFor because their variable types evaluate from expressions.


Good note. Thanks

Dima Granetchi

unread,
Mar 6, 2012, 5:27:37 PM3/6/12
to haxe...@googlegroups.com
I added support for EFunction and ETry block.

You was right, ESwitch and EFor is more complicate. 

Can you look my realization?

(in EFor I doesn't search type of iterator, I simple call e2.iterator().next() and haxe resolve type single :) )

It's very hard and complicate. Maybe I missed something?

Simon Krajewski

unread,
Mar 6, 2012, 7:50:32 PM3/6/12
to haxe...@googlegroups.com
> It's very hard and complicate. Maybe I missed something? --

When working with macros, never think you're doing it wrong just because
it looks complicated. For example, typing the switch case identifiers
means iterating cases and constructors before matching ECall and
EConst(CIdent)). If you type all that out directly, that's easily five
indentation levels to get some measly identifier string.

Consider using tink_macros lib to make some standard tasks easier for
you, like extracting an identifier from an expression or getting a
ComplexType from a Type. You're on the right track and the fact that
your operator overloading with @:autoBuild _does_ work is proof of that.
Keep up the good work!

By the way, if you check haxe's typer.ml for its EFor handling, you will
find that it does something quite similar to your idea.

Simon

Dima Granetchi

unread,
Mar 7, 2012, 3:24:01 AM3/7/12
to haxe...@googlegroups.com
tink_macros is good lib. But overload-operator was my first experiment with macros. Before it, I know about macros only  http://haxe.org/doc/why/macros?lang=en :)

Dima

Simon Krajewski

unread,
Mar 7, 2012, 3:59:04 AM3/7/12
to haxe...@googlegroups.com
Am 07.03.2012 09:24, schrieb Dima Granetchi:
tink_macros is good lib. But overload-operator was my first experiment with macros. Before it, I know about macros only  http://haxe.org/doc/why/macros?lang=en :)

Especially for that it turns out really well and way more impressive than my first macro results. ;)

Simon
Reply all
Reply to author
Forward
0 new messages