{"atom":"regression/flipflop-and-nil-term","family":"regression","slug":"flipflop-and-nil-term","header":{"atom":"regression/flipflop-and-nil-term","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/flipflop-and-nil-term.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/flipflop-and-nil-term.grid"],"records":[["0001","","# Conformance (behaviour matrix): the flip-flop family, and `Nil` as an operand.\n#\n# `Nil` is a TERM, never a routine. The parser fell through to its general\n# identifier path, so anything that could start a listop argument turned into a\n# call: `Nil ~ 1` parsed as `Nil(~1)` and died \"No such method 'Nil'\", `Nil ff 1`\n# as `Nil(ff 1)`. That one parse fix covers ~9 operator rows at once.\n#\n# The flip-flop itself was implemented but unreachable in three of its eight\n# spellings: `^ff`, `ff^` and `^ff^` lexed as three tokens, so the carets read as\n# prefix/postfix on the operands. It also answered Any while OFF where Rakudo\n# answers Nil.\n#\n# And a bare `Nil` operand was excluded from set coercion, so `Nil (|) 1` lost it —\n# while a Nil INSIDE a list had always been kept, which was the tell.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n# NOTE: each flip-flop must sit at a FIXED source position with the loop around it.\n# Wrapping one in a `.map` closure is not equivalent — Rakudo gives that a fresh\n# latch per invocation while rakupp keys state on the source occurrence, which is a\n# real divergence but a different one from what this file pins.\nmy (@ff, @xff, @ffx, @xffx, @fff);\nfor 1..6 -> $n { @ff.push:   (($n == 2) ff   ($n == 4)).gist }\nfor 1..6 -> $n { @xff.push:  (($n == 2) ^ff  ($n == 4)).gist }\nfor 1..6 -> $n { @ffx.push:  (($n == 2) ff^  ($n == 4)).gist }\nfor 1..6 -> $n { @xffx.push: (($n == 2) ^ff^ ($n == 4)).gist }\nfor 1..6 -> $n { @fff.push:  (($n == 2) fff  ($n == 2)).gist }\nmy @ffsame;\nfor 1..6 -> $n { @ffsame.push: (($n == 2) ff ($n == 2)).gist }\n\ncheck(@ff.join(' '),     'Nil 1 2 3 Nil Nil',    'ff runs from the on-element through the off-element');\ncheck(@xff.join(' '),    'Nil Nil 2 3 Nil Nil',  '^ff excludes the on-element but still counts it');\ncheck(@ffx.join(' '),    'Nil 1 2 Nil Nil Nil',  'ff^ excludes the off-element');\ncheck(@xffx.join(' '),   'Nil Nil 2 Nil Nil Nil','^ff^ excludes both');\ncheck(@ffsame.join(' '), 'Nil 1 Nil Nil Nil Nil','ff tests the right side on the SAME element');\ncheck(@fff.join(' '),    'Nil 1 2 3 4 5',        'fff waits for the next element, so this never closes');\n\n# Nil as an operand — each of these used to be a parse-level listop call\ncheck((Nil max 1).gist,        '1',         'Nil max');\ncheck((Nil minmax 1).gist,     '1..1',      'Nil minmax');\ncheck((Nil ^ 1).gist,          'one(Nil, 1)', 'Nil ^ (a one-junction)');\ncheck((Nil X 1).gist,          '((Nil 1))', 'Nil X');\ncheck((Nil Z 1).gist,          '((Nil 1))', 'Nil Z');\ncheck((Nil notandthen 1).gist, '1',         'Nil notandthen');\ncheck((Nil ?^ 1).gist,         'True',      'Nil ?^');\ncheck((Nil ff 1).gist,         'Nil',       'Nil ff');\ncheck(Nil.gist,                'Nil',       'and a bare Nil is still Nil');\ncheck(Nil.defined.gist,        'False',     'still undefined');\n\n# Nil is a set ELEMENT\ncheck((Nil (-) 1).gist, 'Set(Nil)', 'Nil survives set difference');\ncheck((Nil (<) 1).gist, 'False',    'and is not a subset of Set(1)');\ncheck((Nil (<=) 1).gist, 'False',   'nor an improper one');\ncheck(set(1, Nil).elems.gist, '2',  'a set holding Nil has two elements');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}