Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
My first attempt to write and use Perl6 grammars; it seems to work (but it isn't tested extensively)
use v6.c;
# based on my original attempt, enhanced with protos
# and ideas from https://github.com/moritz/json
grammar JSON-Grammar
{
token TOP { \s* <json-value> \s* }
proto token json-value { * }
token json-value:sym<true> { <sym> }
token json-value:sym<false> { <sym> }
token json-value:sym<null> { <sym> }
token json-value:sym<string> { <json-string> }
token json-value:sym<number> { <json-number> }
token json-value:sym<array> { <json-array> }
token json-value:sym<object> { <json-object> }
token json-string { '"' ~ '"' <string-char>* }
rule json-object { '{' ~ '}' [ <object-pair>* % ',' ]? }
rule json-array { '[' ~ ']' [ <json-value>* % ',' ]? }
token json-number { <[+-]>? [ \d+ <frac-part>? | <frac-part> ] <exp-part>? }
token frac-part { '.' \d+ }
token exp-part { <[eE]> <[+-]>? \d+ }
proto token string-char { * }
token string-char:sym<escape> { <escape-char> }
token string-char:sym<regular> { <regular-char> }
proto token escape-char { * }
token escape-char:sym<single> { <backslash-char> }
token escape-char:sym<unicode> { <unicode-char> }
token backslash-char { '\\' <[\" \\ \/ b f n r t]> }
token unicode-char { '\\u' <xdigit> ** 4 }
token regular-char { <-[\\ \" \x00 .. \x1F ]> }
rule object-pair { <json-string> ':' <json-value> }
}
class JSON-to-Perl
{
my %esc-map = %('\\"/bfnrt'.comb Z=> "\\\"/\b\f\n\r\t".comb);
method TOP($/) { make $<json-value>.made }
method json-value:sym<object>($/) { make $<json-object>.made }
method json-value:sym<array>($/) { make $<json-array>.made }
method json-value:sym<string>($/) { make $<json-string>.made }
method json-value:sym<number>($/) { make $<json-number>.made }
method json-value:sym<true>($/) { make True }
method json-value:sym<false>($/) { make False }
method json-value:sym<null>($/) { make Nil }
method json-number($/) { make +~$/ }
method json-string($/) { make $/.caps».value».made .join }
method json-array($/) { make Array.new($/.caps».value».made) }
method json-object($/) { make Hash.new($/.caps».value».made) }
method object-pair($/) { make $<json-string>.made => $<json-value>.made }
method string-char:sym<regular>($/) { make $<regular-char>.made }
method regular-char($/) { make ~$/ }
method string-char:sym<escape>($/) { make $<escape-char>.made }
method escape-char:sym<single>($/) { make $<backslash-char>.made }
method escape-char:sym<unicode>($/) { make $<unicode-char>.made }
method unicode-char($/) { make utf16.new(:16(~$/.substr(2))) }
method backslash-char($/) { make %esc-map{~$/.substr(1)} }
}
sub MAIN(
Str $file-name, #= a JSON file
) {
my $p = JSON-Grammar.parse($file-name.IO.slurp,
actions => JSON-to-Perl) or die "malformed JSON?";
say $p.made.perl;
}
@shintakezou

This comment has been minimized.

Copy link
Owner Author

shintakezou commented Nov 7, 2018

I don't know what we are supposed to do when in an object the same key appears several time. This implementation makes an array, so that {"a":"b", "a":10} is the same as {"a":["b",10]}. NodeJS behaves differently: only the value given in the last key survives. So, in the example above, the final object would be {"a":10}. Moreover, NodeJS seems to preserve the ordering, while the resulting Perl6 hash is, well, a hash (not a sequence of pairs), hence the ordering isn't necessarily the same as the JSON object given as input.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.