Skip to content

Instantly share code, notes, and snippets.

@shintakezou
Last active November 7, 2018 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shintakezou/01ff62a8d57e51ca11681e39862bd573 to your computer and use it in GitHub Desktop.
Save shintakezou/01ff62a8d57e51ca11681e39862bd573 to your computer and use it in GitHub Desktop.
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
Copy link
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