chain bind WIP
This commit is contained in:
+16
-9
@@ -64,10 +64,19 @@ class Bind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function checkField(field:Expr):{e:Expr, field:ClassField} {
|
public static function checkField(field:Expr):{e:Expr, field:ClassField} {
|
||||||
return try { tryCheckField(field); } catch (e:bindx.Error) { e.contextError(); null; };
|
var res = null;
|
||||||
|
try {
|
||||||
|
res = tryCheckField(field);
|
||||||
|
if (res.error != null)
|
||||||
|
res.error.contextError();
|
||||||
|
} catch (e:bindx.Error) {
|
||||||
|
e.contextError();
|
||||||
|
};
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tryCheckField(field:Expr):{e:Expr, field:ClassField} {
|
public static function tryCheckField(field:Expr):{e:Expr, field:ClassField, error:bindx.Error} {
|
||||||
|
var error:bindx.Error;
|
||||||
switch (field.expr) {
|
switch (field.expr) {
|
||||||
case EField(e, field):
|
case EField(e, field):
|
||||||
var classType = Context.typeof(e).follow().getClass();
|
var classType = Context.typeof(e).follow().getClass();
|
||||||
@@ -76,8 +85,7 @@ class Bind {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isBindable(classType)) {
|
if (!isBindable(classType)) {
|
||||||
throw new Error('\'${e.toString()}\' must be bindx.IBindable', e.pos);
|
error = new bindx.Error('\'${e.toString()}\' must be bindx.IBindable', e.pos);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var field:ClassField = classType.findField(field, null);
|
var field:ClassField = classType.findField(field, null);
|
||||||
@@ -87,17 +95,16 @@ class Bind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!field.hasBindableMeta()) {
|
if (!field.hasBindableMeta()) {
|
||||||
throw new Error('\'${e.toString()}.${field.name}\' is not bindable', field.pos);
|
error = new bindx.Error('\'${e.toString()}.${field.name}\' is not bindable', field.pos);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {e:e, field:field};
|
return {e:e, field:field, error:error};
|
||||||
|
|
||||||
case EConst(CIdent(_)):
|
case EConst(CIdent(_)):
|
||||||
throw new 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 _:
|
case _:
|
||||||
throw new Error('can\'t bind field \'${field.toString()}\'', field.pos);
|
return {e:field, field:null, error:new bindx.Error('can\'t bind field \'${field.toString()}\'', field.pos)};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
package bindx;
|
|
||||||
|
|
||||||
#if macro
|
|
||||||
|
|
||||||
import bindx.Error;
|
|
||||||
import haxe.macro.Expr;
|
|
||||||
|
|
||||||
#end
|
|
||||||
class Bindx {
|
|
||||||
|
|
||||||
@:noUsing macro static public function bindChain(expr:Expr, listener:Expr):Expr {
|
|
||||||
return internalBindChain(expr, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if macro
|
|
||||||
static function internalBindChain(expr:Expr, listener:Expr):Expr {
|
|
||||||
var prevField = Bind.checkField(expr);
|
|
||||||
var field = prevField;
|
|
||||||
var fields = [];
|
|
||||||
var rest:Expr;
|
|
||||||
while (field != null) {
|
|
||||||
try {
|
|
||||||
field = Bind.tryCheckField(prevField.expr);
|
|
||||||
fields.push(field.field);
|
|
||||||
rest = field.expr;
|
|
||||||
}
|
|
||||||
catch (e:FatalError) {
|
|
||||||
e.contextFatal();
|
|
||||||
}
|
|
||||||
catch (e:Error) {
|
|
||||||
switch (prevField.expr) {
|
|
||||||
case EField(e, field):
|
|
||||||
var classType = Context.typeof(e).follow().getClass();
|
|
||||||
var field:ClassField = classType.findField(field, null);
|
|
||||||
fields.push(field);
|
|
||||||
rest = e;
|
|
||||||
case EConst(CIdent(_)):
|
|
||||||
rest = prevField.expr;
|
|
||||||
case _:
|
|
||||||
rest = prevField.expr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prevField = field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package bindx;
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
|
||||||
|
|
||||||
|
import bindx.Error;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type;
|
||||||
|
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
typedef FieldExpr = {
|
||||||
|
var field:ClassField;
|
||||||
|
var bindable:Bool;
|
||||||
|
var e:Expr;
|
||||||
|
@:optional var params:Array<Expr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#end
|
||||||
|
@:access(bindx.BindMacros)
|
||||||
|
class BindExt {
|
||||||
|
|
||||||
|
@:noUsing macro static public function chain(expr:Expr, listener:Expr):Expr {
|
||||||
|
return internalBindChain(expr, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
static function internalBindChain(expr:Expr, listener:Expr):Expr {
|
||||||
|
var printer = new haxe.macro.Printer();
|
||||||
|
var first = Bind.checkField(expr);
|
||||||
|
var prevField = {e:first.e, field:first.field, error:null};
|
||||||
|
var fields:Array<FieldExpr> = [{field:first.field, bindable:true, e:first.e}];
|
||||||
|
while (true) {
|
||||||
|
var field = null;
|
||||||
|
try {
|
||||||
|
field = Bind.tryCheckField(prevField.e);
|
||||||
|
if (field.field != null)
|
||||||
|
fields.unshift({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 = Bind.tryCheckField(e);
|
||||||
|
fields.unshift({e:field.e, field:field.field, params:params, bindable:field.error == null});
|
||||||
|
end = false;
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
if (end) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e:FatalError) {
|
||||||
|
e.contextFatal();
|
||||||
|
}
|
||||||
|
prevField = field;
|
||||||
|
}
|
||||||
|
fields.iter(function (it) trace(printer.printExpr(it.e) + " -> " + it.field.name + (it.params != null ? '(${printer.printExprs(it.params, ",")})' : "") + " bind:" + it.bindable));
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
|
||||||
|
var res = [];
|
||||||
|
for (field in fields) {
|
||||||
|
var listenerName = 'listener$i';
|
||||||
|
if (field.bindable) {
|
||||||
|
var listener = switch (field.field.kind) {
|
||||||
|
case FVar(_, _):
|
||||||
|
macro function (from, to) {
|
||||||
|
$listener($a{[from, to]});
|
||||||
|
}
|
||||||
|
case FMethod(_):
|
||||||
|
macro function() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.push(macro var $listenerName = $listener);
|
||||||
|
var expr = BindMacros.bindingSignalProvider.getClassFieldBindExpr(field.e, field.field, listener = macro $i{listenerName});
|
||||||
|
trace(printer.printExpr(expr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
trace(printer.printExpr(field.e) + " . " + field.field.name);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return macro {};
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package ;
|
||||||
|
|
||||||
|
import bindx.Bind;
|
||||||
|
import bindx.BindxExt;
|
||||||
|
import bindx.IBindable;
|
||||||
|
import buddy.BuddySuite;
|
||||||
|
|
||||||
|
using buddy.Should;
|
||||||
|
|
||||||
|
//@exclude
|
||||||
|
class ChainBindTest extends BuddySuite {
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
|
||||||
|
describe("Using BindExt.chain", {
|
||||||
|
|
||||||
|
var b:BindableChain;
|
||||||
|
var callNum:Int;
|
||||||
|
|
||||||
|
before({
|
||||||
|
b = new BindableChain();
|
||||||
|
b.c = new BindableChain();
|
||||||
|
callNum = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("BindExt.chain should bind chain changes", {
|
||||||
|
b.c.f("tada").d = "a";
|
||||||
|
BindExt.chain(b.c.f("tada").d, function (_, _) callNum++);
|
||||||
|
|
||||||
|
b.c.f("tada").d = "b";
|
||||||
|
callNum.should.be(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BindableChain implements bindx.IBindable {
|
||||||
|
|
||||||
|
@:bindable
|
||||||
|
public var d:String;
|
||||||
|
|
||||||
|
public var nd:String;
|
||||||
|
|
||||||
|
public var c:BindableChain;
|
||||||
|
|
||||||
|
@:bindable
|
||||||
|
public function f(s:String):BindableChain {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-2
@@ -1,6 +1,25 @@
|
|||||||
package ;
|
package ;
|
||||||
|
|
||||||
import buddy.BuddySuite;
|
import buddy.BuddySuite;
|
||||||
|
import buddy.SuitesRunner;
|
||||||
|
|
||||||
@:build(buddy.GenerateMain.build(null, ["test"]))
|
class Tests extends BuddySuite {
|
||||||
class Tests extends BuddySuite {}
|
|
||||||
|
public static function main() {
|
||||||
|
var reporter = new buddy.reporting.TravisHxReporter();
|
||||||
|
|
||||||
|
var runner = new SuitesRunner([
|
||||||
|
new BaseTest(),
|
||||||
|
new InheritanceTest(),
|
||||||
|
new InlineTest(),
|
||||||
|
new MetaTest(),
|
||||||
|
new SignalTest(),
|
||||||
|
new TestProperty(),
|
||||||
|
// new ChainBindTest(),
|
||||||
|
], reporter);
|
||||||
|
|
||||||
|
runner.run();
|
||||||
|
|
||||||
|
return runner.statusCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user