Skip to content

Instantly share code, notes, and snippets.

@postite
Forked from SmilyOrg/Reification
Created May 6, 2014 00:24
Show Gist options
  • Save postite/8fc8273edb583f97718d to your computer and use it in GitHub Desktop.
Save postite/8fc8273edb583f97718d to your computer and use it in GitHub Desktop.
// In short, it turns the following code (spoilers!!)
macro { someFunction(lorem, ipsum, dolor); }
// into the following structure (pay no attention to "pos" for now)
{
expr => EBlock([
{
expr => ECall(
{
expr => EConst(CIdent(someFunction)),
pos => { file => yourfilehere.hx, max => 4295, min => 4291 }
},
[
{
expr => EConst(CIdent(lorem)),
pos => { file => yourfilehere.hx, max => 4301, min => 4296 }
},
{
expr => EConst(CIdent(ipsum)),
pos => { file => yourfilehere.hx, max => 4308, min => 4303 }
},
{
expr => EConst(CIdent(dolor)),
pos => { file => yourfilehere.hx, max => 4315, min => 4310 }
}
]
),
pos => { file => yourfilehere.hx, max => 4316, min => 4291 }
}
]),
pos => { file => yourfilehere.hx, max => 4319, min => 4287 }
}
/*
Why would you want this?
Most often it's used in @:macro functions, because you have to return an expression (Expr) that is then inserted at
appropriate places, but you could also use it for other fun things like generating Haxe code by constructing
an expression structure using macro and printing it out as Haxe using haxe.macro.Printer
*/
// At length...
// Because that's a lot of text, it's easier to look at it as a simplified hierarchy
EBlock
ECall
EIdent
EIdent
EIdent
// which translates to having a block {}, which contains a call with three arguments
// All of the structures above that contain expr and pos are actually of type Expr
// Expr, EBlock, ECall, EConst, CIdent are all a part of haxe.macro.Expr
/*
Expr is just a simple structure / object, but with it, along with all the enums in haxe.macro.Expr,
you can construct every Haxe expression from a hierarchy of enums and structures
*/
typedef Expr = {
var expr : ExprDef;
var pos : Position;
}
// ExprDef is a normal enum and all of its values start with E (EBlock, ECall, EConst, ...)
/*
Position is probably used for completion, syntax highlighting and stuff like that,
but it isn't really relevant here, so we're going to ignore it for this example
*/
// nullPos is just a quick hack so I don't have to manually type in position info every time
var nullPos:Position = { file: "", min: 0, max: 0 };
// Lets say we want to create an expression that would otherwise normally be written like so:
{ someFunction(lorem, ipsum, dolor); }
// So it's a block (because of {}) that contains a function call to someFunction with the arguments lorem, ipsum and dolor
// functionName is a String that contains the function name we'll be using later on
var functionName = "someFunction";
// We can create the structure of the expression manually through the structures and enums of Expr
// First the function parameters: lorem,ipsum,dolor
var params:Array<Expr> = [
{ expr: EConst(CIdent("lorem")), pos: nullPos },
{ expr: EConst(CIdent("ipsum")), pos: nullPos },
{ expr: EConst(CIdent("dolor")), pos: nullPos }
];
// params is an Array of Expr structures, each being an identifier
// EConst = constant expression, which can be either an int, float, string, identifier (which is what we want) or a regexp
// EConst has a parameter of type Constant, which is an enum that defines which one of those it is
// The one we want is CIdent for identifiers, which is a Constant value that takes the name of the identifier as a parameter
// That's a lot of typing and structures and enums and madness!
// This is where reification comes to the rescue!
// Observe, this code is equivalent to the code above (save for position info stored in pos)
var params = [macro lorem, macro ipsum, macro dolor];
/*
At compile time, when the Haxe parser encounters the macro keyword, it just takes the next expression
and turns it into a hierarchy of structures and enums defined in Expr, like we did manually
*/
trace(params);
/*
Tracing the structure produces the following output (prettified for clarity),
which is the same for both (again, apart from pos)
*/
[
{ expr => EConst(CIdent(lorem)), pos => { file => , max => 0, min => 0 } },
{ expr => EConst(CIdent(ipsum)), pos => { file => , max => 0, min => 0 } },
{ expr => EConst(CIdent(dolor)), pos => { file => , max => 0, min => 0 } }
]
// Which is, not surprisingly, the same thing we typed in manually before
// That means reification can save you a lot of typing
// haxe.macro also contains a Printer, which can convert all that structure mess into readable Haxe code as a String
var printer = new Printer();
trace(printer.printExprs(params, ","));
// outputs the following for both
lorem,ipsum,dolor
// To wrap those parameters in a block with a function call, we can either type in the structure directly
var expression:Expr = {
expr: EBlock([{
expr: ECall({
expr: EConst(CIdent(functionName)),
pos: nullPos
}, params),
pos: nullPos
}]),
pos: nullPos
};
// or we can use reification
var expression:Expr = macro { $i{functionName}($a{params}); };
// Printing both out
trace(printer.printExpr(expression));
// results in the following
{
someFunction(lorem, ipsum, dolor);
}
// Note that when reificating, functionName and params have to be escaped, because if we just wrote the following
var expression:Expr = macro { functionName(params); }
// it would just print as
{
functionName(params);
}
/*
whereas $i{functionName} makes an identifier from a String EConst(CIdent(functionName))
and $a{params} is replaced by an array of Expr in its place
*/
// There are a few other escaping mechanisms, more here: https://github.com/HaxeFoundation/HaxeManual/blob/master/md/manual/macro-reification-expression.md
// You could also generate the same structure by specifying the name and function arguments directly
var expression:Expr = macro { someFunction(lorem, ipsum, dolor); };
/*
In the end, reification is just syntax sugar for constructing structures and enums from Expr.hx,
but what you do with those structures is a-whole-nother story...
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment