The following behavior in Red seems pretty reasonable, doesn't it?
red>> str: "aaa"
red>> parse str [any [remove "a"]]
So why doesn't R3-Alpha do the same?
r3-alpha>> str: "aaa"
r3-alpha>> parse str [any [remove "a"]]
Believe it or not, this was by design. The concept is that if a rule doesn't advance the index, it isn't considered "successful":
I am not a fan. A successful rule is one that matched. The REMOVE matched.
While the REMOVE works in Red, it seems there's still some infinite-loop-avoidance through an advancement in the rules:
red>> parse "a" [any [ahead "a"]]
I'm not sure where to draw the line. Personally I'd think that should be an infinite loop. Last I checked, this is a language where infinite loops are possible in a general sense, and it's counter-intuitive to me to see mystery behaviors trying to avoid them
Not excusing this avoidance in any way, but Parse has a checkered past with infinite loops.
Consider in Rebol 2 the difference hitting ESCAPE while performing the following:
parse "" [any ] ; unbreakable infinite loop
parse "" [any [()]] ; breakable infinite loop
This perhaps explains the reluctance to allow unfettered looping. On initial inspection, R3Alpha eventually returns TRUE for
parse "" [while ]—it may be the unbreakable nature of Parse loops were already mitigated(?).
Well that has has been recently addressed, so in Ren-C you can break a
forever  (now CYCLE). You'd be able to break an infinite looping PARSE, too.
 is a match on any input. And I'd like any  to be an infinite loop.
I mention in my linked post that sometimes decaying to infinite loops is a feature...and here it certainly can be. Consider any [:(...)]. It may be that on one iteration it evaluates to null and hence vaporizes. But on the next pass it might evaluate to a rule which splices.
(Curiously, there's a difference between
any [:(...)] and
any :(...), because the first evaluates the group on each pass while the second evaluates just once.)
...and with that...I'm convinced...
The progress rule is bad mojo. Let's toss it.
Bad cancellation may have played a role, but I found out what the actual reason was:
Ladislav wanted a: [any b] to be an iterative equivalent of the recursive a: [b a |] Then, he wanted this to produce
i = 3:
a: [a |]
parse "" [(i: 0) 3 [["a" |] (i: i + 1)] end]
If you mix all that in with the desire to have
any [... | end] not be an infinite loop, something has to give somewhere. But I think the "no progress" rule is the wrong answer.
I posted my remarks on the issue....that I think this should be the true purpose of the WHILE vs ANY distinction. ANY and SOME will break on END, but a while won't. So it's actually a: [while b] to be an iterative equivalent of the recursive a: [b a |], with ANY and SOME having the historical behavior.
So WHILE actually has a reason to exist, after all. Who knew.