Do not shorten Expr-essions in trace

The only reason you do trace( expr ) is to see what the expression actually looks like. In haxe 3 if I was interested in what is the ast for some expression, I could just trace it, format it in text editor and read it. In haxe 4 I’ll get { expr => EReturn( <…> ): pos => whatever }, then I have to go back, add switch, new trace, then another switch etc, which is just annoying.
At least make it show the whole expression in --debug mode.

This has nothing to do with expressions in particular. Printing of objects has to be limited because object values can be recursive. You can use haxe.macro.Printer to print out haxe.macro.Expr instances.

Printer formats expressions into haxe code, while what I need is raw ast. It actually makes learning macroses harder because you just can’t trace everything anymore. Considering how trace is debug feature anyway, can’t you just keep track of visited nodes and not touch them again if they were already printed?

2 Likes

Yeah, printing whole AST trees has become a lot harder.

A quick and absolutely sub-optimal util that should be used with care (it recursively prints the AST, so this can get really ugly with huge snippets of code ; also it can go really bad if your expression is recursive somehow):

  static var printer = new haxe.macro.Printer();
  static function traceExpr(expr:haxe.macro.Expr) {
    trace(printer.printExpr(expr));
    trace(expr);
    haxe.macro.ExprTools.iter(expr, traceExpr);
  }

With this example output:

Macro.hx:24: {
	function doRecurse(count:Int) {
		items.push(count);
		if (count > 0) doRecurse(count - 1);
	};
	(5);
}
Macro.hx:25: {pos: #pos(Macro.hx:6: lines 6-12), expr: EBlock([{pos: #pos(Macro.hx:7: lines 7-11), expr: EFunction(doRecurse,{ret: <...>, params: <...>, expr: <...>, args: <...>})},{pos: #pos(Macro.hx:11: characters 10-13), expr: EParenthesis({pos: <...>, expr: <...>})}])}
Macro.hx:24: function doRecurse(count:Int) {
	items.push(count);
	if (count > 0) doRecurse(count - 1);
}
Macro.hx:25: {pos: #pos(Macro.hx:7: lines 7-11), expr: EFunction(doRecurse,{ret: null, params: [], expr: {pos: #pos(Macro.hx:7: lines 7-11), expr: EBlock([<...>,<...>])}, args: [{name: count, opt: false, type: TPath(<...>)}]})}
Macro.hx:24: {
	items.push(count);
	if (count > 0) doRecurse(count - 1);
}
Macro.hx:25: {pos: #pos(Macro.hx:7: lines 7-11), expr: EBlock([{pos: #pos(Macro.hx:8: characters 13-30), expr: ECall({pos: <...>, expr: <...>},[<...>])},{pos: #pos(Macro.hx:9: lines 9-10), expr: EIf({pos: <...>, expr: <...>},{pos: <...>, expr: <...>},null)}])}
Macro.hx:24: items.push(count)
Macro.hx:25: {pos: #pos(Macro.hx:8: characters 13-30), expr: ECall({pos: #pos(Macro.hx:8: characters 13-23), expr: EField({pos: #pos(Macro.hx:8: characters 13-18), expr: EConst(<...>)},push)},[{pos: #pos(Macro.hx:8: characters 24-29), expr: EConst(CIdent(<...>))}])}
Macro.hx:24: items.push
Macro.hx:25: {pos: #pos(Macro.hx:8: characters 13-23), expr: EField({pos: #pos(Macro.hx:8: characters 13-18), expr: EConst(CIdent(items))},push)}
Macro.hx:24: items
Macro.hx:25: {pos: #pos(Macro.hx:8: characters 13-18), expr: EConst(CIdent(items))}
Macro.hx:24: count
Macro.hx:25: {pos: #pos(Macro.hx:8: characters 24-29), expr: EConst(CIdent(count))}
Macro.hx:24: if (count > 0) doRecurse(count - 1)
Macro.hx:25: {pos: #pos(Macro.hx:9: lines 9-10), expr: EIf({pos: #pos(Macro.hx:9: characters 16-25), expr: EBinop(OpGt,{pos: #pos(Macro.hx:9: characters 16-21), expr: EConst(<...>)},{pos: #pos(Macro.hx:9: characters 24-25), expr: EConst(<...>)})},{pos: #pos(Macro.hx:10: characters 14-32), expr: ECall({pos: #pos(Macro.hx:10: characters 14-23), expr: EConst(<...>)},[{pos: <...>, expr: <...>}])},null)}
Macro.hx:24: count > 0
Macro.hx:25: {pos: #pos(Macro.hx:9: characters 16-25), expr: EBinop(OpGt,{pos: #pos(Macro.hx:9: characters 16-21), expr: EConst(CIdent(count))},{pos: #pos(Macro.hx:9: characters 24-25), expr: EConst(CInt(0))})}
Macro.hx:24: count
Macro.hx:25: {pos: #pos(Macro.hx:9: characters 16-21), expr: EConst(CIdent(count))}
Macro.hx:24: 0
Macro.hx:25: {pos: #pos(Macro.hx:9: characters 24-25), expr: EConst(CInt(0))}
Macro.hx:24: doRecurse(count - 1)
Macro.hx:25: {pos: #pos(Macro.hx:10: characters 14-32), expr: ECall({pos: #pos(Macro.hx:10: characters 14-23), expr: EConst(CIdent(doRecurse))},[{pos: #pos(Macro.hx:10: characters 24-31), expr: EBinop(OpSub,{pos: <...>, expr: <...>},{pos: <...>, expr: <...>})}])}
Macro.hx:24: doRecurse
Macro.hx:25: {pos: #pos(Macro.hx:10: characters 14-23), expr: EConst(CIdent(doRecurse))}
Macro.hx:24: count - 1
Macro.hx:25: {pos: #pos(Macro.hx:10: characters 24-31), expr: EBinop(OpSub,{pos: #pos(Macro.hx:10: characters 24-29), expr: EConst(CIdent(count))},{pos: #pos(Macro.hx:10: characters 30-31), expr: EConst(CInt(1))})}
Macro.hx:24: count
Macro.hx:25: {pos: #pos(Macro.hx:10: characters 24-29), expr: EConst(CIdent(count))}
Macro.hx:24: 1
Macro.hx:25: {pos: #pos(Macro.hx:10: characters 30-31), expr: EConst(CInt(1))}
Macro.hx:24: (5)
Macro.hx:25: {pos: #pos(Macro.hx:11: characters 10-13), expr: EParenthesis({pos: #pos(Macro.hx:11: characters 11-12), expr: EConst(CInt(5))})}
Macro.hx:24: 5
Macro.hx:25: {pos: #pos(Macro.hx:11: characters 11-12), expr: EConst(CInt(5))}

In my opinion this should be addressed at Haxe-level by having a proper printing method, either in std or in some library. The object notation looks like a mess for expressions because of all the positions inbetween. I’m quite surprised somebody relied on that for debugging purposes.

While I agree that a good printing method for the Expr objects included in the std would be nice, with the new real-time debugging support in Eval this becomes less of an issue, no?