Skip to content

Instantly share code, notes, and snippets.

@cambiata
Last active August 29, 2015 14:02
Show Gist options
  • Save cambiata/e7363739768b6f860e36 to your computer and use it in GitHub Desktop.
Save cambiata/e7363739768b6f860e36 to your computer and use it in GitHub Desktop.
Lazy macro implementation
package ;
import lazy.Lazy;
class Main
{
static function main()
{
var t:TestLazy = new TestLazy();
trace(t.sum);
trace(t.sum);
trace(t.helloWorld);
trace(t.helloWorld);
}
}
class TestLazy implements Lazy
{
public function new() {}
@lazy public function sum():Int
{
trace('init sum');
return 1 + 2 + 3;
}
@lazy public function helloWorld():String
{
trace('init helloWorld');
return 'Hello ' + 'World!';
}
}
package lazy ;
@:autoBuild(lazy.LazyBuilder.build()) interface Lazy
{
}
package lazy;
import haxe.macro.Context;
import haxe.macro.Expr;
using haxe.macro.ExprTools;
using Lambda;
// All credits to Andreas Söderlund!
class LazyBuilder
{
public static function build():Array<Field>
{
var originalFields = Context.getBuildFields();
var newFields:Array<Field> = [];
for (f in originalFields)
{
var found = false;
if (f.meta != null)
for (m in f.meta)
{
if (m.name == "lazy")
{
found = true;
newFields = newFields.concat(createLazyFields(f));
break;
}
}
if (!found) newFields.push(f);
}
return newFields;
}
static private function createLazyFields(f:Field):Array<Field>
{
var lazyfields:Array<Field> = [];
var fieldName = f.name;
var fieldType = switch f.kind {
case FFun(f2): f2.ret;
case _:
Context.warning("The 'lazy' meta...", f.pos);
return [f];
}
var functionBody = switch f.kind {
case FFun(f2): f2.expr;
case _: return [f];
}
//-----------------------------------------------------------------------------------------------------------------------
// Create private variable for storing the method result
var privateFieldName = '__lazy' + fieldName;
var nullField = TPath( { name:'Null', pack:[], params:[TPType(fieldType)] } );
lazyfields.push({
name: privateFieldName,
doc: null,
access: [Access.APrivate],
kind: FVar(nullField, null),
pos: Context.currentPos()
});
//-----------------------------------------------------------------------------------------------------------------------
// Create public property
lazyfields.push({
name: fieldName,
doc: null,
access: [Access.APublic],
kind: FProp('get', 'never', fieldType, null),
pos: Context.currentPos()
});
//-----------------------------------------------------------------------------------------------------------------------
// Create private getter method, including the original method
lazyfields.push({
name: 'get_$fieldName',
doc: null,
access: [Access.APrivate],
kind: FFun(createLazyGetter(privateFieldName, functionBody)),
pos: Context.currentPos()
});
return lazyfields;
}
static private function createLazyGetter(privateFieldName:String, functionBody:Expr) : Function
{
var replaceReturnValue:Expr -> Void;
replaceReturnValue = function(e:Expr) {
switch e {
case macro return $retval:
var change = macro return $i{privateFieldName} = $retval;
e.expr = change.expr;
case _: e.iter(replaceReturnValue);
}
}
functionBody.iter(replaceReturnValue);
switch functionBody.expr {
case EBlock(exprs):
exprs.unshift(macro if ( $i { privateFieldName } != null) return $i { privateFieldName } );
// case OBS hantera fall där metoden saknar block!
case _:
}
var result = {
ret: null,
params: [],
expr: functionBody,
args: []
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment