chain bind WIP

This commit is contained in:
Dima Granetchi
2014-11-18 20:02:52 +02:00
parent 910f8f0d6f
commit 42aabddec3
5 changed files with 180 additions and 59 deletions
+16 -9
View File
@@ -64,10 +64,19 @@ class Bind {
}
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) {
case EField(e, field):
var classType = Context.typeof(e).follow().getClass();
@@ -76,8 +85,7 @@ class Bind {
return null;
}
if (!isBindable(classType)) {
throw new Error('\'${e.toString()}\' must be bindx.IBindable', e.pos);
return null;
error = new bindx.Error('\'${e.toString()}\' must be bindx.IBindable', e.pos);
}
var field:ClassField = classType.findField(field, null);
@@ -87,17 +95,16 @@ class Bind {
}
if (!field.hasBindableMeta()) {
throw new Error('\'${e.toString()}.${field.name}\' is not bindable', field.pos);
return null;
error = new bindx.Error('\'${e.toString()}.${field.name}\' is not bindable', field.pos);
}
return {e:e, field:field};
return {e:e, field:field, error:error};
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 _:
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;
}
-48
View File
@@ -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
}
+88
View File
@@ -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
}
+55
View File
@@ -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
View File
@@ -1,6 +1,25 @@
package ;
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();
}
}