Last active
November 7, 2018 17:39
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.