Double Negation in PARSE is NOT NOT NOT Cool

So there's a bit of an issue with the NOT combinator in UPARSE. It fails when the rule you give it as a parameter succeeds:

>> uparse [1] [not integer!]  ; fails... because it *was* an integer
; null

The NOT combinator received information about how much of the input the INTEGER! matched. But it threw that information away.

So what if you tried to negate it again...with NOT NOT?

>> uparse [1] [not not integer!]
; ???

Here the first NOT is negating the second rule that failed. So it's a success in the traditional sense you'd usually expect with a NOT. But it doesn't know in that success how much to skip. So the 1 is still pending.

Hence to get this to work you'd have to say:

>> uparse [1] [not not integer!, integer!]
== [1]

In other words, every NOT is effectively a NOT AHEAD.

But Red actually tries to cancel out the NOTs:

red>> parse [1] [set x not not integer!]
== true

red>> x
== 1

This doesn't make a lot of sense. It doesn't work with other failing rules... 1 is not a "not integer!" and it's also not a "tag!", so why would this not capture it?

red>> parse [1] [set x not tag!]
== false

Here are the options for dealing with this in the combinator model:

  1. Change the name of NOT to NOT-AHEAD. This is how Haskell does it (they call it notFollowedBy... that combinator list is good to look at, by the way).

  2. Leave it as-is and assume that it's the kind of thing you can just learn.

  3. Rig up a hack to make the specific case of NOT immediately followed by NOT a no-op.

  4. Systematically build into the protocol the idea that a rule can separately signal that it failed as well as "how much input it would have consumed if it had succeeded"

I think (3) as in Red is a bad idea. I'm not even sure what (4) would mean...?

red>> parse [1 2 3 4] [copy numbers not some not integer!]
== false

I think my inclination would be (2).

UPDATE: Looking a little further, I think the NOT NOT behavior in Red is likely a bug and not an attempt to implement a nonsensical feature...being caused by the same thing that made #1246 happen in R3-Alpha, e.g. controlling negation via a single flag.