DESTRUCTURE Dialect

I've talked about this. But there's no excuse not to write it, when there's so much power in modern PARSE (the engine we up until recently called UPARSE).

A simple motivating case was a desire for different behavior from PARSE alternates with |. So while [word! | word! word!] is always considered to be a badly-written parse rule, you could get a different treatment in destructure...because it always assumes you mean to go to <end>

>> destructure [banana] [
      [word!] => [print "Just one word"]
      [word! word!] => [print "Two words"]
   ]
Just one word

>> destructure [apple banana] [
      [word!] => [print "Just one word"]  ; not complete, so no match
      [word! word!] => [print "Two words"]
   ]
Two words

But the dialect concept is more ambitious than that, allowing you to give names to capture rules. Then when you use those names, that's the variable that is set:

>> destructure [apple banana] [
      w1: [word!] w2: [word!]

      [w1] => [print ["One:" w1]]
      [w1 w2] => [print ["Two:" w1 w2]]
   ]
Two: apple banana

And because what's actually powering this behind the scenes is PARSE, you have all those tools at your disposal...including the just-added ellipsis operator!

>> destructure [1 2 apple banana 3 4 5] [
      w1: [word!] w2: [word!]

      [w1] => [print ["One:" w1]]
      [w1 w2] => [print ["Two:" w1 w2]]
      [... w1 w2 ...] => [print ["Words and more stuff:" w1 w2]]
   ]
Words and more stuff: apple banana

A Crazy Short Implementation!

I'm sure it will receive some enhancements, but... this is pretty badass for something I wrote that worked more or less the first time!

destructure: func [
    input [any-series!]
    dialect [block!]
    /multi "Run multiple branches"
][
    let result': void'
    let combinators: copy default-combinators
    parse dialect [while [not <end>] [
        let set-word: set-word!, let rule: block! (
            combinators.(to word! set-word): compose [(set-word) (rule)]
        )
        |
        let pattern: block!, '=>, let branch: block!
        (
            parse/combinators input pattern combinators then (
                branch
            ) also ^r' -> [
                if not multi [
                    return unmeta r'
                ]
                result': r'
            ]
        )
        |
        fail "Invalid DESTRUCTURE dialect entry"
    ]]
    return unmeta result'
]

And It's Composable, Too!

That's right... you can use DESTRUCTURE with ELSE and THEN.

It follows the rules; a destructure that does not match anything is void

>> void? destructure [a] [[integer!] => [print "Not happening"]]
== ~true~  ; anti

Notice I even threw a /MULTI feature in there, so you can match multiple ways!

>> destructure/multi [apple banana] [
      w1: [word!] w2: [word!]

      [w1] => [print ["One:" w1]]
      [w1 w2] => [print ["Two:" w1 w2]]
      [repeat 2 <any>] => [print "Matching any two values also counts"]
   ]
Two: apple banana
Matching any two values also counts

:boom: :raised_hand_with_fingers_splayed: :electric_plug: POWER, IN YOUR HANDS! :electric_plug: :raised_hand_with_fingers_splayed: :boom:

2 Likes

Awesome! Standing ovation!

applause-futurama

So DESTRUCTURE was a victim of the new policy of extracting unbound structural data by default, and stopped working:

How to Capture Binding Of PARSE Items

But impressively, changing the above to use a binding operation that runs IN on the current input block made it work! (Calling this *in* temporarily until a name is decided)

   let set-word: *in* set-word!, let rule: *in* block! (
        combinators.(to word! set-word): compose [(set-word) (rule)]
    )
    |
    let pattern: *in* block!, '=>, let branch: *in* block!

The *in* combinator was trivial to write:

combinator [
    return: "Argument binding gotten INSIDE the current input"
        [any-value?]
    parser [action?]
    <local> result'
][
    [^result' remainder]: parser input except e -> [
        return raise e
    ]

    return in state.input unmeta result'
]

And that's all it took. :clap:

This is encouraging, and seeing this work out makes me more comfortable with merging Pure Virtual Binding II sooner rather than later.

2 Likes