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
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.
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 };
Plus properties are global for all contexts.
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
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.
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
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 :)