The PARSE of /PROGRESS

There has been a lot of fiddling over time with PARSE's return value. :violin:

In order to make it play nicely with ELSE and THEN, a failed parse should return NULL. But a successful parse could return many things.

  1. Just returning #[true] makes the output of PARSE easier to read in tutorials. This isn't overwhelmingly important (and mitigated by DID in modern scenarios).

  2. Returning the input value makes it easy to use PARSE as a validator for data.

    if parse data [integer! integer!] [  ; exactly two integers
       call-routine data
    ] else [fail]
    
    call-routine (parse data [integer! integer!] else [fail])  ; nicer
    
    call-routine non null parse data [integer! integer!]  ; even nicer :-)
    
  3. Returning how far a successful parse got is strictly more informative, as the information on a partial process is difficult to reconstruct otherwise.

For at least some time, @rgchris favored #3, because many sophisticated tasks are helped by knowing how far PARSE got. But that required a change to the semantics of PARSE to not automatically fail on partial inputs, so the rules had to explicitly ask to hit an <end>

But the need to tack on <end> made some things seem less concise and elegant. And surveying how other languages do "destructuring" made me feel that PARSE requiring completion was the best answer in the Redbol world. When you're matching a structure against [tag! tag!] it feels somewhat wrong for [<x> <y> <z>] to "match" when it seems "over the limit".

UPARSE Offers The Best Of All Worlds

Everything changed with UPARSE. All rules synthesize a result, and you can end the parse at any time with RETURN:

>> uparse "abc" ["ab"]
; null

>> uparse "abc" ["ab", return <input>]
== "abc"

>> uparse "abc" ["ab", return <here>]
== "c"

Regardless, there is a lower-level piece of implementation called UPARSE* that doesn't have the "must reach end, unless there's a return" condition.

>> uparse* "abc" ["ab"]
== "ab"
2 Likes