Stack tracing improvements
A limitation of my previous stack tracing patches was that io-wrap and io-stream-wrap did not properly report traces on failure. The reason for this is easy to spot if we look at how the error is handled (this is where execution flow ends up when you call io-wrap):
option-wrap(opts, usage, about, announce, s) = parse-options(opts, usage, about) ; announce ; (s; report-success <+ report-failure) report-failure = report-run-time ; <fprintnl> (stderr(), [
(), ": rewriting failed"]) ; <exit> 1
As you can imagine, even though the program now happily prints a stack trace when the main strategy exits with a failure, it will not be printed when exit is called.
I've introduced a couple of stack introspection functions for dealing with this: stacktrace-get-current-frame-name returns the name of the current frame s, stacktrace-get-all-frame-names returns a list of all frame names and, stacktrace-get-current-frame-index returns integer that holds the current depth of the stack. These are actually implemented by primitives in the Stratego Standard Library (SSL).
A caveat of these strategies is that calling them will of course alter the stack. Even in the wonderful world of computing, we're not entirely free of Heisenbergian effects, apparently. However, there's a simple workaround: call the primitives directly, since this bypasses the way the compiler registers the stack frames.
With this trick in hand, I rewrote the two above strategies to include proper stack tracing for io-wrap:
option-wrap(opts, usage, about, announce, s) = parse-options(opts, usage, about) ; announce ; (s; report-success <+ prim("SSL_stacktrace_get_all_frame_names") ; report-failure) report-failure = ?stacktrace ; report-run-time ; <fprintnl> (stderr(), [ <whoami> (), ": rewriting failed, trace:"]) ; <reverse ; map(<fprintnl> (stderr(), ["\t", <id>]))> stacktrace ; <exit> 1
Applying the modified io-wrap on the following sample program
main = io-wrap(my-wrap(foo)) my-wrap(s) = s foo = debug(!"foo") ; bar bar = debug(!"bar") ; fap ; zap fap = debug(!"fap") ; id zap = debug(!"zap") ; debug ; fail
./prog: rewriting failed, trace: main_0_0 io_wrap_1_0 option_wrap_5_0 lifted144 input_1_0 lifted145 output_1_0 lifted0 my_wrap_1_0 foo_0_0 bar_0_0 zap_0_0
Due to the compiler lifting inner strategies into freshly named, top-level strategies, the trace will contain some lifted* entries. Also, should you call strategies or rules which are compiled with older versions of the compiler, there will be "dark spots" in your trace. It won't be truncated -- only the frames due to the old library will be hidden.