Last active
August 29, 2015 14:02
-
-
Save cambiata/e7363739768b6f860e36 to your computer and use it in GitHub Desktop.
Lazy macro implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!'; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lazy ; | |
@:autoBuild(lazy.LazyBuilder.build()) interface Lazy | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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