As reported in issue 908 the Nimrod language doesn't give meaning to empty lines. The effect of this is that the example posted there fails because the expected finally as a statement actually get's appended to the first try-except block, running immediately after it while the intention was to run it at the end of the proc.
After some experimentation turns out that inserting a discard before the finally as statement fixes the problem. The finally block is then not appended to the previous try-except.
Templates FTW! I thought to myself. But after implementing the template I saw why it doesn't work. Rather than expanding in the caller's scope it generates a Call AST node. The effect is that the discard doesn't affect the superior AST level. Attempting to do a macro instead of a template doesn't help either.
Looks like this is a limitation of metaprogramming in general if templates/macros are not allowed to modify the parent. Is there any hope for this trickery to work and the discard to affect the AST of the caller? If not, any chance to deprecate the finally as a statement and introduce the memento keyword to replace this functionality without the ambiguity?
On Linux your program outputs for me:
File opened for workaround Workaround did work File opened for memento Closing file Success testing memento
And indeed wrapping the finally in a template should work. The easier implementation works as well for me:
template memento(body: stmt): stmt {.immediate.} =
## Like finally as a statement but without warts.
finally: body
Ugh, turns out write doesn't produce an exception when the file has already been closed, but the file is empty. Anyway, it's a simple bug that the template doesn't shield properly here. Added it to my todo.
EDIT: It's actually quite subtle... Will take a deeper look later.
I think that for the same reason I'd like the discard to affect the parent environment there might be reasons to explicitly avoid this. For instance, a try-except template without a finally statement. This would be unrolled in the parent and a finally as statement written by the user would get added to the expanded template, again confusing the meaning of the source code.
So maybe add an explicit unroll_ast pragma and keep both behaviours? Hmm... or maybe make the default behaviour to unroll and force users to write their try-except templates inside an explicit block?