Closed filt3rek closed 4 years ago
Given macros exist at compile time only, I don't think that is possible to implement.
I don't understand, yes it's supposed to be in compile-time.
Because in your sample:
var myHtmlFile = new HtmlFile( "path/to/my/template.mtt" );
var myElement1 = myHtmlFile.getElementById( "myId1" );
var myElement2 = myHtmlFile.getElementById( "myId2" );
var myElement3 = myHtmlFile.getElementById( "myId3" );
myHtmlFile
is defined and exists at run time.
Yes it looks like, but in fact I don't want it to exist at runtime, only compiletime.
If you look at the sample I wrote, getElementById()
method is a macro method that generates a real js.Browser.document.getElementById()
call for runtime if it find the id at compiletime or throws a compiletime error if the id doesn't exist.
So here, neither var myHtmlFile = new HtmlFile( "path/to/my/template.mtt" );
and var myElement1 = myHtmlFile.getElementById( "myId1" );
don't need to exist at runtime, only compiletime...
i don't know if it's clear :/
The only difference between a static method, that I put in the sample too and which works, and a "new one using macro instance", would be that I give the path to the Html file in the macro instance constructor and then I call the macro instance method giving it only the id, and not the path + the id...
Then what would happen if a user do this?
var myHtmlFile = new HtmlFile( "path/to/my/template.mtt" );
var anotherVar = myHtmlFile;
anotherVar.getElementById( "myId1" );
What if myHtmlFile
was passed to an argument of a function?
I think it's to much hassle for avoiding static caches.
I think myHtmlFile should be a "macro type", so user couldn't assign a "macro type" to a "run-time" type, something like that... Maybe Kevin should come to this discussion because he wrote that technically it could be done.
For your example, I found two ways to achieve it:
The first:
import HtmlFile.home;
import HtmlFile.cart;
class Main {
static function main() {
var header = home("#header");
var menu = cart(".menu");
}
}
class HtmlFile {
#if macro
var ids : Array<String>;
function new( path : String ) {
}
function querySelector( css : String ) {
return macro js.Browser.document.querySelector( $v{ css } );
}
static function _query( path : String, css : String ) {
var hf = map.get(path);
if (hf == null) {
hf = new HtmlFile(path);
map.set(path, hf);
}
return hf.querySelector(css);
}
@:persistent static var map = new Map<String, HtmlFile>();
#end
macro public static function home( css : String ) {
return _query("home.html", css);
}
macro public static function cart( css : String ) {
return _query("cart.html", css);
}
}
The second: uses @:genericBuild
// main.hx
// maybe "@:eager" is not necessary
@:eager typedef Home = HtmlFileBuild<"home.html">;
@:eager typedef Cart = HtmlFileBuild<"cart.html">;
class Main {
static function main() {
var header = Home.query("#header");
var menu = Cart.query(".menu");
}
}
// HtmlFile.hx
@:genericBuild(HtmlFile.build())
class HtmlFile<Const> {
#if macro
public static function build() {
// ......
}
#end
}
Hej @R32 ,
Thanks for your reply !
Your first example, it's not very generic, as a instance could be, because you have to modify the HtmlFile to implement the templates by yourself.
The second example could be good and near to the behaviour a macro instance could have even if the type params could not take any type of arguments, or they would be tricky to implement.
I think It's like in haxe.xml.Proxy<T,T2>
It's a bit harder to implement and to use :
#if macro
import haxe.macro.Context;
import sys.io.File;
import haxe.macro.Expr;
#end
#if !macro
@:genericBuild( HtmlFileBuilder.build() )
#end
class HtmlFile<Const>{}
class HtmlFileBuilder{
#if macro
public static function build() : ComplexType{
var spath = switch Context.getLocalType(){
case TInst(_, [TInst(t, _)]):
t.toString().substr( 1 );
case _ : null;
}
if( spath == null ){
Context.fatalError( 'Const type parameter needed', Context.currentPos() );
}
var cfPath = Sys.getCwd() + Context.getPosInfos( Context.currentPos() ).file;
var path = new haxe.io.Path( cfPath );
var content = null;
try{
content = File.getContent( path.dir + "/" + spath );
}catch( e : Dynamic ){
Context.fatalError( 'Can\'t find file "$spath"', Context.currentPos() );
}
var regex = ~/id=["||']([a-z0-9_-]+)["||']/igm;
var s = content;
var ae = [];
while (regex.match( s ) ){
var name = regex.matched( 1 );
ae.push( macro $v{ name } );
s = regex.matchedRight();
}
var cname = 'HtmlFile_S${ spath.split( "/" ).join( "_" ).split( "." ).join( "_" ) }';
var cl = macro class $cname{
static var ids = $a{ ae };
macro public static function getElementById( eid : haxe.macro.Expr ){
var id = switch eid.expr {
case haxe.macro.Expr.ExprDef.EConst( CString(s) ) : s;
case _ : null;
}
if( ids.indexOf( id ) == -1 ){
haxe.macro.Context.fatalError( 'id "'+ id +'" doesn\'t exist in "$spath"', haxe.macro.Context.currentPos() );
return null;
}else{
return macro js.Browser.document.getElementById( $eid );
}
}
}
Context.defineType( cl );
return Context.toComplexType( Context.getType( cname ) );
}
#end
}
We have decided to reject this proposal in our haxe-evolution meeting yesterday.
It is unclear what problem you're trying to solve here and the suggestion raises more questions than it answers. Please present the actual issue you're having as a forum post over at http://community.haxe.org so it can be discussed.
Hej !
I'm sorry, I didn't know how a proposal work, at the begining I just wanted to ask simply if something like that could be done without much work. Then now, I followed the proposal process, I hope all is filled right and I do all the right way...
rendered version
Thanks for your interest, Regards, Michal