Write a JSON validator Write a program that determines whether its input is valid JSON.


Input: ASCII text: [\x00-\x7F]*

Note: if ASCII is problematic, feel free to use another encoding, but indicate it in your post.
Output: Valid or Invalid.  Trailing newline may be omitted.
Example:

$ echo '{"key": "value"}' | ./json-validate
Valid
$ echo '{key: "value"}' | ./json-validate
Invalid

Rules:


Do not use a JSON parsing library.
Partially-correct solutions are allowed, but frowned upon.
Post your test suite score (see below).



The shortest correct solution wins.

Please run json-validate-test-suite.sh on your program, and post your score.  Example:

$ ./json-validate-test-suite.sh ./buggy-prog
fail: should be invalid:  [ 0.1e ] 
fail: should be invalid:  [ 0.1e+-1 ] 
fail: should be invalid:  [ 0.1e-+1 ] 
score: 297/300


Resources:


json.org - Concise definition of the JSON grammar with easy-to-follow pictures.
RFC 4627 - JSON specification
json-validate.c - A 200-line implementation that passes the testsuite.




The JSON grammar is as follows:

json: object | array

object: '{' members? '}'
    members: pair (',' pair)*
    pair:    string ':' value

array: '[' elements? ']'
    elements: value (',' value)*

value: string
     | number
     | object
     | array
     | 'true'
     | 'false'
     | 'null'

string: '"' char* '"'
    char: [^"\\\x00-\x1F]
        | '\' escape
    escape: ["\\/bfnrt]
          | u [0-9A-Fa-f]{4}

number: '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)?


Also, whitespace can appear before or after any of the six structural characters {}[]:,

ws = [\t\n\r ]*


Bear in mind the following:


Be careful with functions like isspace().  Whitespace in JSON is [\t\n\r ], but isspace() also treats \v (vertical tab) and \f (form feed) as space.  Although word has it that isdigit() can accept more than just [0-9], should be okay to use here, as we assume input is in ASCII.
\x7F is technically a control character, but the JSON RFC doesn't mention it (it only mentions [\x00-\x1F]), and most JSON parsers tend to accept \x7F characters in strings.  Because of this ambiguity, solutions may choose to either accept them or not.
PHP : 297 285 264 253 characters

&lt;?=preg_match(&lt;&lt;&lt;'R'
~([\h
]*)({(?1)((("([^"\\\0- ]| |\\(["\\/bfnrt]|u[\dA-Fa-f]{4}))*")(?1):(?1)((?5)|-?(0|[1-9]\d*)(\.\d+)?([Ee][+-]?\d+)?|(?2)|true|false|null))(((?1),(?1))(?4))*)?}|\[(?1)((?8)((?13)(?8))*)?(?1)])(?1)\z~A
R
,`cat`)?'Valid':'Invalid';


score: 300/300

This is a full, recursive implementation of the JSON grammar.

It works only on PHP ≥ 5.3 due to nowdoc syntax (heredoc would have required to double all \).

Readable version:

(this is the same regex, with named capture groups and extended syntax):

#!/usr/bin/env php
&lt;?php

$re = &lt;&lt;&lt; 'RE'
~\A (?P&lt;ws&gt;[\t\n\r ])* (
    (?P&lt;object&gt;\{ (?P&gt;ws)*
        (?P&lt;members&gt;
            (?P&lt;pair&gt;
                (?P&lt;string&gt;
                    "(?P&lt;char&gt;
                        [^"\\\x00-\x1F]
                        |\\(?P&lt;escape&gt;
                            ["\\/bfnrt]
                            |u [0-9A-Fa-f]{4}
                        )
                    )*"
                ) (?P&gt;ws)* : (?P&gt;ws)* (?P&lt;value&gt;
                    (?P&gt;string)
                    | (?P&lt;number&gt;-? (0 | [1-9][0-9]*) (\. [0-9]+)? ([Ee] [+-]? [0-9]+)? )
                    | (?P&gt;object)
                    | (?P&gt;array)
                    | true
                    | false
                    | null
                )
            ) ( (?P&gt;ws)* , (?P&gt;ws)* (?P&gt;pair) )*
        )?
    \})
    |(?P&lt;array&gt;\[ (?P&gt;ws)*
        (?P&lt;elements&gt;
            (?P&gt;value) ( (?P&gt;ws)* , (?P&gt;ws)* (?P&gt;value) )*
        )?
    (?P&gt;ws)* \])
) (?P&gt;ws)* \z~x
RE;

if (preg_match($re, stream_get_contents(STDIN))) {
    echo 'Valid';
} else {
    echo 'Invalid';
}