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