I think I've finally decided to declare @
to be a shorthand for "match any item at this position", and not take an argument.
This combinator's "long" form is called ONE (a replacement for historical SKIP, because reading [x: skip]
and expecting that to store an item in a variable sounds like the opposite of skipping... and also, SKIP being arity-0 doesn't fit with the rest of the system... UPARSE instead has a SKIP combinator that takes how much to skip):
>> parse [#foo <bar>] [issue! one]
== <bar>
So now, it simply has a shorthand:
>> parse [#foo <bar>] [issue! @]
== <bar>
To justify why this isn't a fully arbitrary choice: when we see something like @var
that's matching at the current position under the constraint of the provided variable:
>> block: [some "a"]
>> parse [[some "a"] [some "a"]] [some @block]
== [some "a"]
So it doesn't seem too crazy that when you take away the variable name that's being looked up for the constraint, you'd get a combinator that matches anything.
This Frees BLANK! Up For Literal Match Blank, Or Space
Since the dawn of the BLANK! datatype, I have wanted to use it for space in string PARSE (among other places), and literal match of blanks in blocks:
>> parse "a b" ["a" _ "b"]
== "b"
>> parse [a _ b] ['a _ 'b]
== b
But the idea of blank being either "match anything at current position" or "no-op" have been competing intents.
Even the question of being an underscore in strings comes up, but now that's done with quoting (which mold-matches any type)
>> parse "a_<b>" ['a '_ '<b>]
== <b>
The @ symbol is a bit bulkier for match any item here, but I think its bulk is to scale of its intent...and as I point out, puts it in the family of the other @xxx
combinators.
We now have a pretty good answer for people who want a way to opt out of rules without using an empty block... use a void:
>> rule: if 1 = 2 [[some "b"]]
== ~void~ ; anti
>> parse "aaa" [rule some "a"]
== "a"
So I don't think it's necessary to dabble in the idea of having a fetched blank mean something different. I'm happy enough saying that the BLANK! combinator only applies in the rule as source, and gives you an error if you try to fetch it via word.
Though I will point out that @var
has quoting semantics--as if the fetched var were in the rule block with one quote level added. Hence you would get the underscore behavior:
>> parse "a_<b>" ['a @blank '<b>]
== <b>
...but if fetching BLANK! from a WORD! did anything (though I think it shouldn't), it should be a no-op:
>> parse "ab" ['a blank 'b]
== b ; not that I think it should do this, but if it DID do something...
If some amazingly compelling case for that shows up, then perhaps it should be enabled.
For Quoting, There's JUST and LITERAL
This means @ doesn't behave like it does in the main evaluator as an arity-1 operator for literalizing the subsequent argument.
But you have other options. JUST will "just" synthesize the value (don't match it), while LITERAL will match it (and synthesize if matched).
>> parse [] [just x]
== x
>> parse [''x] [literal ''x]
== ''x
LITERAL is nice when the thing you are matching has more than one quote level, because otherwise it can feel a little confusing:
>> parse [''x] ['''x]
== ''x
It's also nice if something has a quote mark in the name:
>> foo': "foo prime"
>> parse [foo'] ['foo'] ; hrrrm
== foo'
>> parse [foo'] [literal foo']
== foo'
As a shorthand, there's LIT.
>> parse [foo'] [lit foo']
== foo'