From 9739d7dc924b85a0a50f3a0729ffe4f7cd07029e Mon Sep 17 00:00:00 2001 From: Reto Stuber Date: Tue, 29 Jun 2021 14:40:23 +0200 Subject: [PATCH 1/3] added experimental @bind support --- domkit/Macros.hx | 97 ++++++++++++++++++++++++++++++++++++++++++++++-- sample/Test.hx | 34 +++++++++++++++++ 2 files changed, 127 insertions(+), 4 deletions(-) diff --git a/domkit/Macros.hx b/domkit/Macros.hx index 840fc50..54732b5 100644 --- a/domkit/Macros.hx +++ b/domkit/Macros.hx @@ -2,8 +2,11 @@ package domkit; #if macro import haxe.macro.Context; import haxe.macro.Expr; +import haxe.macro.Type; import domkit.Error; + using haxe.macro.Tools; +using haxe.macro.ExprTools; typedef ComponentData = { var declaredIds : Map; @@ -104,9 +107,42 @@ class Macros { } } + static function remapBind( rootExpr : haxe.macro.Expr ) { + var isBound = false; + var bname:String = null; + function _remap(e : haxe.macro.Expr) { + switch( e.expr ) { + case EMeta(m, group) if( m.name == "bind" ): + if(m.params.length > 0) + switch(m.params[0].expr) { + case EConst(CIdent(name)): + bname = name; + default: + } + isBound = true; + _remap(group); + e.expr = group.expr; + return; + case EConst(CIdent(name)) if(isBound): + e.expr = ((macro domkit.Macros.bindVar($i{name})).expr); + return; + case EField(obj, name) if(isBound): + e.expr = ((macro domkit.Macros.bindVar($obj.$name)).expr); + return; + default: + } + e.iter(_remap); + } + _remap(rootExpr); + return { + isBound: isBound, + name: bname + }; + } + static var latestBlock : Array = null; - static function remapBuild( e : haxe.macro.Expr ) { + static function remapBuild( e : haxe.macro.Expr) { switch( e.expr ) { case EMeta(m, expr) if( m.name == "rebuild" ): switch( expr.expr ) { @@ -261,10 +297,22 @@ class Macros { } else error("Unknown property "+comp.name+"."+p.name, attr.vmin, attr.pmax); } else { - aexprs.push(macro var __attrib = $e); + var binding = remapBind(e); var eattrib = { expr : EConst(CIdent("__attrib")), pos : e.pos }; - aexprs.push({ expr : EMeta({ pos : e.pos, name : ":privateAccess" }, { expr : ECall(withPos(eset,e.pos),[macro cast tmp.obj,eattrib]), pos : e.pos }), pos : e.pos }); - aexprs.push(macro @:privateAccess tmp.initStyle($v{p.name},$eattrib)); + var setter = { expr : ECall(withPos(eset,e.pos),[macro cast tmp.obj,eattrib]), pos : e.pos }; + aexprs.push(macro { + function __onVarChanged() { + var __attrib = $e; + @:privateAccess $setter; + @:privateAccess tmp.initStyle($v{p.name},$eattrib); + } + $e{ + if(binding.isBound) + macro registerBind(__onVarChanged, $v{binding.name}) + else macro {} + } + __onVarChanged(); + }); } } } @@ -596,4 +644,45 @@ class Macros { #end + public static macro function bindVar(e : haxe.macro.Expr) : haxe.macro.Expr { + switch(Context.typeof(e)) { + case TInst(_): + return e; + case TFun(args,ret): + if(args.length == 1) { + function matchCallback(t:Type) { + return switch(t) { + // Void->Void callback + case TFun([], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])): + return macro { + $e(__onVarChanged); + }; + // (newValue:T)->Void callback + case TFun([{ t : argType }], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])) if (Context.unify(argType,ret)): + return macro { + $e(v -> __onVarChanged()); + }; + // (newValue:T, oldValue:T)->Void callback + case TFun([{ t : argType }, { t : arg2Type }], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])) if (Context.unify(argType,ret) && Context.unify(arg2Type,ret)): + return macro { + $e((v1,v2) -> __onVarChanged()); + }; + default: null; + } + } + var t = args[0].t; + var expr = switch(t) { + case TAbstract(_,[func]): + matchCallback(func); + default: + matchCallback(t); + }; + if(expr != null) + return expr; + } + default: + } + throw "Unsupported callback type used with @bind"; + } + } diff --git a/sample/Test.hx b/sample/Test.hx index 456fa86..60111dc 100644 --- a/sample/Test.hx +++ b/sample/Test.hx @@ -36,6 +36,36 @@ class SyntaxTest extends Components.BaseComponent { } +class BindingTest extends Components.BaseComponent { + + static var SRC = + + + + var labelPrefix = "Hello"; + var labelText(default, set) = "My Placeholder"; + var binding:Void->Void; + + public function new(?parent) { + super(parent); + var arr = [1,2,3]; + initComponent(); + labelText = "My Label"; + } + function set_labelText(v) { + labelText = v; + if(binding != null) binding(); + return v; + } + function myText(cb:Void->Void):String { + binding = cb; + return labelText; + } + function registerBind(cb, name) { + trace("Registered binding of name: "+name); + } +} + class Test { public static var exampleText = "Hello World"; @@ -60,6 +90,10 @@ class Test { trace(o.sub.maxWidth); // null + + var o = new BindingTest(); + trace( cast(o.getChildren()[0],Components.TextComponent).text ); + } } \ No newline at end of file From b03ccc8217d19cffeed4b4fcaa1fc2fa413c1162 Mon Sep 17 00:00:00 2001 From: Reto Stuber Date: Thu, 1 Jul 2021 20:51:17 +0200 Subject: [PATCH 2/3] - removed default logic for bind (now implemented by user through `processBind` macro function) - moved bind macro call outside of re-evaluation callback --- domkit/Macros.hx | 55 ++++++++++++------------------------------------ 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/domkit/Macros.hx b/domkit/Macros.hx index 54732b5..042d0a5 100644 --- a/domkit/Macros.hx +++ b/domkit/Macros.hx @@ -30,6 +30,10 @@ class Macros { public static dynamic function processMacro( id : String, args : Null>, pos : haxe.macro.Expr.Position ) : MarkupParser.Markup { return null; } + + public static dynamic function processBind( e : haxe.macro.Expr ) : haxe.macro.Expr { + return null; + } public static function registerComponentsPath( path : String ) { if( componentsSearchPath.indexOf(path) < 0 ) @@ -110,6 +114,7 @@ class Macros { static function remapBind( rootExpr : haxe.macro.Expr ) { var isBound = false; var bname:String = null; + var bindExprs:Array = []; function _remap(e : haxe.macro.Expr) { switch( e.expr ) { case EMeta(m, group) if( m.name == "bind" ): @@ -124,10 +129,12 @@ class Macros { e.expr = group.expr; return; case EConst(CIdent(name)) if(isBound): - e.expr = ((macro domkit.Macros.bindVar($i{name})).expr); + var b = macro domkit.Macros.bindVar($i{name}); + bindExprs.push(b); return; case EField(obj, name) if(isBound): - e.expr = ((macro domkit.Macros.bindVar($obj.$name)).expr); + var b = macro domkit.Macros.bindVar($obj.$name); + bindExprs.push(b); return; default: } @@ -136,7 +143,8 @@ class Macros { _remap(rootExpr); return { isBound: isBound, - name: bname + name: bname, + exprs: bindExprs }; } @@ -306,6 +314,7 @@ class Macros { @:privateAccess $setter; @:privateAccess tmp.initStyle($v{p.name},$eattrib); } + $b{binding.exprs}; $e{ if(binding.isBound) macro registerBind(__onVarChanged, $v{binding.name}) @@ -645,44 +654,6 @@ class Macros { #end public static macro function bindVar(e : haxe.macro.Expr) : haxe.macro.Expr { - switch(Context.typeof(e)) { - case TInst(_): - return e; - case TFun(args,ret): - if(args.length == 1) { - function matchCallback(t:Type) { - return switch(t) { - // Void->Void callback - case TFun([], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])): - return macro { - $e(__onVarChanged); - }; - // (newValue:T)->Void callback - case TFun([{ t : argType }], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])) if (Context.unify(argType,ret)): - return macro { - $e(v -> __onVarChanged()); - }; - // (newValue:T, oldValue:T)->Void callback - case TFun([{ t : argType }, { t : arg2Type }], TAbstract(_.get() => {module: "StdTypes", name: "Void"}, [])) if (Context.unify(argType,ret) && Context.unify(arg2Type,ret)): - return macro { - $e((v1,v2) -> __onVarChanged()); - }; - default: null; - } - } - var t = args[0].t; - var expr = switch(t) { - case TAbstract(_,[func]): - matchCallback(func); - default: - matchCallback(t); - }; - if(expr != null) - return expr; - } - default: - } - throw "Unsupported callback type used with @bind"; + return processBind(e); } - } From a1adfccc5182d386105f7b2aa45a7202cecfa272 Mon Sep 17 00:00:00 2001 From: Reto Stuber Date: Thu, 1 Jul 2021 20:53:30 +0200 Subject: [PATCH 3/3] removed test sample --- sample/Test.hx | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/sample/Test.hx b/sample/Test.hx index 60111dc..456fa86 100644 --- a/sample/Test.hx +++ b/sample/Test.hx @@ -36,36 +36,6 @@ class SyntaxTest extends Components.BaseComponent { } -class BindingTest extends Components.BaseComponent { - - static var SRC = - - - - var labelPrefix = "Hello"; - var labelText(default, set) = "My Placeholder"; - var binding:Void->Void; - - public function new(?parent) { - super(parent); - var arr = [1,2,3]; - initComponent(); - labelText = "My Label"; - } - function set_labelText(v) { - labelText = v; - if(binding != null) binding(); - return v; - } - function myText(cb:Void->Void):String { - binding = cb; - return labelText; - } - function registerBind(cb, name) { - trace("Registered binding of name: "+name); - } -} - class Test { public static var exampleText = "Hello World"; @@ -90,10 +60,6 @@ class Test { trace(o.sub.maxWidth); // null - - var o = new BindingTest(); - trace( cast(o.getChildren()[0],Components.TextComponent).text ); - } } \ No newline at end of file