diff --git a/src/bindx/Bind.hx b/src/bindx/Bind.hx index 82dd3df..6ce7f8e 100644 --- a/src/bindx/Bind.hx +++ b/src/bindx/Bind.hx @@ -38,20 +38,23 @@ class Bind { #if macro public static function internalBind(field:Expr, listener:Expr, doBind:Bool):Expr { - var fieldData = checkField(field); + var fieldData = warnCheckField(field); return if (fieldData != null) { if (doBind) BindMacros.bindingSignalProvider.getClassFieldBindExpr(fieldData.e, fieldData.field, listener); else BindMacros.bindingSignalProvider.getClassFieldUnbindExpr(fieldData.e, fieldData.field, listener); - } else macro {}; + } else { + Context.fatalError('can\'t bind field ${field.expr}', field.pos); + macro {}; + } } public static function internalBindTo(field:Expr, target:Expr):Expr { - var fieldData = checkField(field); + var fieldData = warnCheckField(field); return BindMacros.bindingSignalProvider.getClassFieldBindToExpr(fieldData.e, fieldData.field, target); } public static function internalNotify(field:Expr, ?oldValue:Expr, ?newValue:Expr):Expr { - var fieldData = checkField(field); + var fieldData = warnCheckField(field); return BindMacros.bindingSignalProvider.getClassFieldChangedExpr(fieldData.e, fieldData.field, oldValue, newValue); } @@ -63,19 +66,20 @@ class Bind { return BindMacros.bindingSignalProvider.getUnbindAllExpr(object, type); } - public static function checkField(field:Expr):{e:Expr, field:ClassField} { + static function warnCheckField(field:Expr):{e:Expr, field:ClassField} { var res = null; try { - res = tryCheckField(field); - if (res.error != null) - res.error.contextError(); + res = checkField(field); + if (res.error != null) throw res.error; + } catch (e:FatalError) { + e.contextFatal(); } catch (e:bindx.Error) { e.contextError(); }; return res; } - public static function tryCheckField(field:Expr):{e:Expr, field:ClassField, error:bindx.Error} { + public static function checkField(field:Expr):{e:Expr, field:ClassField, error:bindx.Error} { var error:bindx.Error; switch (field.expr) { case EField(e, field): @@ -101,12 +105,11 @@ class Bind { return {e:e, field:field, error:error}; case EConst(CIdent(_)): - return {e:field, field:null, error:new bindx.Error('can\'t bind \'${field.toString()}\'. Please use \'this.${field.toString()}\'', field.pos)}; + return {e:field, field:null, error:new bindx.Error('Can\'t bind \'${field.toString()}\'. Please use \'this.${field.toString()}\'', field.pos)}; case _: - return {e:field, field:null, error:new bindx.Error('can\'t bind field \'${field.toString()}\'', field.pos)}; } - return null; + return {e:field, field:null, error:new bindx.Error('Can\'t bind field \'${field.toString()}\'', field.pos)}; } public static function isBindable(classType:ClassType):Bool { diff --git a/src/bindx/BindxExt.hx b/src/bindx/BindxExt.hx index 6d2939b..0a18e88 100644 --- a/src/bindx/BindxExt.hx +++ b/src/bindx/BindxExt.hx @@ -40,7 +40,8 @@ class BindExt { public static function internalBindChain(expr:Expr, listener:Expr):Expr { var zeroListener = listenerName(0, ""); - var chain = prepareChain(expr, macro $i{ zeroListener }); + var chain = null; + try { chain = warnPrepareChain(expr, macro $i{ zeroListener }); } catch (e:bindx.Error) e.contextError(); var res = macro (function ($zeroListener):Void->Void $b { chain.bind.concat(chain.init).concat([macro return function ():Void $b { chain.unbind }]) } @@ -85,10 +86,13 @@ class BindExt { var zeroListener = listenerName(0, pre); chain.init.push(macro var $zeroListener = ${ecall ? methodListenerNameExpr : fieldListenerNameExpr}); - var c = prepareChain(start, macro $i{zeroListener}, pre); - chain.init = chain.init.concat(c.init); - chain.bind = chain.bind.concat(c.bind); - chain.unbind = chain.unbind.concat(c.unbind); + var c = null; + try { c = warnPrepareChain(start, macro $i{zeroListener}, pre); } catch (e:bindx.Error) { e.contextWarning(); } + if (c != null) { + chain.init = chain.init.concat(c.init); + chain.bind = chain.bind.concat(c.bind); + chain.unbind = chain.unbind.concat(c.unbind); + } } e.iter(findChain); } @@ -119,23 +123,16 @@ class BindExt { var prevField = {e:first.e, field:first.field, error:null}; var fields:Array = [ { field:first.field, bindable:true, e:first.e } ]; - inline function tryCheck(e:Expr) { - var field = null; - try { field = Bind.tryCheckField(e); } catch (e:FatalError) e.contextFatal(); - return field; - } - while (true) { - var field = tryCheck(prevField.e); + var field = Bind.checkField(prevField.e); if (field.field != null) { fields.push( { field:field.field, bindable:field.error == null, e:field.e } ); } else if (field.error != null) { var end = true; switch (prevField.e.expr) { case ECall(e, params): - field = tryCheck(e); - if (field.error != null) field.error.contextError(); - if (field.field == null) Context.fatalError('error parse fields ${e.toString()}', e.pos); + field = Bind.checkField(e); + if (field.field == null) throw new FatalError('error parse fields ${e.toString()}', e.pos); fields.push( { e:field.e, field:field.field, params:params, bindable:field.error == null } ); end = false; case _: @@ -148,11 +145,13 @@ class BindExt { return fields; } - static function prepareChain(expr:Expr, listener:Expr, prefix = ""):Chain { + static function warnPrepareChain(expr:Expr, listener:Expr, prefix = ""):Chain { var fields = checkFields(expr); - if (fields.length == 0) - Context.fatalError("can't bind empty expression", expr.pos); + if (fields.length == 0) { + throw new FatalError("can't bind empty expression: " + expr.toString(), expr.pos); + return null; + } var i = fields.length; var first = null; @@ -161,15 +160,20 @@ class BindExt { if (first != null) f.bindable = false; else if (!f.bindable && first == null) first = f; } + var bindableNum = fields.fold(function (it, n) return n += it.bindable ? 1 : 0, 0); + if (bindableNum == 0) { + throw new bindx.Error('expr is not bindable "${expr.toString()}".', expr.pos); + return null; + } if (first != null) - Context.warning('expr in not full bindable. Can bind only "${first.e.toString()}"', expr.pos); + Context.warning('expr is not full bindable. Can bind only "${first.e.toString()}"', expr.pos); - return prepareBindChain(fields, macro listener, expr.pos, prefix); + return prepareChain(fields, macro listener, expr.pos, prefix); } inline static function listenerName(idx:Int, prefix) return '${prefix}listener$idx'; - public static function prepareBindChain(fields:Array, listener:Expr, pos:Position, prefix = ""):Chain { + static function prepareChain(fields:Array, listener:Expr, pos:Position, prefix = ""):Chain { var res:Chain = { init:[], bind:[], unbind:[] }; var prevListenerName = listenerName(0, prefix); diff --git a/test/ExprBindTest.hx b/test/ExprBindTest.hx index 79ab06b..886a57f 100644 --- a/test/ExprBindTest.hx +++ b/test/ExprBindTest.hx @@ -24,9 +24,9 @@ class ExprBindTest extends BuddySuite { var b = new BaseTest.Bindable1(); a.str = "a1"; b.str = "b1"; - inline function val() return a.str + b.str; + inline function val() return a.str + b.str + "ab".charAt(0); - BindExt.expr(a.str + b.str, function (from, to:String) { + BindExt.expr(a.str + b.str + "ab".charAt(0), function (from, to:String) { //trace(from); to.should.be(val()); callNum ++; @@ -37,6 +37,10 @@ class ExprBindTest extends BuddySuite { a.str = "a2"; callNum.should.be(2); + + b.str = "b2"; + + callNum.should.be(3); }); });