Macro context var names like "`"?

Hej my dear friends, long time I haven’t written anything in the forum ! :rofl:

For those who want to bleed from eyes, I have another thing to show, some stupid code playing with macros.
I have an old static extension called .ns() that acts like kind of “null safety”.
I can wrtie something like that and it works fine at run-time (here it will trace substitute because all the rest is null…) :

var o	: Dynamic = {};
 trace( ( o.foo.bar().sub:Dynamic ).ns( "substitute" ) );

I was using it for long time without any problem, but yesterday I wanted to try that, but it failed :

trace( ( [ for (i in 0...5) { o : i } ] [ 2 ].o:Dynamic).ns() );

So I made some modifications and came into that : Try Haxe !
Now it works great for that and I’ll stay with that until in the future I will have another needs.

BUT the problem is that when I made changes, I came into a strange issue when (on compile-time, macro context), Haxe tolds me that “`” is not a valid variable name, whearas this is not my var name but Haxe compiler one during the macro context… :
Because this code :

(o.foo.bar().sub : Dynamic)

Is transformed into that by Haxe :

(cast {
	var `:Array<{ var o : StdTypes.Int; }> = [];
	{
		{
			`.push({ o : 0 });
		};
		{
			`.push({ o : 1 });
		};
		{
			`.push({ o : 2 });
		};
		{
			`.push({ o : 3 });
		};
		{
			`.push({ o : 4 });
		};
	};
	`;
}[2].o)

As you can see the var name that Haxe gave to the array is “`”…

So to prevent that, I “rename” this kind of variables into a iteration in the expression as you can see L42 in the Macro class…

I know that I’m playing with hell getting back a @:this this typed expression into an untyped one, and It’s not the best thing to do, but anyway, is it normal that Haxe compiler give “`” as a name var please ?

Once more, thanks for all that take attention to my “adventures” :rofl:

Best,

Yes, backtick is used by the compiler for compiler-generated variables.

Thanks Aleksandr for the reply,

Is there a reason for that please ?
Is that kind of error I encouter only due to the fact that I’m getting back a @:this this typed expression into an untyped one and I should try to write this kind of macros in the “typed context” with “typed” things ?
Because that’s true that I’m often writting macros in the untyped macro context…

Thanks for your avdices !

The reason is to not shadow variables introduced manually by users.
And yes, you encounter it because of @:this this.

Because of @:this this exactly (so static extension) or because of Context.getTypedExpr( Context.typeExpr( expr ) ) ?
I think it’s more about getting back the typedExpr, but yes, playing with static extension, you MUST get it back and then be careful…
That’s pitty because static extension is really handy to shorten the code
Maybe having a special syntax for static extension something like myvar..ns() or I don’t know what, and then the compiler just textually replace that by ns( myvar ) would be good. But I know it’s easier to say than to implement, I don’t know how things go underground :stuck_out_tongue:

Or maybe o...ns() because it’s a valid Haxe syntax, and then a build macro that replace that with the right call…

I get it working : Try Haxe !

@:build( StaticExtension.build() )
class Test {
  static function main() {
    var o	= { foo : "bar" }
    trace( o.foo.test()...ns() );
  }
}

I don’t know if I’ll use it, I don’t know how it is expensive to use this kind of “syntax sugar” but it works, no need to use Context.getTypedExpr( Context.typeExpr( expr ) ) anymore :slight_smile:

I still don’t understand what are you trying to achieve, but I have a strong feeling of overengenineering about your solution :slight_smile:
Maybe if you describe the desired effect I could suggest you a solution without macros.
Sorry, I should’ve asked this in the beginning.

Hej Aleksandr,

Don’t worry and thanks for answering me !

You’re right. All that is kind of “overengenineering” because I want to use static extension with macros.
But using macros with static extensions gives expr as @:this and not the “dot-field” expr (i.e. my.obj.foo.bar). For example if I want to use ns() static function with static extension, I would do my.obj.foo.bar.ns()

So in order to get back the “dot-field” expr I do a haxe.macro.Context.getTypedExpr( haxe.macro.Context.typeExpr( expr ) ) but doing that I get something messed with haxe compiler internal var names and so on.

So I finally wrote a little macro (kind of syntax macro) that tranforms all !.into a static extension call like that : my.object.foo.bar!.ns()gives that : ns( my.obj.foo.bar ).

So at the end it acts like a native static extension and instead of getting @:thisi get the “dot-field” expr, not transformed …
I don’t know if it’s clear…
And for now it works just fine, I found (for my need) a trick to make working static extension with macros… And I haven’t meet for the moment any bug or strange behaviour…
Just to say that first I had ... as syntax, but this operator has lower priority so I had troubles with assignations or something like that. But now with the !. all works fine.

BTW, if you understand what I’ve done at the end if you can advice me of some cases that I haven’t taken in account and that could brings bugs it will be nice.

Anyway, thanks again for answering me, that’s nice !

My custom Static Extension buold macro :

package ftk.macro.build;

import haxe.ds.StringMap;
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

using haxe.macro.ExprTools;
#end

@:autoBuild( ftk.macro.StaticExtension.build() )
interface IStaticExtensionable{}

class StaticExtension{

	static var husings	: StringMap<StringMap<Array<String>>>	= new StringMap();

	// Init macros

	public static function init( pathFilter = "", recursive = false ){
		haxe.macro.Compiler.addGlobalMetadata( pathFilter, '@:build( ftk.macro.build.StaticExtension.build() )', recursive );
	}

	// Build macros

	public static function build(){
#if static_extension_build_trace
		trace( Context.getLocalType().getParameters()[ 0 ] );
#end
		var rcl	= Context.getLocalClass();
		if( rcl == null )	return null;
		var cl	= rcl.get();

		if( cl.isInterface )	return null;
		if( cl.kind.match( KAbstractImpl(_) ) )	return null;
		
		var cname	= cl.name;
		
		var sf		= new StringMap();
		husings.set( cl.pack.join( "." ) + "." + cname, sf );
		var usings	= Context.getLocalUsing();

		for( u in usings ){
			getStaticFunctions( u.get(), sf );
		}

		var fields	= Context.getBuildFields();

		for( field in fields ){
			switch field.kind{
				case FFun(f)	:
					if( f.expr != null ){
						checkExpr( f.expr, sf );
					}
				case _:
			}
		}
		return fields;
	}

#if macro
	static function getStaticFunctions( ct : haxe.macro.ClassType, h : StringMap<Array<String>> ){
		for( field in ct.statics.get() ){
			if( field.isPublic ){
				h.set( field.name, ct.pack.concat( [ ct.name, field.name ] ) );
			}
		}
	}

	// Check Static Extension operator "!."
	static function checkExpr( e : Expr, h : StringMap<Array<String>> ){
		switch e {
			case macro $b!.$c( $a{ p } )	:
				var se		= c.toString();
				var call	= h.get( se );
				if( call != null ){
					p.unshift( b );
					var ne	= ( macro $p{ call }( $a{ p } ) );
					checkExpr( ne, h );
					e.expr	= ne.expr;
				}
			case _:
				e.iter( checkExpr.bind(_,h) );
		}
	}
#end

}

Oh, If your goal is to use macros as static extensions, then you have to deal with @:this.
But if your goal is (for example) to check an expression for null, then there’s probably an easier way to do so.

Yes my goal is to use macro as static extensions and yes with “native static extension” I have to deal with @:this BUT with the custom static extension that I’ve written, I don’t have to deal with @:this if you take a look here : Try Haxe ! (take a look at Compiler output)

And all works fine now for my needs.

The last question/advice I wanted to know is if my custom static extension can’t bring me anothers bugs (that I haven’t encountered for now, but…)

Sorry, I don’t want to dive into a big macro code :slight_smile:

Oh hey, that’s exactly what I came here looking for. Tried searching the forums for “@:this this,” but found nothing due to the special characters. Then I went and found my own solution, and then I stumbled across this post. Oh well.

Might as well note my solution for posterity. Context.typeExpr(), then TypedExprTools.toString(t, true), then Context.parse(). It’s a bit roundabout, but you could edit the string before parsing if you wanted.

Myself, I think I’ll go with getTypedExpr(), like you did.

1 Like

Oh God, someone with the same problem I had !!! :joy:
Your solution with Context.parse() is very nice because I think you’ll not fall into the same problem I encoutenred dealing with some compiler internal var names like the “`” and so on.
Just because this will be a new untyped yet expression. I haven’t tried it yet but I think so.

I just tried and I thought your solution will work better but it just does the same as mine (maybe worse because you loose types) : Try Haxe ! (take a look at compiler output)

That’s why I fall into into my solution of “custom static extension” as I described here : Try Haxe ! (take a look at compiler output)

This way you get the right untyped expression so you haven’t to deal with @:this and others typd expressions anymore.

I didn’t expect this to perform better, just differently.

And my problem isn’t exactly the same. I’m trying to do a @:from macro, not a static extension, and I don’t think build macros would help. Glad they worked for you, though!