Skip to content

Instantly share code, notes, and snippets.

@voxpelli
Last active August 22, 2023 21:55
Show Gist options
  • Save voxpelli/3af3a377ed13c2ffa7e507437974fd2b to your computer and use it in GitHub Desktop.
Save voxpelli/3af3a377ed13c2ffa7e507437974fd2b to your computer and use it in GitHub Desktop.
// JSON Grammar
// ============
//
// Based on the grammar from RFC 7159 [1].
//
// Note that JSON is also specified in ECMA-262 [2], ECMA-404 [3], and on the
// JSON website [4] (somewhat informally). The RFC seems the most authoritative
// source, which is confirmed e.g. by [5].
//
// [1] http://tools.ietf.org/html/rfc7159
// [2] http://www.ecma-international.org/publications/standards/Ecma-262.htm
// [3] http://www.ecma-international.org/publications/standards/Ecma-404.htm
// [4] http://json.org/
// [5] https://www.tbray.org/ongoing/When/201x/2014/03/05/RFC7159-JSON
// ----- 2. JSON Grammar -----
JSON_text
= ws @value ws
begin_array = "["
begin_object = "{"
end_array = "]"
end_object = "}"
name_separator = ":"
value_separator = ","
ws "whitespace" = value:[ \t\n\r]* { return value.join(''); }
// ----- 3. Values -----
value
= preValueWs:ws value:non_ws_value postValueWs:ws {
return {
preValueWs,
...value,
postValueWs
};
}
non_ws_value
= false
/ null
/ true
/ object
/ array
/ number
/ string
false = "false" { return false; }
null = "null" { return null; }
true = "true" { return true; }
// ----- 4. Objects -----
object
= "{" preInnerWs:ws
members:(
head:member
tail:("," postPropertySeparatorWs:ws value:member)*
{
return [head].concat(tail.map(([,postPropertySeparatorWs, value]) => ({ postPropertySeparatorWs, ...value })));
}
)?
postInnerWs:ws "}"
{ return members !== null ? { type: 'object', members, preInnerWs, postInnerWs }: {}; }
member
= key:string preValueSeparatorWs:ws ":" value:value {
return { key, preValueSeparatorWs, value };
}
// ----- 5. Arrays -----
array
= "[" preInnerWs:ws
values:(
head:value
tail:(value_separator @value)*
{ return [head].concat(tail); }
)?
postInnerWs:ws "]"
{ return values !== null ? { type: 'array', values, preInnerWs, postInnerWs } : []; }
// ----- 6. Numbers -----
number "number"
= minus? int frac? exp? { return parseFloat(text()); }
decimal_point
= "."
digit1_9
= [1-9]
e
= [eE]
exp
= e (minus / plus)? DIGIT+
frac
= decimal_point DIGIT+
int
= zero / (digit1_9 DIGIT*)
minus
= "-"
plus
= "+"
zero
= "0"
// ----- 7. Strings -----
string "string"
= quotation_mark chars:char* quotation_mark { return { type: 'string', value: chars.join("") } }
char
= unescaped
/ escape
sequence:(
'"'
/ "\\"
/ "/"
/ "b" { return "\b"; }
/ "f" { return "\f"; }
/ "n" { return "\n"; }
/ "r" { return "\r"; }
/ "t" { return "\t"; }
/ "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG) {
return String.fromCharCode(parseInt(digits, 16));
}
)
{ return sequence; }
escape
= "\\"
quotation_mark
= '"'
unescaped
= [^\0-\x1F\x22\x5C]
// ----- Core ABNF Rules -----
// See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4234).
DIGIT = [0-9]
HEXDIG = [0-9a-f]i
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment