1grammar        = sp value:v sp end                    -> v
2
3sp             = ws*
4
5ws             = '\u0020' | eol | comment
6               | '\u0009' | '\u000B' | '\u000C' | '\u00A0' | '\uFEFF'
7               | anything:x ?( is_unicat(x, 'Zs') ) -> x
8
9eol            = '\u000D' '\u000A' | '\u000D' | '\u000A'
10               | '\u2028' | '\u2029'
11
12comment        = '//' (~eol anything)*
13               | '/*' (~'*/' anything)* '*/'
14
15value          = 'null'                               -> 'None'
16               | 'true'                               -> 'True'
17               | 'false'                              -> 'False'
18               | object:v                             -> ['object', v]
19               | array:v                              -> ['array', v]
20               | string:v                             -> ['string', v]
21               | num_literal:v                        -> ['number', v]
22
23object         = '{' sp member_list:v sp '}'          -> v
24               | '{' sp '}'                           -> []
25
26array          = '[' sp element_list:v sp ']'         -> v
27               | '[' sp ']'                           -> []
28
29string         = squote sqchar*:cs squote             -> join('', cs)
30               | dquote dqchar*:cs dquote             -> join('', cs)
31
32sqchar         = bslash esc_char:c                    -> c
33               | bslash eol                           -> ''
34               | ~bslash ~squote ~eol anything:c      -> c
35
36dqchar         = bslash esc_char:c                    -> c
37               | bslash eol                           -> ''
38               | ~bslash ~dquote ~eol anything:c      -> c
39
40bslash         = '\u005C'
41
42squote         = '\u0027'
43
44dquote         = '\u0022'
45
46esc_char       = 'b'                                 -> '\u0008'
47               | 'f'                                 -> '\u000C'
48               | 'n'                                 -> '\u000A'
49               | 'r'                                 -> '\u000D'
50               | 't'                                 -> '\u0009'
51               | 'v'                                 -> '\u000B'
52               | squote                              -> '\u0027'
53               | dquote                              -> '\u0022'
54               | bslash                              -> '\u005C'
55               | hex_esc:c                           -> c
56               | unicode_esc:c                       -> c
57
58hex_esc        = 'x' hex:h1 hex:h2                   -> xtou(h1 + h2)
59
60unicode_esc    = 'u' hex:a hex:b hex:c hex:d         -> xtou(a + b + c + d)
61
62element_list   = value:v (sp ',' sp value)*:vs sp ','?   -> [v] + vs
63
64member_list    = member:m (sp ',' sp member)*:ms sp ','? -> [m] + ms
65
66member         = string:k sp ':' sp value:v          -> [k, v]
67               | ident:k sp ':' sp value:v           -> [k, v]
68
69ident          = id_start:hd id_continue*:tl         -> join('', [hd] + tl)
70
71id_start       = ascii_id_start
72               | other_id_start
73               | bslash unicode_esc
74
75ascii_id_start = 'a'..'z'
76               | 'A'..'Z'
77               | '$'
78               | '_'
79
80other_id_start = anything:x ?(is_unicat(x, 'Ll'))    -> x
81               | anything:x ?(is_unicat(x, 'Lm'))    -> x
82               | anything:x ?(is_unicat(x, 'Lo'))    -> x
83               | anything:x ?(is_unicat(x, 'Lt'))    -> x
84               | anything:x ?(is_unicat(x, 'Lu'))    -> x
85               | anything:x ?(is_unicat(x, 'Nl'))    -> x
86
87id_continue    = ascii_id_start
88               | digit
89               | other_id_start
90               | anything:x ?(is_unicat(x, 'Mn'))    -> x
91               | anything:x ?(is_unicat(x, 'Mc'))    -> x
92               | anything:x ?(is_unicat(x, 'Nd'))    -> x
93               | anything:x ?(is_unicat(x, 'Pc'))    -> x
94               | bslash unicode_esc
95               | '\u200C'
96               | '\u200D'
97
98num_literal    = '-' num_literal:n                   -> '-' + n
99               | '+'? dec_literal:d ~id_start        -> d
100               | hex_literal
101               | 'Infinity'
102               | 'NaN'
103
104dec_literal    = dec_int_lit:d frac:f exp:e          -> d + f + e
105               | dec_int_lit:d frac:f                -> d + f
106               | dec_int_lit:d exp:e                 -> d + e
107               | dec_int_lit:d                       -> d
108               | frac:f exp:e                        -> f + e
109               | frac:f                              -> f
110
111dec_int_lit    = '0' ~digit                          -> '0'
112               | nonzerodigit:d digit*:ds            -> d + join('', ds)
113
114digit          = '0'..'9'
115
116nonzerodigit   = '1'..'9'
117
118hex_literal    = ('0x' | '0X') hex+:hs               -> '0x' + join('', hs)
119
120hex            = 'a'..'f' | 'A'..'F' | digit
121
122frac           = '.' digit*:ds                       -> '.' + join('', ds)
123
124exp            = ('e' | 'E') ('+' | '-'):s digit*:ds -> 'e' + s + join('', ds)
125               | ('e' | 'E') digit*:ds               -> 'e' + join('', ds)
126