Minor (but nice!) %prot-http.r parsing improvements

Here's is R3-Alpha's PARSE-WRITE-DIALECT from %prot-http.r

parse-write-dialect: func [port block /local spec] [
    spec: port/spec
    parse block [
        [set block word! (spec/method: block) | (spec/method: 'post)]
        opt [set block [file! | url!] (spec/path: block)]
        [set block block! (spec/headers: block) | (spec/headers: [])]
        [set block [any-string! | binary!] (spec/content: block) | (spec/content: none)]
    ]
]

You couldn't set paths in R3-Alpha parse, which is weird. So things had to be captured into a temporary variable (it reused the input block for some reason).

Let's imagine that setting paths were legal:

parse-write-dialect: func [port block /local spec] [
    spec: port/spec
    parse block [
        [set spec/method word! | (spec/method: 'post)]
        opt [set spec/path [file! | url!]]
        [set spec/headers block! | (spec/headers: [])]
        [set spec/content [any-string! | binary!] | (spec/content: none)]
    ]
]

Let's do a quick transformation of this to PARSE3 in Ren-C:

parse-write-dialect: func [port block <local> spec] [
    spec: port.spec
    parse3 block [  ; PARSE3 is R3-Alpha-compatible parse
        [set spec.method word! | (spec.method: 'post)]
        opt [set spec.path [file! | url!]]
        [set spec.headers block! | (spec.headers: [])]
        [set spec.content [any-string! | binary!] | (spec.content: _)]
    ]
]
  • The TUPLE!s help us see we're not making function calls, which is nice

  • We have BLANK! which gives a nicer semiotic "we're nulling out this variable" look.

  • /LOCAL is a legitimate normal refinement, and <local> variables are truly local--you can't slip them in from the caller. Because they are invisible, you can build function compositions that don't have to worry about colliding with their names.

    • It also frees up /LOCAL to just be a typical refinement, like TIME/LOCAL

Now let's throw in some UPARSE, with the SET-WORD!s for assignment...

parse-write-dialect: func [port block <local> spec] [
    spec: port.spec
    parse block [  ; PARSE is now UPARSE
        [spec.method: word! | (spec.method: 'post)]
        opt [spec.path: [file! | url!]]
        [spec.headers: block! | (spec.headers: [])]
        [spec.content: [any-string! | binary!] | (spec.content: _)]
    ]
]

It looks a little tighter...but then we also have the ability to factor the assignments out...

parse-write-dialect: func [port block <local> spec] [
    spec: port.spec
    parse block [
        spec.method: [word! | ('post)]
        spec.path: opt [file! | url!]
        spec.headers: [block! | ([])]
        spec.content: [any-string! | binary! | (_)]
    ]
]

As it happens, that last line could just be opt [any-string! binary!], because it would assign the result to NULL.

But... That Raises a Question...

What if the PARSE behavior for BLANK! was the same as in the evaluator, and it just produced NULL out of thin air but didn't match it? Hence _ would be a synonym for (_).

Then these would be equivalent:

spec.content: [any-string! | binary! | _]

spec.content: opt [any-string! | binary!]

You'd also be able to easily zero variables without entering a GROUP!.

>> data: <before>

>> parse [x x x] [data: _ [some "y" (data: "Found some Y!")]]
; null

>> data
; null

I think the literal INTEGER! behavior has turned out to be a winner, where 3 is the same as (3). So maybe that would be good for blank too, given its new useful null-making behavior? Dunno.

3 Likes

Wow, that cleans up beautifully! You say it's minor, and maybe it is, but it looks like the kind of brevity you'll be thanking yourself for again and again when you revisit the code in the future.

3 Likes