Macro for runtime bool evaluation?

In an attempt to level up my very basic Haxe macro skills, I want to find a macro solution where I can replace the follwing…

var active:Bool = true;
var activeStr:String = active ? "active" : "";  

to a macro solution something like this:

var active:Bool = true;
var activeStr = active.boolToString(); 
// if true returns "active"
// if false returns ""

The variable name “active” is known at compile time, so no problem getting that using a macro like this:

import haxe.macro.Expr;

function main() {
	var active:Bool = true;
	var varName:String = getVariableName(active); // "active"
}

macro function getVariableName(e:ExprOf<Bool>)
	return switch e.expr {
		case EConst(CIdent(s)): macro $v{s};
		default: throw 'Expr must be a single var name';
	}

But the actual boolean value of the variable “active” isn’t known at compile time, so - as far as I understand it - I must create a macro that “generates the code needed for evaluating the value runtime value” - and here my knowledge ends.

I tried the following naive approach - with bad success, simply because the actual expression that goes into the getVariableName() macro method is nothing but the variable name “val”…

function main() {
	var active = true;
	var activeStr:String = boolToString(active);
	trace(activeStr); // gives "val", not "active"
}

function boolToString(val:Bool):String
	return val ? getVariableName(val) : "";
. . .

So, I guess that the macro must produce som some kind of runtime switch or if-statement that delivers the desired result… Please enligthen me! :slight_smile:

Thank you in advance!

Jonas

For the “simple” case of a macro that just produces the runtime switch, you need to reify like so:

macro function boolToString(val:Expr):Expr {
  function getVariableName(e:Expr):String {
    return (switch (e) {
      case macro $i{name}: name;
      case _: throw "...";
    });
  }
  return macro $val ? $v{getVariableName(val)} : "";
}

This creates the AST node corresponding to the ternary statement, inserts the original expression into the condition ($val), then inserts the variable name as a string using $v{...} into the “then” branch.

You can get a step further if the analyser is enabled, I guess, since typing could result in constant folding:

macro function boolToString(val:Expr):Expr {
  var typed = Context.typeExpr(val);
  switch (typed.expr) {
    case TConst(TBool(true)): return ...;
    case TConst(TBool(false)): return ...;
  }
  // otherwise use the original approach ...
}

Thank you, Aurel!

I will digest this, and as soon I can present it in a reasonable form i will take it further to code.haxe.org (of course with the credits to you!) - if you don’t mind.

1 Like