For an incremental cost of zero extra statements, and less than 1k of memory, I've managed to successfully implement support for nested IF/UNLESS/ELSE statements!
Starting from the premise that the regular expression engine already had support for recursion, I was somewhat dismayed to discover I didn't have access to recursion because it wasn't available in older Perls.
Suggestions from Google were that in a pinch you can do something similar using negative look-behind assertions (similar to tricks I've used before to do rather elegant and fast unescaped-quote scanning in PPI).
Alas, delving through the man pages I find that I don't have support for look-behind assertions either if I want 5.004 support (and that's always a nice-to-have in
So, how to do recursive conditions.
The slightly odd answer is that you can emulate the same kind of negative look-behind using a slightly crazier negative look-ahead assertion (which I DO have access to).
The new condition regex looks something like this (where $LEFT and $RIGHT are fairly complex whitespace-chomping expressions for [% and %])
my $CONDITION = qr/
$LEFT (IF|UNLESS) \s+ ( $EXPR ) $RIGHT
$LEFT (?: IF | UNLESS )
$LEFT ELSE $RIGHT
$LEFT (?: IF | UNLESS )
$LEFT END $RIGHT
So basically, look for the IF block, which ISN'T followed by another IF block, then an option ELSE block (which isn't followed by an IF block), followed by an END block.
Superficially, this doesn't really make sense, because you only match things at the deepest level.
Actually, it's even worse, because it ONLY matches the last and/or deepest IF block. So lets first at least deal with all of them by doing multiple passes.
1 while $copy =~ s/
$1 eq 'UNLESS'
!! # Force boolification
} ? $3 : $4;
The key part there is the "1 while". This keeps passing over the string until we've run out of last and deepest conditions.
The only real problems here are that substiting in multiple passes runs the risk of an infinite loop, and we're processing the conditions from bottom to top and deepest to shallowest.
The infinite loop isn't a problem here (fortunately) because we never add new content to the file, only cut parts out. The expression evaluation stuff is done in a separate regex.
And the fact the conditions run in the opposite order isn't a problem either because we've already said that the stash MUST be immutable and side effects aren't allowed (although if anyone tries to cheat on this point, they're going to get weirded out).
So there's full (and infinitely deep) support for nested conditions, for pretty much no cost. This leaves the 60k of spare room largely free, which is good because FOREACH support is going to be much much harder than IF/UNLESS/ELSE.
The main problem here is that all the blocks end with the same terminator [% END %] (which means it's really hard to process different statements separately).
I do, however, have some interesting new ideas on how I might deal with this problem now that I have the negative-lookahead+while trick down pat.