Did grammar behaviour break between 6.c and 6.d?

@Håkon named the game. You teed off to land on the courseway. I got us near the green but in the rough. @ugexe spotted the ball. @Håkon skipped this hole, moved to the next tee and holed in one. So I guess I'm left to write the post-game wrap up.


It's not 6.c vs 6.d.

The underlying issue is that hashes are specified to have their key/value pairs listed in random order. In earlier compilers the hash list implementation returned key/value pairs in a fixed order. Then samcv implemented hash randomization for MoarVM last year. It breaks code that incorrectly relies on hash order.

So the following code -- on Rakudo/MoarVM since 2018.05 -- randomly displays either (a b) or (b a):

say .values given { apple => 'a', bananas => 'b' }

Your code was calling .values on a Match object.

Calling that method will usually boil down to something similar in effect to:

say .values given ( apple => 'a', bananas => 'b', 0, 1, 2 ) .Capture

which will display a list starting with 0 1 2 (they're positionals, so they display first, in their correct order) followed by either a b or b a (they're named so they display last, in random order).

This in turn is because a Match is a sub-class of Capture and inherits its .values method from that class. As its doc says:

Returns a Seq containing all positional values followed by all named argument values.

In your code there were no positional captures. So all you got to see was the values of named captures. But of course they're in a list, so appear under indices .[0], .[1], etc. which presumably tricked you into thinking they were ordered (in combination with the fact that, in earlier Rakudos, their supposedly random order was in reality fixed).

.caps improves on this because it orders its values according to their match positions in the input. This will ensure stable ordering of a list of the values of named captures -- provided none of the captures share a matching position:

say .caps given 'foo' ~~ / $<alias>=<ident> /

will randomly display either (alias => 「foo」 ident => 「foo」) or (ident => 「foo」 alias => 「foo」).

My first answer

For the record, here was the first version of this answer:

Here it is golf'd about as far down as I think it can go:

grammar {
  token TOP { <one> <two> }
  token one { 1 }
  token two { 2 }
}.parse('12').values.say;

In the v2018.04 that's the current version running on tio.run this reliably displays (「1」 「2」). In the v2018.12 that's the current version running on glot.io this produces either of (「1」 「2」) or (「2」 「1」), randomly varying between the two.

I don't know if this is a bug. Investigation continues but I thought I'd post this as an initial answer.


I am not sure what order $/.values is supposed to return (I get the randomized order as you describe), but instead of using $/.values:

method TOP($/)     {
     make $<divi> ?? $/.values.[0].made~'.'~$/.values.[1].made
                  !! $/.values.[0].made
}

you can use try $/.caps:

method TOP($/)     {
    make $<divi> ?? $/.caps.[0].value.made~'.'~$/.caps.[2].value.made
                 !! $/.caps.[0].value.made
}

at least this works for me.

Tags:

Raku