use Test::More tests => 1; use Parse::ABNF; use File::Spec qw(); use IO::File; my $path = File::Spec->catfile('data', 'rfc3987.abnf'); my $text = do { local $/; IO::File->new('<' . $path)->getline }; # Try to avoid the usual newline madness... $text =~ s/\x0d\x0a|\x0d|\x0a/\n/g; my $gram = Parse::ABNF->new->parse($text); my $expt = [ { 'value' => { 'value' => [ { 'name' => 'scheme', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' }, { 'name' => 'ihier-part', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '?', 'class' => 'Literal' }, { 'name' => 'iquery', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '#', 'class' => 'Literal' }, { 'name' => 'ifragment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'IRI', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => [ { 'value' => '//', 'class' => 'Literal' }, { 'name' => 'iauthority', 'class' => 'Reference' }, { 'name' => 'ipath-abempty', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'name' => 'ipath-absolute', 'class' => 'Reference' }, { 'name' => 'ipath-rootless', 'class' => 'Reference' }, { 'name' => 'ipath-empty', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'ihier-part', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'IRI', 'class' => 'Reference' }, { 'name' => 'irelative-ref', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'IRI-reference', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'scheme', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' }, { 'name' => 'ihier-part', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '?', 'class' => 'Literal' }, { 'name' => 'iquery', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'absolute-IRI', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'irelative-part', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '?', 'class' => 'Literal' }, { 'name' => 'iquery', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '#', 'class' => 'Literal' }, { 'name' => 'ifragment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'irelative-ref', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => [ { 'value' => '//', 'class' => 'Literal' }, { 'name' => 'iauthority', 'class' => 'Reference' }, { 'name' => 'ipath-abempty', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'name' => 'ipath-absolute', 'class' => 'Reference' }, { 'name' => 'ipath-noscheme', 'class' => 'Reference' }, { 'name' => 'ipath-empty', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'irelative-part', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'iuserinfo', 'class' => 'Reference' }, { 'value' => '@', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'name' => 'ihost', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => ':', 'class' => 'Literal' }, { 'name' => 'port', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'iauthority', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'iunreserved', 'class' => 'Reference' }, { 'name' => 'pct-encoded', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'iuserinfo', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'IP-literal', 'class' => 'Reference' }, { 'name' => 'IPv4address', 'class' => 'Reference' }, { 'name' => 'ireg-name', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'ihost', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'iunreserved', 'class' => 'Reference' }, { 'name' => 'pct-encoded', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'ireg-name', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'ipath-abempty', 'class' => 'Reference' }, { 'name' => 'ipath-absolute', 'class' => 'Reference' }, { 'name' => 'ipath-noscheme', 'class' => 'Reference' }, { 'name' => 'ipath-rootless', 'class' => 'Reference' }, { 'name' => 'ipath-empty', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'ipath', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'value' => [ { 'value' => '/', 'class' => 'Literal' }, { 'name' => 'isegment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'ipath-abempty', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => '/', 'class' => 'Literal' }, { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'isegment-nz', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '/', 'class' => 'Literal' }, { 'name' => 'isegment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => undef, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'ipath-absolute', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'isegment-nz-nc', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '/', 'class' => 'Literal' }, { 'name' => 'isegment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => undef, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'ipath-noscheme', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'isegment-nz', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'value' => '/', 'class' => 'Literal' }, { 'name' => 'isegment', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => undef, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'ipath-rootless', 'class' => 'Rule' }, { 'value' => { 'min' => '0', 'value' => { 'value' => 'ipchar', 'class' => 'ProseValue' }, 'max' => '0', 'class' => 'Repetition' }, 'name' => 'ipath-empty', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'name' => 'ipchar', 'class' => 'Reference' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'isegment', 'class' => 'Rule' }, { 'value' => { 'min' => '1', 'value' => { 'name' => 'ipchar', 'class' => 'Reference' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'isegment-nz', 'class' => 'Rule' }, { 'value' => { 'min' => '1', 'value' => { 'value' => [ { 'name' => 'iunreserved', 'class' => 'Reference' }, { 'name' => 'pct-encoded', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' }, { 'value' => '@', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'isegment-nz-nc', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'iunreserved', 'class' => 'Reference' }, { 'name' => 'pct-encoded', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' }, { 'value' => '@', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'name' => 'ipchar', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'ipchar', 'class' => 'Reference' }, { 'name' => 'iprivate', 'class' => 'Reference' }, { 'value' => '/', 'class' => 'Literal' }, { 'value' => '?', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'iquery', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'ipchar', 'class' => 'Reference' }, { 'value' => '/', 'class' => 'Literal' }, { 'value' => '?', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'ifragment', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'ALPHA', 'class' => 'Reference' }, { 'name' => 'DIGIT', 'class' => 'Reference' }, { 'value' => '-', 'class' => 'Literal' }, { 'value' => '.', 'class' => 'Literal' }, { 'value' => '_', 'class' => 'Literal' }, { 'value' => '~', 'class' => 'Literal' }, { 'name' => 'ucschar', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'iunreserved', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'min' => 'A0', 'max' => 'D7FF', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'F900', 'max' => 'FDCF', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'FDF0', 'max' => 'FFEF', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '10000', 'max' => '1FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '20000', 'max' => '2FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '30000', 'max' => '3FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '40000', 'max' => '4FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '50000', 'max' => '5FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '60000', 'max' => '6FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '70000', 'max' => '7FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '80000', 'max' => '8FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '90000', 'max' => '9FFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'A0000', 'max' => 'AFFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'B0000', 'max' => 'BFFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'C0000', 'max' => 'CFFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'D0000', 'max' => 'DFFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'E1000', 'max' => 'EFFFD', 'type' => 'hex', 'class' => 'Range' } ], 'class' => 'Choice' }, 'name' => 'ucschar', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'min' => 'E000', 'max' => 'F8FF', 'type' => 'hex', 'class' => 'Range' }, { 'min' => 'F0000', 'max' => 'FFFFD', 'type' => 'hex', 'class' => 'Range' }, { 'min' => '100000', 'max' => '10FFFD', 'type' => 'hex', 'class' => 'Range' } ], 'class' => 'Choice' }, 'name' => 'iprivate', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'ALPHA', 'class' => 'Reference' }, { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'ALPHA', 'class' => 'Reference' }, { 'name' => 'DIGIT', 'class' => 'Reference' }, { 'value' => '+', 'class' => 'Literal' }, { 'value' => '-', 'class' => 'Literal' }, { 'value' => '.', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'scheme', 'class' => 'Rule' }, { 'value' => { 'min' => 0, 'value' => { 'name' => 'DIGIT', 'class' => 'Reference' }, 'max' => undef, 'class' => 'Repetition' }, 'name' => 'port', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => '[', 'class' => 'Literal' }, { 'value' => [ { 'name' => 'IPv6address', 'class' => 'Reference' }, { 'name' => 'IPvFuture', 'class' => 'Reference' } ], 'class' => 'Choice' }, { 'value' => ']', 'class' => 'Literal' } ], 'class' => 'Group' }, 'name' => 'IP-literal', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => 'v', 'class' => 'Literal' }, { 'min' => '1', 'value' => { 'name' => 'HEXDIG', 'class' => 'Reference' }, 'max' => undef, 'class' => 'Repetition' }, { 'value' => '.', 'class' => 'Literal' }, { 'min' => '1', 'value' => { 'value' => [ { 'name' => 'unreserved', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'max' => undef, 'class' => 'Repetition' } ], 'class' => 'Group' }, 'name' => 'IPvFuture', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => [ { 'min' => '6', 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '6', 'class' => 'Repetition' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'value' => '::', 'class' => 'Literal' }, { 'min' => '5', 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '5', 'class' => 'Repetition' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'name' => 'h16', 'class' => 'Reference' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'min' => '4', 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '4', 'class' => 'Repetition' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '1', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'min' => '3', 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '3', 'class' => 'Repetition' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '2', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'min' => '2', 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '2', 'class' => 'Repetition' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '3', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '4', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'name' => 'ls32', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '5', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'min' => 0, 'value' => { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' } ], 'class' => 'Group' }, 'max' => '6', 'class' => 'Repetition' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, 'max' => 1, 'class' => 'Repetition' }, { 'value' => '::', 'class' => 'Literal' } ], 'class' => 'Group' } ], 'class' => 'Choice' }, 'name' => 'IPv6address', 'class' => 'Rule' }, { 'value' => { 'min' => '1', 'value' => { 'name' => 'HEXDIG', 'class' => 'Reference' }, 'max' => '4', 'class' => 'Repetition' }, 'name' => 'h16', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => [ { 'name' => 'h16', 'class' => 'Reference' }, { 'value' => ':', 'class' => 'Literal' }, { 'name' => 'h16', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'name' => 'IPv4address', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'ls32', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'dec-octet', 'class' => 'Reference' }, { 'value' => '.', 'class' => 'Literal' }, { 'name' => 'dec-octet', 'class' => 'Reference' }, { 'value' => '.', 'class' => 'Literal' }, { 'name' => 'dec-octet', 'class' => 'Reference' }, { 'value' => '.', 'class' => 'Literal' }, { 'name' => 'dec-octet', 'class' => 'Reference' } ], 'class' => 'Group' }, 'name' => 'IPv4address', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'DIGIT', 'class' => 'Reference' }, { 'value' => [ { 'min' => '31', 'max' => '39', 'type' => 'hex', 'class' => 'Range' }, { 'name' => 'DIGIT', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'value' => '1', 'class' => 'Literal' }, { 'min' => '2', 'value' => { 'name' => 'DIGIT', 'class' => 'Reference' }, 'max' => '2', 'class' => 'Repetition' } ], 'class' => 'Group' }, { 'value' => [ { 'value' => '2', 'class' => 'Literal' }, { 'min' => '30', 'max' => '34', 'type' => 'hex', 'class' => 'Range' }, { 'name' => 'DIGIT', 'class' => 'Reference' } ], 'class' => 'Group' }, { 'value' => [ { 'value' => '25', 'class' => 'Literal' }, { 'min' => '30', 'max' => '35', 'type' => 'hex', 'class' => 'Range' } ], 'class' => 'Group' } ], 'class' => 'Choice' }, 'name' => 'dec-octet', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => '%', 'class' => 'Literal' }, { 'name' => 'HEXDIG', 'class' => 'Reference' }, { 'name' => 'HEXDIG', 'class' => 'Reference' } ], 'class' => 'Group' }, 'name' => 'pct-encoded', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'ALPHA', 'class' => 'Reference' }, { 'name' => 'DIGIT', 'class' => 'Reference' }, { 'value' => '-', 'class' => 'Literal' }, { 'value' => '.', 'class' => 'Literal' }, { 'value' => '_', 'class' => 'Literal' }, { 'value' => '~', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'name' => 'unreserved', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'name' => 'gen-delims', 'class' => 'Reference' }, { 'name' => 'sub-delims', 'class' => 'Reference' } ], 'class' => 'Choice' }, 'name' => 'reserved', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => ':', 'class' => 'Literal' }, { 'value' => '/', 'class' => 'Literal' }, { 'value' => '?', 'class' => 'Literal' }, { 'value' => '#', 'class' => 'Literal' }, { 'value' => '[', 'class' => 'Literal' }, { 'value' => ']', 'class' => 'Literal' }, { 'value' => '@', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'name' => 'gen-delims', 'class' => 'Rule' }, { 'value' => { 'value' => [ { 'value' => '!', 'class' => 'Literal' }, { 'value' => '$', 'class' => 'Literal' }, { 'value' => '&', 'class' => 'Literal' }, { 'value' => '\'', 'class' => 'Literal' }, { 'value' => '(', 'class' => 'Literal' }, { 'value' => ')', 'class' => 'Literal' }, { 'value' => '*', 'class' => 'Literal' }, { 'value' => '+', 'class' => 'Literal' }, { 'value' => ',', 'class' => 'Literal' }, { 'value' => ';', 'class' => 'Literal' }, { 'value' => '=', 'class' => 'Literal' } ], 'class' => 'Choice' }, 'name' => 'sub-delims', 'class' => 'Rule' } ]; is_deeply($gram, $expt, "RFC 3987 grammar");