From 24f2247b7458523fbb0e29004fdc2a5577b97460 Mon Sep 17 00:00:00 2001 From: Dima Granetchi Date: Thu, 8 Jan 2015 15:47:14 +0200 Subject: [PATCH] refactoring --- build.hxml | 6 + src/bindx/Bind.hx | 115 +----- src/bindx/BindExt.hx | 348 +----------------- src/bindx/BindSignal.hx | 167 --------- src/bindx/IBindable.hx | 2 +- src/bindx/macro/BindExtMacros.hx | 344 +++++++++++++++++ src/bindx/macro/BindMacros.hx | 106 ++++++ src/bindx/macro/BindSignalProvider.hx | 164 +++++++++ .../BindableMacros.hx} | 15 +- src/bindx/{ => macro}/GenericError.hx | 7 +- .../{ => macro}/IBindingSignalProvider.hx | 7 +- src/bindx/{ => macro}/MetaUtils.hx | 7 +- test.hxml | 4 +- 13 files changed, 648 insertions(+), 644 deletions(-) create mode 100644 build.hxml create mode 100644 src/bindx/macro/BindExtMacros.hx create mode 100644 src/bindx/macro/BindMacros.hx create mode 100644 src/bindx/macro/BindSignalProvider.hx rename src/bindx/{BindMacros.hx => macro/BindableMacros.hx} (97%) rename src/bindx/{ => macro}/GenericError.hx (97%) rename src/bindx/{ => macro}/IBindingSignalProvider.hx (95%) rename src/bindx/{ => macro}/MetaUtils.hx (98%) diff --git a/build.hxml b/build.hxml new file mode 100644 index 0000000..e9995cb --- /dev/null +++ b/build.hxml @@ -0,0 +1,6 @@ +-main Tests +-cp src +-cp test +-D dump=pretty +-neko bin/test.n +-lib buddy diff --git a/src/bindx/Bind.hx b/src/bindx/Bind.hx index 9b24855..c755e32 100644 --- a/src/bindx/Bind.hx +++ b/src/bindx/Bind.hx @@ -1,129 +1,28 @@ package bindx; -#if macro -import bindx.GenericError; import haxe.macro.Expr; -import haxe.macro.Type; -import haxe.macro.Context; +import bindx.macro.BindMacros; -using haxe.macro.Tools; -using bindx.MetaUtils; -using Lambda; -#end - - -@:access(bindx.BindMacros) +@:access(bindx.macro.BindMacros) class Bind { @:noUsing macro static public function bind(field:Expr, listener:Expr):Expr { - return internalBind(field, listener, true); + return BindMacros.internalBind(field, listener, true); } @:noUsing macro static public function bindTo(field:Expr, target:Expr):Expr { - return internalBindTo(field, target); + return BindMacros.internalBindTo(field, target); } @:noUsing macro static public function unbind(field:Expr, ?listener:Expr):Expr { - return internalBind(field, listener, false); + return BindMacros.internalBind(field, listener, false); } @:noUsing macro static public function notify(field:Expr, ?oldValue:Expr, ?newValue:Expr):Expr { - return internalNotify(field, oldValue, newValue); + return BindMacros.internalNotify(field, oldValue, newValue); } @:noUsing macro static public function unbindAll(object:ExprOf):Expr { - return internalUnbindAll(object); + return BindMacros.internalUnbindAll(object); } - - #if macro - - static inline function internalBind(field:Expr, listener:Expr, doBind:Bool):Expr { - var fieldData = warnCheckField(field); - return if (doBind) BindMacros.bindingSignalProvider.getClassFieldBindExpr(fieldData.e, fieldData.field, listener); - else BindMacros.bindingSignalProvider.getClassFieldUnbindExpr(fieldData.e, fieldData.field, listener); - } - - static inline function internalBindTo(field:Expr, target:Expr):Expr { - var fieldData = warnCheckField(field); - return BindMacros.bindingSignalProvider.getClassFieldBindToExpr(fieldData.e, fieldData.field, target); - } - - static inline function internalNotify(field:Expr, ?oldValue:Expr, ?newValue:Expr):Expr { - var fieldData = warnCheckField(field); - return BindMacros.bindingSignalProvider.getClassFieldChangedExpr(fieldData.e, fieldData.field, oldValue, newValue); - } - - static inline function internalUnbindAll(object:ExprOf):Expr { - var type = Context.typeof(object).follow(); - if (!isBindable(type.getClass())) { - Context.error('\'${object.toString()}\' must be bindx.IBindable', object.pos); - } - return BindMacros.bindingSignalProvider.getUnbindAllExpr(object, type); - } - - static inline function warnCheckField(field:Expr):{e:Expr, field:ClassField} { - var res = null; - try { - res = checkField(field); - if (res.error != null) res.error.contextError(); - } catch (e:FatalError) { - e.contextFatal(); - } catch (e:GenericError) { - e.contextError(); - }; - return res; - } - - static function checkField(f:Expr):{e:Expr, field:ClassField, error:GenericError} { - var error:GenericError; - switch (f.expr) { - case EField(e, field): - var type = Context.typeof(e); - var classType = switch (type) { case TInst(c, _): c.get(); case _: null; }; - if (classType == null) { - error = new FatalError('Type \'${e.toString()}\' is unknown', e.pos); - return {e:f, field:null, error:error}; - } - if (!isBindable(classType)) { - error = new GenericError('\'${e.toString()}\' must be bindx.IBindable', e.pos); - } - - var field = classType.findField(field, null); - if (field == null) { - throw new FatalError('\'${e.toString()}.${field.name}\' expected', field.pos); - return null; - } - - if (!field.hasBindableMeta()) { - error = new GenericError('\'${e.toString()}.${field.name}\' is not bindable', field.pos); - } - - return {e:e, field:field, error:error}; - - case EConst(CIdent(_)): - return {e:f, field:null, error:new GenericError('Can\'t bind \'${f.toString()}\'. Please use \'this.${f.toString()}\'', f.pos)}; - - case _: - } - return {e:f, field:null, error:new GenericError('\'${f.toString()}\' is not bindable', f.pos)}; - } - - static inline function isBindable(classType:ClassType):Bool { - var check = [classType]; - var res = false; - while (check.length > 0 && !res) { - var t = check.shift(); - while (t != null) { - if (t.module == "bindx.IBindable" && t.name == "IBindable") { - res = true; - break; - } - for (it in t.interfaces) - check.push(it.t.get()); - t = t.superClass != null ? t.superClass.t.get() : null; - } - } - return res; - } - #end } \ No newline at end of file diff --git a/src/bindx/BindExt.hx b/src/bindx/BindExt.hx index 90c300a..f479168 100644 --- a/src/bindx/BindExt.hx +++ b/src/bindx/BindExt.hx @@ -1,365 +1,29 @@ package bindx; -#if macro - -import bindx.GenericError; import haxe.macro.Expr; -import haxe.macro.MacroStringTools; -import haxe.macro.Type; import haxe.macro.Context; -import haxe.macro.Printer; +import bindx.macro.BindExtMacros; -using Lambda; -using StringTools; using haxe.macro.Tools; -typedef FieldExpr = { - var field:ClassField; - var bindable:Bool; - var e:Expr; - @:optional var params:Array; -} - -typedef Chain = { - var init:Array; - var bind:Array; - var unbind:Array; - var expr:Expr; -} -#end - -@:access(bindx.BindMacros) -@:access(bindx.Bind) +@:access(bindx.macro.BindxExtMacro) class BindExt { @:noUsing macro static public function expr(expr:ExprOf, listener:ExprOf->Null->Void>):ExprOfVoid> { - return internalBindExpr(expr, listener); + return BindxExtMacro.internalBindExpr(expr, listener); } @:noUsing macro static public function exprTo(expr:ExprOf, target:ExprOf):ExprOfVoid> { var type = Context.typeof(expr).toComplexType(); - return internalBindExpr(expr, macro function (_, to:Null<$type>) $target = to); + return BindxExtMacro.internalBindExpr(expr, macro function (_, to:Null<$type>) $target = to); } @:noUsing macro static public function chain(expr:ExprOf, listener:Expr):ExprOfVoid> { - return internalBindChain(expr, listener); + return BindxExtMacro.internalBindChain(expr, listener); } @:noUsing macro static public function chainTo(expr:ExprOf, target:ExprOf):ExprOfVoid> { var type = Context.typeof(expr).toComplexType(); - return internalBindChain(expr, macro function (_, to:Null<$type>) $target = to); + return BindxExtMacro.internalBindChain(expr, macro function (_, to:Null<$type>) $target = to); } - - #if macro - - static inline function internalBindChain(expr:Expr, listener:Expr):Expr { - var zeroListener = listenerName(0, ""); - var chain = null; - try { chain = warnPrepareChain(expr); } catch (e:GenericError) e.contextError(); - - var res = macro (function ($zeroListener):Void->Void - $b { chain.init.concat(chain.bind).concat([(macro var res = function ():Void $b { chain.unbind }), macro return res]) } - )($listener); - return res; - } - - static inline function unwrapFormatedString(expr:Expr):Expr { - return if (MacroStringTools.isFormatExpr(expr)) { - var f = switch (expr.expr) { - case EConst(CString(s)): s; - case _: null; - } - if (f != null) MacroStringTools.formatString(f, expr.pos) else expr; - } else expr; - } - - static function internalBindExpr(expr:Expr, listener:Expr):Expr { - var type = Context.typeof(expr).toComplexType(); - var listenerNameExpr = macro listener; - var fieldListenerName = "fieldListener"; - var fieldListenerNameExpr = macro $i{fieldListenerName}; - var methodListenerName = "methodListener"; - var methodListenerNameExpr = macro $i{methodListenerName}; - var chain:Chain = { init:[], bind:[], unbind:[], expr:expr }; - var binded:Map = new Map(); - - var prefix = 0; - function findChain(expr:Expr) { - var isChain; - expr = unwrapFormatedString(expr); - var e = expr; - do switch (e.expr) { - case EField(le, _) | ECall(le, _): - isChain = true; - e = le; - case _: - isChain = false; - } while (isChain); - var doBind = e != expr; - if (doBind) { - var key = expr.toString(); - for (k in binded.keys()) if (k.startsWith(key)) { - doBind = false; - break; - } - } - if (doBind) { - var pre = '_${prefix++}'; - var zeroListener = listenerName(0, pre); - var c = null; - try { - c = warnPrepareChain(expr, pre, true); - } catch (e:GenericError) { - Warn.w('${expr.toString()} is not bindable.', e.pos, WarnPriority.ALL); - } - if (c != null) { - var key = c.expr.toString(); - if (!binded.exists(key)) { - var ecall = switch (c.expr.expr) { - case EField(e, field): - var type = Context.typeof(e); - var classRef = type.getClass(); - var field = classRef.findField(field); - field.kind.match(FMethod(_)); - case _: false; - } - var prebind = macro var $zeroListener = ${ecall ? methodListenerNameExpr : fieldListenerNameExpr}; - binded.set(key, {prebind:prebind, c:c}); - } - } - } - expr.iter(findChain); - } - findChain(expr); - - var keys = [for (k in binded.keys()) k]; - var i = 0; - while (i < keys.length) { - var k = keys[i]; - var remove = false; - var j = i; - while (!remove && ++j < keys.length) remove = keys[j].startsWith(k); - if (remove) keys = keys.splice(i, 1); else i++; - } - - var msg = []; - for (k in keys) { - var data = binded.get(k); - msg.push(data.c.expr.toString()); - chain.bind.unshift(data.prebind); - var c = data.c; - chain.init = chain.init.concat(c.init); - chain.bind = chain.bind.concat(c.bind); - chain.unbind = chain.unbind.concat(c.unbind); - } - Warn.w('Bind \'${msg.join("', '")}\'', expr.pos, WarnPriority.INFO); - - var zeroListener = listenerName(0, ""); - var zeroValue = 'value0'; - chain.unbind.unshift(macro $i { zeroValue } = null); - - var callListener = switch (type) { - case macro : Void: macro if (!init) $i{zeroListener}(); - case _: - macro if (!init) { - var v:Null < $type > = null; - try { v = $expr; } catch (e:Dynamic) { }; - $i { zeroListener } ($i { zeroValue }, v); - $i { zeroValue } = v; - }; - } - - var preInit = [ - (macro var init:Bool = true), - macro var $zeroValue:Null<$type> = null - ]; - - var postInit = [ - macro function $fieldListenerName(?from:Dynamic, ?to:Dynamic) $callListener, - macro function $methodListenerName() $callListener - ]; - - var result = [ - macro init = false, - macro $i { methodListenerName } (), - (macro var res = function ():Void $b { chain.unbind } ), - macro return res - ]; - - var res = macro (function ($zeroListener):Void->Void - $b { preInit.concat(chain.init).concat(postInit).concat(chain.bind).concat(result) } - )($listener); - - return res; - } - - static function checkFields(expr:Expr):Array { - var first = Bind.checkField(expr); - var firstParams; - if (first.field == null) { - switch (expr.expr) { - case ECall(e, params): - first = Bind.checkField(e); - firstParams = params; - case _: - } - } - if (first.field == null) { - if (first.error != null) throw first.error; - else throw new FatalError('${expr.toString()} is not bindable.', expr.pos); - } - - var prevField = {e:first.e, field:first.field, error:null}; - var fields:Array = [ { field:first.field, bindable:first.error == null, e:first.e, params:firstParams } ]; - - var end; - do { - end = true; - var field = Bind.checkField(prevField.e); - if (field.field != null) { - fields.push( { field:field.field, bindable:field.error == null, e:field.e } ); - end = false; - } else if (field.error != null) switch (prevField.e.expr) { - case ECall(e, params): - field = Bind.checkField(e); - if (field.field == null) throw new FatalError('${e.toString()} is not bindable.', expr.pos); - else fields.push( { e:field.e, field:field.field, params:params, bindable:field.error == null } ); - end = false; - case _: - } - else if (field.e == null) { - throw new FatalError('${prevField.e.toString()} is not bindable.', prevField.e.pos); - } - prevField = field; - } while (!end); - return fields; - } - - static function warnPrepareChain(expr:Expr, prefix = "", skipUnbindable = false):Chain { - var fields = checkFields(expr); - - if (fields.length == 0) - throw new FatalError('Can\'t bind empty expression: ${expr.toString()}', expr.pos); - - var i = fields.length; - var first = null; - while (i-- > 0) { - var f = fields[i]; - if (first != null) f.bindable = false; - else if (!f.bindable && first == null) { - first = f; - if (skipUnbindable) { - fields = fields.splice(i+1, fields.length - i); - break; - } - } - } - var bindableNum = fields.fold(function (it, n) return n += it.bindable ? 1 : 0, 0); - if (bindableNum == 0) { - throw new GenericError('${expr.toString()} is not bindable.', expr.pos); - } - if (first != null) { - Warn.w('${expr.toString()} is not full bindable. Can bind only "${first.e.toString()}".', expr.pos, WarnPriority.INFO); - } - return prepareChain(fields, expr, prefix); - } - - inline static function listenerName(idx:Int, prefix) return '${prefix}listener$idx'; - - static function prepareChain(fields:Array, expr:Expr, prefix = ""):Chain { - var res:Chain = { init:[], bind:[], unbind:[], expr:null }; - - var prevListenerName = listenerName(0, prefix); - var prevListenerNameExpr = macro $i { prevListenerName }; - var zeroListener = fields[0].bindable ? { f:fields[0], l:prevListenerNameExpr } : null; - if (zeroListener != null) { - var fn = zeroListener.f.field.name; - res.expr = macro @:pos(zeroListener.f.e.pos) ${zeroListener.f.e}.$fn; - } - var i = -1; - while (++i < fields.length - 1) { - var field = fields[i + 1]; - var prev = fields[i]; - var type = Context.typeof(field.e).toComplexType(); - var listenerName = listenerName(i+1, prefix); - var listenerNameExpr = macro $i { listenerName }; - - var value = '${prefix}value${i+1}'; - var valueExpr = macro $i { value }; - - var oldValue = '${prefix}oldValue${i+1}'; - var oldValueExpr = macro $i { oldValue }; - - var fieldName = prev.field.name; - var e = prev.e; - - var fieldListenerBody = []; - var fieldListener; - - if (field.bindable) zeroListener = { f:field, l:listenerNameExpr }; - - if (prev.bindable && res.expr == null) { - var fn = prev.field.name; - res.expr = macro @:pos(prev.e.pos) ${prev.e}.$fn; - } - - if (prev.bindable) { - var unbind = BindMacros.bindingSignalProvider.getClassFieldUnbindExpr(valueExpr, prev.field, prevListenerNameExpr ); - - res.bind.push(macro var $value:Null<$type> = null ); - res.unbind.push(macro if ($valueExpr != null) { $unbind; $valueExpr = null; } ); - - fieldListenerBody.push(macro if ($valueExpr != null) $unbind ); - fieldListenerBody.push(macro $valueExpr = n ); - fieldListenerBody.push(macro if (n != null) - $ { BindMacros.bindingSignalProvider.getClassFieldBindExpr(macro n, prev.field, prevListenerNameExpr ) }); - } - var callPrevArgs = prev.params != null ? [] : [macro o != null ? o.$fieldName : null, macro n != null ? n.$fieldName : null]; - var callPrev = macro $prevListenerNameExpr($a { callPrevArgs } ); - fieldListenerBody.push(callPrev); - - if (field.params != null) { - fieldListenerBody.unshift(macro $i { oldValue } = n); - fieldListenerBody.unshift(macro try { n = $e; } catch (e:Dynamic) { }); - fieldListenerBody.unshift(macro var n:Null < $type > = null); - fieldListenerBody.unshift(macro var o:Null<$type> = $i{oldValue} ); - - res.init.push(macro var $oldValue:Null<$type> = null); - res.unbind.push(macro $oldValueExpr = null); - - fieldListener = macro function $listenerName ():Void $b { fieldListenerBody }; - } else { - if (prev.bindable) { - fieldListenerBody.unshift(macro if (o != null) - ${BindMacros.bindingSignalProvider.getClassFieldUnbindExpr(macro o, prev.field, prevListenerNameExpr )} - ); - } - fieldListener = macro function $listenerName (o:Null<$type>, n:Null<$type>):Void $b { fieldListenerBody }; - } - - res.bind.push(fieldListener); - - prevListenerName = listenerName; - prevListenerNameExpr = listenerNameExpr; - } - - if (zeroListener == null || zeroListener.f.bindable == false) - throw new GenericError('${expr.toString()} is not bindable.', expr.pos); - - var zeroName = zeroListener.f.e.toString(); - if (zeroName != "this") - res.init.unshift(macro var $zeroName = $i{zeroName}); - - res.bind.push(BindMacros.bindingSignalProvider.getClassFieldBindExpr(macro $i{zeroName}, zeroListener.f.field, zeroListener.l )); - res.unbind.push(BindMacros.bindingSignalProvider.getClassFieldUnbindExpr(macro $i{zeroName}, zeroListener.f.field, zeroListener.l )); - - if (zeroListener.f.params != null) { - res.bind.push(macro ${zeroListener.l}()); - } else { - var fieldName = zeroListener.f.field.name; - res.bind.push(macro $ { zeroListener.l } (null, $ { zeroListener.f.e } .$fieldName )); - } - return res; - } - #end } \ No newline at end of file diff --git a/src/bindx/BindSignal.hx b/src/bindx/BindSignal.hx index 5a8e0d4..93c57ff 100644 --- a/src/bindx/BindSignal.hx +++ b/src/bindx/BindSignal.hx @@ -1,172 +1,5 @@ package bindx; -#if macro - -import bindx.BindSignal.BindSignalProvider; -import bindx.BindSignal.Signal; -import haxe.macro.Expr; -import haxe.macro.Type; -import haxe.macro.Context; - -using bindx.MetaUtils; -using haxe.macro.Tools; -using Lambda; - -class BindSignalProvider implements IBindingSignalProvider { - - static inline var SIGNAL_POSTFIX = "Changed"; - - /** - * default value: true - */ - static inline var LAZY_SIGNAL = "lazySignal"; - /** - * default value: false - */ - static inline var INLINE_SIGNAL_GETTER = "inlineSignalGetter"; - - public function new() {} - - @:extern static inline function signalName(fieldName:String):String return fieldName + SIGNAL_POSTFIX; - @:extern static inline function signalGetterName(fieldName:String):String return "get_" + signalName(fieldName); - @:extern static inline function signalPrivateName(fieldName:String):String return "_" + signalName(fieldName); - - public function getFieldDispatcher(field:Field, res:Array):Void { - switch (field.kind) { - case FFun(_): - generateSignal(field, macro : bindx.BindSignal.MethodSignal, macro new bindx.BindSignal.MethodSignal(), res); - case FProp(_, _, type, _) | FVar(type, _): - generateSignal(field, macro : bindx.BindSignal.FieldSignal<$type>, macro new bindx.BindSignal.FieldSignal<$type>(), res); - } - } - - public function getFieldChangedExpr(field:Field, oldValue:Expr, newValue:Expr):Expr { - var args = switch (field.kind) { case FFun(_): []; case _: [oldValue, newValue]; } - return dispatchSignal(macro this, field.name, args, hasLazy(field.bindableMeta())); - } - - public function getClassFieldBindExpr(expr:Expr, field:ClassField, listener:Expr):Expr { - var signalName = signalName(field.name); - return macro @:privateAccess $expr.$signalName.add($listener); - } - - public function getClassFieldBindToExpr(expr:Expr, field:ClassField, target:Expr):Expr { - var signalName = signalName(field.name); - return switch (field.kind) { - case FMethod(_): - var fieldName = field.name; - macro @:privateAccess { - var listener = function () $target = $expr.$fieldName(); - $expr.$signalName.add(listener); - function __unbind__() $expr.$signalName.remove(listener); - } - case FVar(_, _): - var type = field.type.follow().toComplexType(); - macro @:privateAccess { - var listener = function (from:$type, to:$type) $target = to; - $expr.$signalName.add(listener); - function __unbind__() $expr.$signalName.remove(listener); - } - } - } - - public function getClassFieldUnbindExpr(expr:Expr, field:ClassField, listener:Expr):Expr { - var signalName = signalName(field.name); - return if (!listener.isNullOrEmpty()) - macro @:privateAccess $expr.$signalName.remove($listener); - else - macro @:privateAccess $expr.$signalName.removeAll(); - } - - public function getClassFieldChangedExpr(expr:Expr, field:ClassField, oldValue:Expr, newValue:Expr):Expr { - var args = switch (field.kind) { - case FMethod(_): - if (!oldValue.isNullOrEmpty()) - Context.error("method notify doesn't require oldValue", oldValue.pos); - if (!newValue.isNullOrEmpty()) - Context.error("method notify doesn't require newValue", newValue.pos); - []; - case FVar(_, _): - [oldValue, newValue]; - } - return dispatchSignal(expr, field.name, args, hasLazy(field.bindableMeta())); - } - - public function getUnbindAllExpr(expr:ExprOf, type:Type):Expr { - return macro bindx.BindSignal.SignalTools.unbindAll($expr); - } - - function generateSignal(field:Field, type:ComplexType, builder:Expr, res:Array):Void { - var signalName = signalName(field.name); - var meta = field.bindableMeta(); - var inlineSignalGetter = meta.findParam(INLINE_SIGNAL_GETTER); - - if (hasLazy(meta)) { - var signalPrivateName = signalPrivateName(field.name); - res.push({ - name: signalPrivateName, - kind: FVar(type, null), - pos: field.pos, - meta: [ { name:SignalTools.BIND_SIGNAL_META, pos:field.pos, params: [macro true] } ], - access: [APrivate] - }); - - res.push({ - name: signalName, - kind: FProp("get", "never", type, null), - pos: field.pos, - access: [APrivate], - }); - - var getter = macro function foo() { - if (this.$signalPrivateName == null) - this.$signalPrivateName = ${builder}; - return $i{signalPrivateName}; - }; - var getterAccess = [APrivate]; - if (inlineSignalGetter.isNotNullAndTrue()) getterAccess.push(AInline); - - res.push({ - name: signalGetterName(field.name), - kind: FFun(switch (getter.expr) { case EFunction (_, func): func; case _: throw false; }), - pos: field.pos, - access: getterAccess - }); - } else { - if (inlineSignalGetter != null) - Context.warning('$INLINE_SIGNAL_GETTER works only with lazy signals', inlineSignalGetter.pos); - - res.push({ - name: signalName, - kind: FProp("default", "null", type, builder), - pos: field.pos, - access: [APrivate], - meta: [ { name:SignalTools.BIND_SIGNAL_META, pos:field.pos, params: [macro false] } ] - }); - } - } - - inline function dispatchSignal(expr:Expr, fieldName:String, args:Array, lazy:Bool):Expr { - return - if (lazy) { - var signalPrivateName = signalPrivateName(fieldName); - macro @:privateAccess { - if ($expr.$signalPrivateName != null) - $expr.$signalPrivateName.dispatch($a { args } ); - } - } else { - var signalName = signalName(fieldName); - macro @:privateAccess $expr.$signalName.dispatch($a { args } ); - } - } - - @:extern inline function hasLazy(meta:MetadataEntry):Bool { - return meta.findParam(LAZY_SIGNAL).isNullOrTrue(); - } -} - -#end - class MethodSignal extends Signal Void> { public function dispatch():Void { diff --git a/src/bindx/IBindable.hx b/src/bindx/IBindable.hx index 16e87fc..ded90cf 100644 --- a/src/bindx/IBindable.hx +++ b/src/bindx/IBindable.hx @@ -1,5 +1,5 @@ package bindx; -@:autoBuild(bindx.BindMacros.buildIBindable()) +@:autoBuild(bindx.macro.BindableMacros.buildIBindable()) interface IBindable { } diff --git a/src/bindx/macro/BindExtMacros.hx b/src/bindx/macro/BindExtMacros.hx new file mode 100644 index 0000000..545c047 --- /dev/null +++ b/src/bindx/macro/BindExtMacros.hx @@ -0,0 +1,344 @@ +package bindx.macro; + +#if macro + +import bindx.macro.GenericError; +import haxe.macro.Expr; +import haxe.macro.MacroStringTools; +import haxe.macro.Type; +import haxe.macro.Context; +import bindx.macro.BindMacros; + +using Lambda; +using StringTools; +using haxe.macro.Tools; + +private typedef FieldExpr = { + var field:ClassField; + var bindable:Bool; + var e:Expr; + @:optional var params:Array; +} + +private typedef Chain = { + var init:Array; + var bind:Array; + var unbind:Array; + var expr:Expr; +} + +@:access(bindx.macro.BindableMacros) +@:access(bindx.macro.BindMacros) +class BindxExtMacro { + + static inline function internalBindChain(expr:Expr, listener:Expr):Expr { + var zeroListener = listenerName(0, ""); + var chain = null; + try { chain = warnPrepareChain(expr); } catch (e:GenericError) e.contextError(); + + var res = macro (function ($zeroListener):Void->Void + $b { chain.init.concat(chain.bind).concat([(macro var res = function ():Void $b { chain.unbind }), macro return res]) } + )($listener); + return res; + } + + static inline function unwrapFormatedString(expr:Expr):Expr { + return if (MacroStringTools.isFormatExpr(expr)) { + var f = switch (expr.expr) { + case EConst(CString(s)): s; + case _: null; + } + if (f != null) MacroStringTools.formatString(f, expr.pos) else expr; + } else expr; + } + + static function internalBindExpr(expr:Expr, listener:Expr):Expr { + var type = Context.typeof(expr).toComplexType(); + var listenerNameExpr = macro listener; + var fieldListenerName = "fieldListener"; + var fieldListenerNameExpr = macro $i{fieldListenerName}; + var methodListenerName = "methodListener"; + var methodListenerNameExpr = macro $i{methodListenerName}; + var chain:Chain = { init:[], bind:[], unbind:[], expr:expr }; + var binded:Map = new Map(); + + var prefix = 0; + function findChain(expr:Expr) { + var isChain; + expr = unwrapFormatedString(expr); + var e = expr; + do switch (e.expr) { + case EField(le, _) | ECall(le, _): + isChain = true; + e = le; + case _: + isChain = false; + } while (isChain); + var doBind = e != expr; + if (doBind) { + var key = expr.toString(); + for (k in binded.keys()) if (k.startsWith(key)) { + doBind = false; + break; + } + } + if (doBind) { + var pre = '_${prefix++}'; + var zeroListener = listenerName(0, pre); + var c = null; + try { + c = warnPrepareChain(expr, pre, true); + } catch (e:GenericError) { + Warn.w('${expr.toString()} is not bindable.', e.pos, WarnPriority.ALL); + } + if (c != null) { + var key = c.expr.toString(); + if (!binded.exists(key)) { + var ecall = switch (c.expr.expr) { + case EField(e, field): + var type = Context.typeof(e); + var classRef = type.getClass(); + var field = classRef.findField(field); + field.kind.match(FMethod(_)); + case _: false; + } + var prebind = macro var $zeroListener = ${ecall ? methodListenerNameExpr : fieldListenerNameExpr}; + binded.set(key, {prebind:prebind, c:c}); + } + } + } + expr.iter(findChain); + } + findChain(expr); + + var keys = [for (k in binded.keys()) k]; + var i = 0; + while (i < keys.length) { + var k = keys[i]; + var remove = false; + var j = i; + while (!remove && ++j < keys.length) remove = keys[j].startsWith(k); + if (remove) keys = keys.splice(i, 1); else i++; + } + + var msg = []; + for (k in keys) { + var data = binded.get(k); + msg.push(data.c.expr.toString()); + chain.bind.unshift(data.prebind); + var c = data.c; + chain.init = chain.init.concat(c.init); + chain.bind = chain.bind.concat(c.bind); + chain.unbind = chain.unbind.concat(c.unbind); + } + Warn.w('Bind \'${msg.join("', '")}\'', expr.pos, WarnPriority.INFO); + + var zeroListener = listenerName(0, ""); + var zeroValue = 'value0'; + chain.unbind.unshift(macro $i { zeroValue } = null); + + var callListener = switch (type) { + case macro : Void: macro if (!init) $i{zeroListener}(); + case _: + macro if (!init) { + var v:Null < $type > = null; + try { v = $expr; } catch (e:Dynamic) { }; + $i { zeroListener } ($i { zeroValue }, v); + $i { zeroValue } = v; + }; + } + + var preInit = [ + (macro var init:Bool = true), + macro var $zeroValue:Null<$type> = null + ]; + + var postInit = [ + macro function $fieldListenerName(?from:Dynamic, ?to:Dynamic) $callListener, + macro function $methodListenerName() $callListener + ]; + + var result = [ + macro init = false, + macro $i { methodListenerName } (), + (macro var res = function ():Void $b { chain.unbind } ), + macro return res + ]; + + var res = macro (function ($zeroListener):Void->Void + $b { preInit.concat(chain.init).concat(postInit).concat(chain.bind).concat(result) } + )($listener); + + return res; + } + + static function checkFields(expr:Expr):Array { + var first = BindMacros.checkField(expr); + var firstParams; + if (first.field == null) { + switch (expr.expr) { + case ECall(e, params): + first = BindMacros.checkField(e); + firstParams = params; + case _: + } + } + if (first.field == null) { + if (first.error != null) throw first.error; + else throw new FatalError('${expr.toString()} is not bindable.', expr.pos); + } + + var prevField = {e:first.e, field:first.field, error:null}; + var fields:Array = [ { field:first.field, bindable:first.error == null, e:first.e, params:firstParams } ]; + + var end; + do { + end = true; + var field = BindMacros.checkField(prevField.e); + if (field.field != null) { + fields.push( { field:field.field, bindable:field.error == null, e:field.e } ); + end = false; + } else if (field.error != null) switch (prevField.e.expr) { + case ECall(e, params): + field = BindMacros.checkField(e); + if (field.field == null) throw new FatalError('${e.toString()} is not bindable.', expr.pos); + else fields.push( { e:field.e, field:field.field, params:params, bindable:field.error == null } ); + end = false; + case _: + } + else if (field.e == null) { + throw new FatalError('${prevField.e.toString()} is not bindable.', prevField.e.pos); + } + prevField = field; + } while (!end); + return fields; + } + + static function warnPrepareChain(expr:Expr, prefix = "", skipUnbindable = false):Chain { + var fields = checkFields(expr); + + if (fields.length == 0) + throw new FatalError('Can\'t bind empty expression: ${expr.toString()}', expr.pos); + + var i = fields.length; + var first = null; + while (i-- > 0) { + var f = fields[i]; + if (first != null) f.bindable = false; + else if (!f.bindable && first == null) { + first = f; + if (skipUnbindable) { + fields = fields.splice(i+1, fields.length - i); + break; + } + } + } + var bindableNum = fields.fold(function (it, n) return n += it.bindable ? 1 : 0, 0); + if (bindableNum == 0) { + throw new GenericError('${expr.toString()} is not bindable.', expr.pos); + } + if (first != null) { + Warn.w('${expr.toString()} is not full bindable. Can bind only "${first.e.toString()}".', expr.pos, WarnPriority.INFO); + } + return prepareChain(fields, expr, prefix); + } + + inline static function listenerName(idx:Int, prefix) return '${prefix}listener$idx'; + + static function prepareChain(fields:Array, expr:Expr, prefix = ""):Chain { + var res:Chain = { init:[], bind:[], unbind:[], expr:null }; + + var prevListenerName = listenerName(0, prefix); + var prevListenerNameExpr = macro $i { prevListenerName }; + var zeroListener = fields[0].bindable ? { f:fields[0], l:prevListenerNameExpr } : null; + if (zeroListener != null) { + var fn = zeroListener.f.field.name; + res.expr = macro @:pos(zeroListener.f.e.pos) ${zeroListener.f.e}.$fn; + } + var i = -1; + while (++i < fields.length - 1) { + var field = fields[i + 1]; + var prev = fields[i]; + var type = Context.typeof(field.e).toComplexType(); + var listenerName = listenerName(i+1, prefix); + var listenerNameExpr = macro $i { listenerName }; + + var value = '${prefix}value${i+1}'; + var valueExpr = macro $i { value }; + + var oldValue = '${prefix}oldValue${i+1}'; + var oldValueExpr = macro $i { oldValue }; + + var fieldName = prev.field.name; + var e = prev.e; + + var fieldListenerBody = []; + var fieldListener; + + if (field.bindable) zeroListener = { f:field, l:listenerNameExpr }; + + if (prev.bindable && res.expr == null) { + var fn = prev.field.name; + res.expr = macro @:pos(prev.e.pos) ${prev.e}.$fn; + } + + if (prev.bindable) { + var unbind = BindableMacros.bindingSignalProvider.getClassFieldUnbindExpr(valueExpr, prev.field, prevListenerNameExpr ); + + res.bind.push(macro var $value:Null<$type> = null ); + res.unbind.push(macro if ($valueExpr != null) { $unbind; $valueExpr = null; } ); + + fieldListenerBody.push(macro if ($valueExpr != null) $unbind ); + fieldListenerBody.push(macro $valueExpr = n ); + fieldListenerBody.push(macro if (n != null) + $ { BindableMacros.bindingSignalProvider.getClassFieldBindExpr(macro n, prev.field, prevListenerNameExpr ) }); + } + var callPrevArgs = prev.params != null ? [] : [macro o != null ? o.$fieldName : null, macro n != null ? n.$fieldName : null]; + var callPrev = macro $prevListenerNameExpr($a { callPrevArgs } ); + fieldListenerBody.push(callPrev); + + if (field.params != null) { + fieldListenerBody.unshift(macro $i { oldValue } = n); + fieldListenerBody.unshift(macro try { n = $e; } catch (e:Dynamic) { }); + fieldListenerBody.unshift(macro var n:Null < $type > = null); + fieldListenerBody.unshift(macro var o:Null<$type> = $i{oldValue} ); + + res.init.push(macro var $oldValue:Null<$type> = null); + res.unbind.push(macro $oldValueExpr = null); + + fieldListener = macro function $listenerName ():Void $b { fieldListenerBody }; + } else { + if (prev.bindable) { + fieldListenerBody.unshift(macro if (o != null) + ${BindableMacros.bindingSignalProvider.getClassFieldUnbindExpr(macro o, prev.field, prevListenerNameExpr )} + ); + } + fieldListener = macro function $listenerName (o:Null<$type>, n:Null<$type>):Void $b { fieldListenerBody }; + } + + res.bind.push(fieldListener); + + prevListenerName = listenerName; + prevListenerNameExpr = listenerNameExpr; + } + + if (zeroListener == null || zeroListener.f.bindable == false) + throw new GenericError('${expr.toString()} is not bindable.', expr.pos); + + var zeroName = zeroListener.f.e.toString(); + if (zeroName != "this") + res.init.unshift(macro var $zeroName = $i{zeroName}); + + res.bind.push(BindableMacros.bindingSignalProvider.getClassFieldBindExpr(macro $i{zeroName}, zeroListener.f.field, zeroListener.l )); + res.unbind.push(BindableMacros.bindingSignalProvider.getClassFieldUnbindExpr(macro $i{zeroName}, zeroListener.f.field, zeroListener.l )); + + if (zeroListener.f.params != null) { + res.bind.push(macro ${zeroListener.l}()); + } else { + var fieldName = zeroListener.f.field.name; + res.bind.push(macro $ { zeroListener.l } (null, $ { zeroListener.f.e } .$fieldName )); + } + return res; + } +} +#end \ No newline at end of file diff --git a/src/bindx/macro/BindMacros.hx b/src/bindx/macro/BindMacros.hx new file mode 100644 index 0000000..dca2e3a --- /dev/null +++ b/src/bindx/macro/BindMacros.hx @@ -0,0 +1,106 @@ +package bindx.macro; + +#if macro + +import bindx.macro.GenericError; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.Context; + +using haxe.macro.Tools; +using bindx.macro.MetaUtils; +using Lambda; + +@:access(bindx.macro.BindableMacros) +class BindMacros { + + static inline function internalBind(field:Expr, listener:Expr, doBind:Bool):Expr { + var fieldData = warnCheckField(field); + return if (doBind) BindableMacros.bindingSignalProvider.getClassFieldBindExpr(fieldData.e, fieldData.field, listener); + else BindableMacros.bindingSignalProvider.getClassFieldUnbindExpr(fieldData.e, fieldData.field, listener); + } + + static inline function internalBindTo(field:Expr, target:Expr):Expr { + var fieldData = warnCheckField(field); + return BindableMacros.bindingSignalProvider.getClassFieldBindToExpr(fieldData.e, fieldData.field, target); + } + + static inline function internalNotify(field:Expr, ?oldValue:Expr, ?newValue:Expr):Expr { + var fieldData = warnCheckField(field); + return BindableMacros.bindingSignalProvider.getClassFieldChangedExpr(fieldData.e, fieldData.field, oldValue, newValue); + } + + static inline function internalUnbindAll(object:ExprOf):Expr { + var type = Context.typeof(object).follow(); + if (!isBindable(type.getClass())) { + Context.error('\'${object.toString()}\' must be bindx.IBindable', object.pos); + } + return BindableMacros.bindingSignalProvider.getUnbindAllExpr(object, type); + } + + static inline function warnCheckField(field:Expr):{e:Expr, field:ClassField} { + var res = null; + try { + res = checkField(field); + if (res.error != null) res.error.contextError(); + } catch (e:FatalError) { + e.contextFatal(); + } catch (e:GenericError) { + e.contextError(); + }; + return res; + } + + static function checkField(f:Expr):{e:Expr, field:ClassField, error:GenericError} { + var error:GenericError = null; + switch (f.expr) { + case EField(e, field): + var type = Context.typeof(e); + var classType = switch (type) { case TInst(c, _): c.get(); case _: null; }; + if (classType == null) { + error = new FatalError('Type \'${e.toString()}\' is unknown', e.pos); + return {e:f, field:null, error:error}; + } + if (!isBindable(classType)) { + error = new GenericError('\'${e.toString()}\' must be bindx.IBindable', e.pos); + } + + var field = classType.findField(field, null); + if (field == null) { + throw new FatalError('\'${e.toString()}.${field.name}\' expected', field.pos); + return null; + } + + if (!field.hasBindableMeta()) { + error = new GenericError('\'${e.toString()}.${field.name}\' is not bindable', field.pos); + } + + return {e:e, field:field, error:error}; + + case EConst(CIdent(_)): + return {e:f, field:null, error:new GenericError('Can\'t bind \'${f.toString()}\'. Please use \'this.${f.toString()}\'', f.pos)}; + + case _: + } + return {e:f, field:null, error:new GenericError('\'${f.toString()}\' is not bindable', f.pos)}; + } + + static inline function isBindable(classType:ClassType):Bool { + var check = [classType]; + var res = false; + while (check.length > 0 && !res) { + var t = check.shift(); + while (t != null) { + if (t.module == "bindx.IBindable" && t.name == "IBindable") { + res = true; + break; + } + for (it in t.interfaces) + check.push(it.t.get()); + t = t.superClass != null ? t.superClass.t.get() : null; + } + } + return res; + } +} +#end \ No newline at end of file diff --git a/src/bindx/macro/BindSignalProvider.hx b/src/bindx/macro/BindSignalProvider.hx new file mode 100644 index 0000000..bd47262 --- /dev/null +++ b/src/bindx/macro/BindSignalProvider.hx @@ -0,0 +1,164 @@ +package bindx.macro; + +import bindx.BindSignal.Signal; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.Context; +import bindx.BindSignal; + +using bindx.macro.MetaUtils; +using haxe.macro.Tools; +using Lambda; + +class BindSignalProvider implements IBindingSignalProvider { + + static inline var SIGNAL_POSTFIX = "Changed"; + + /** + * default value: true + */ + static inline var LAZY_SIGNAL = "lazySignal"; + /** + * default value: false + */ + static inline var INLINE_SIGNAL_GETTER = "inlineSignalGetter"; + + public function new() {} + + @:extern static inline function signalName(fieldName:String):String return fieldName + SIGNAL_POSTFIX; + @:extern static inline function signalGetterName(fieldName:String):String return "get_" + signalName(fieldName); + @:extern static inline function signalPrivateName(fieldName:String):String return "_" + signalName(fieldName); + + public function getFieldDispatcher(field:Field, res:Array):Void { + switch (field.kind) { + case FFun(_): + generateSignal(field, macro : bindx.BindSignal.MethodSignal, macro new bindx.BindSignal.MethodSignal(), res); + case FProp(_, _, type, _) | FVar(type, _): + generateSignal(field, macro : bindx.BindSignal.FieldSignal<$type>, macro new bindx.BindSignal.FieldSignal<$type>(), res); + } + } + + public function getFieldChangedExpr(field:Field, oldValue:Expr, newValue:Expr):Expr { + var args = switch (field.kind) { case FFun(_): []; case _: [oldValue, newValue]; } + return dispatchSignal(macro this, field.name, args, hasLazy(field.bindableMeta())); + } + + public function getClassFieldBindExpr(expr:Expr, field:ClassField, listener:Expr):Expr { + var signalName = signalName(field.name); + return macro @:privateAccess $expr.$signalName.add($listener); + } + + public function getClassFieldBindToExpr(expr:Expr, field:ClassField, target:Expr):Expr { + var signalName = signalName(field.name); + return switch (field.kind) { + case FMethod(_): + var fieldName = field.name; + macro @:privateAccess { + var listener = function () $target = $expr.$fieldName(); + $expr.$signalName.add(listener); + function __unbind__() $expr.$signalName.remove(listener); + } + case FVar(_, _): + var type = field.type.follow().toComplexType(); + macro @:privateAccess { + var listener = function (from:$type, to:$type) $target = to; + $expr.$signalName.add(listener); + function __unbind__() $expr.$signalName.remove(listener); + } + } + } + + public function getClassFieldUnbindExpr(expr:Expr, field:ClassField, listener:Expr):Expr { + var signalName = signalName(field.name); + return if (!listener.isNullOrEmpty()) + macro @:privateAccess $expr.$signalName.remove($listener); + else + macro @:privateAccess $expr.$signalName.removeAll(); + } + + public function getClassFieldChangedExpr(expr:Expr, field:ClassField, oldValue:Expr, newValue:Expr):Expr { + var args = switch (field.kind) { + case FMethod(_): + if (!oldValue.isNullOrEmpty()) + Context.error("method notify doesn't require oldValue", oldValue.pos); + if (!newValue.isNullOrEmpty()) + Context.error("method notify doesn't require newValue", newValue.pos); + []; + case FVar(_, _): + [oldValue, newValue]; + } + return dispatchSignal(expr, field.name, args, hasLazy(field.bindableMeta())); + } + + public function getUnbindAllExpr(expr:ExprOf, type:Type):Expr { + return macro bindx.BindSignal.SignalTools.unbindAll($expr); + } + + function generateSignal(field:Field, type:ComplexType, builder:Expr, res:Array):Void { + var signalName = signalName(field.name); + var meta = field.bindableMeta(); + var inlineSignalGetter = meta.findParam(INLINE_SIGNAL_GETTER); + + if (hasLazy(meta)) { + var signalPrivateName = signalPrivateName(field.name); + res.push({ + name: signalPrivateName, + kind: FVar(type, null), + pos: field.pos, + meta: [ { name:SignalTools.BIND_SIGNAL_META, pos:field.pos, params: [macro true] } ], + access: [APrivate] + }); + + res.push({ + name: signalName, + kind: FProp("get", "never", type, null), + pos: field.pos, + access: [APrivate], + }); + + var getter = macro function foo() { + if (this.$signalPrivateName == null) + this.$signalPrivateName = ${builder}; + return $i{signalPrivateName}; + }; + var getterAccess = [APrivate]; + if (inlineSignalGetter.isNotNullAndTrue()) getterAccess.push(AInline); + + res.push({ + name: signalGetterName(field.name), + kind: FFun(switch (getter.expr) { case EFunction (_, func): func; case _: throw false; }), + pos: field.pos, + access: getterAccess + }); + } else { + if (inlineSignalGetter != null) + Context.warning('$INLINE_SIGNAL_GETTER works only with lazy signals', inlineSignalGetter.pos); + + res.push({ + name: signalName, + kind: FProp("default", "null", type, builder), + pos: field.pos, + access: [APrivate], + meta: [ { name:SignalTools.BIND_SIGNAL_META, pos:field.pos, params: [macro false] } ] + }); + } + } + + inline function dispatchSignal(expr:Expr, fieldName:String, args:Array, lazy:Bool):Expr { + return + if (lazy) { + var signalPrivateName = signalPrivateName(fieldName); + macro @:privateAccess { + if ($expr.$signalPrivateName != null) + $expr.$signalPrivateName.dispatch($a { args } ); + } + } else { + var signalName = signalName(fieldName); + macro @:privateAccess $expr.$signalName.dispatch($a { args } ); + } + } + + @:extern inline function hasLazy(meta:MetadataEntry):Bool { + return meta.findParam(LAZY_SIGNAL).isNullOrTrue(); + } +} \ No newline at end of file diff --git a/src/bindx/BindMacros.hx b/src/bindx/macro/BindableMacros.hx similarity index 97% rename from src/bindx/BindMacros.hx rename to src/bindx/macro/BindableMacros.hx index 5b957e7..766ff68 100644 --- a/src/bindx/BindMacros.hx +++ b/src/bindx/macro/BindableMacros.hx @@ -1,19 +1,17 @@ -package bindx; +package bindx.macro; -import bindx.GenericError; +import bindx.macro.GenericError; import haxe.macro.Type; import haxe.macro.Expr; import haxe.macro.Context; -import haxe.macro.TypeTools; using haxe.macro.Tools; using Lambda; using StringTools; -using bindx.MetaUtils; -using haxe.macro.Tools; +using bindx.macro.MetaUtils; + +class BindableMacros { -class BindMacros { - #if macro static inline var OLD_VALUE = "__oldValue__"; static inline var NEW_VALUE = "__newValue__"; @@ -48,7 +46,7 @@ class BindMacros { }); if (bindingSignalProvider == null) { - bindingSignalProvider = new bindx.BindSignal.BindSignalProvider(); + bindingSignalProvider = new bindx.macro.BindSignalProvider(); } var fields = Context.getBuildFields(); @@ -240,5 +238,4 @@ class BindMacros { case _: true; } } - #end } \ No newline at end of file diff --git a/src/bindx/GenericError.hx b/src/bindx/macro/GenericError.hx similarity index 97% rename from src/bindx/GenericError.hx rename to src/bindx/macro/GenericError.hx index a37e2bf..573d5f3 100644 --- a/src/bindx/GenericError.hx +++ b/src/bindx/macro/GenericError.hx @@ -1,6 +1,4 @@ -package bindx; - -#if macro +package bindx.macro; import haxe.macro.Context; import haxe.macro.Expr.Position; @@ -50,5 +48,4 @@ class GenericError { public function contextFatal():Void { Context.fatalError(message, pos); } -} -#end \ No newline at end of file +} \ No newline at end of file diff --git a/src/bindx/IBindingSignalProvider.hx b/src/bindx/macro/IBindingSignalProvider.hx similarity index 95% rename from src/bindx/IBindingSignalProvider.hx rename to src/bindx/macro/IBindingSignalProvider.hx index 9fd4ac7..8a72f80 100644 --- a/src/bindx/IBindingSignalProvider.hx +++ b/src/bindx/macro/IBindingSignalProvider.hx @@ -1,6 +1,4 @@ -package bindx; - -#if macro +package bindx.macro; import haxe.macro.Expr; import haxe.macro.Type; @@ -15,5 +13,4 @@ interface IBindingSignalProvider { function getClassFieldUnbindExpr(expr:Expr, field:ClassField, listener:Expr):Expr; function getClassFieldChangedExpr(expr:Expr, field:ClassField, oldValue:Expr, newValue:Expr):Expr; function getUnbindAllExpr(expr:ExprOf, type:Type):Expr; -} -#end \ No newline at end of file +} \ No newline at end of file diff --git a/src/bindx/MetaUtils.hx b/src/bindx/macro/MetaUtils.hx similarity index 98% rename from src/bindx/MetaUtils.hx rename to src/bindx/macro/MetaUtils.hx index 5459d37..eb02fd4 100644 --- a/src/bindx/MetaUtils.hx +++ b/src/bindx/macro/MetaUtils.hx @@ -1,6 +1,5 @@ -package bindx; +package bindx.macro; -#if macro import haxe.macro.Type; import haxe.macro.Expr; import haxe.macro.Context; @@ -79,6 +78,4 @@ class ExprMetaUtils { static public inline function isNullOrTrue(expr:Expr):Bool return expr == null || isTrue(expr); -} - -#end \ No newline at end of file +} \ No newline at end of file diff --git a/test.hxml b/test.hxml index aeaa0ea..5366f2a 100644 --- a/test.hxml +++ b/test.hxml @@ -2,5 +2,5 @@ -cp src -cp test -D dump=pretty --neko bin/test.n --lib buddy \ No newline at end of file +--interp +-lib buddy