Here's mine, done with Treetop.  It also includes a
Readline-based interpretive checker. The generated
parser from Treetop has a slightly different interface,
so I've included JEG's test program with an adapter at
the top.

The nicest thing about using Treetop is how close the
grammar to the JSON spec :-).

I prefer to convert hash keys to symbols, but the test
cases don't allow that so I stripped out my .to_sym's.

Note that the test cases are rather limited in things
like white-space handling (and in fact the JSON spec is
actually incorrect, in that it doesn't define which rules
constitute tokens that may be separated by whitespace!)
Whitespace in Treetop must be handled explicitly, and it's
easy to miss a spot where it should be skipped, so the
tests should cover that.

I welched on full Unicode support as commented in my code,
but there's another test case you should apply, to parse
the string "\\u1234", which should throw an exception.
You'll see that my code is missing that exception, and
will misbehave instead :-).

It wasn't clear from the quiz or the JSON spec whether an
integer is valid JSON. I elected to accept any value, not
just an object or array.

Treetop now uses Polyglot, which loads the generated .rb
file if you've generated it, or the .treetop file if not.

Clifford Heath.

First, the interactive test program:

require 'treetop'
require 'json'		# Note that we can require the Treetop file directly.
require 'readline'

parser = JsonParser.new
while line = Readline::readline("? ", [])
   begin
     tree = parser.parse(line)
     if tree
       p tree.obj
     else
       puts parser.failure_reason
     end
   rescue => e
     puts e
     p e.backtrace
     p tree if tree
   end
end
puts

Now, my test adapter:

class JSONParser
   def parse(text)
     parser = JsonParser.new
     p = parser.parse(text)
     raise parser.failure_reason unless p
     p.obj
   end
end

Finally, the grammar itself:

# Treetop grammar for JSON for Ruby Quiz #155 by Clifford Heath.
grammar Json
  rule json
    value
  end

  rule object
    '{' s pairs:pairs? s '}' s
                        { def obj
                            pairs.empty? ? {} : pairs.obj
                          end
                        }
  end

  rule pairs
    member rest:(s ',' s member)*
                        { def obj
                            rest.elements.inject({eval(member.k.text_value) => member.value.obj}) { |h, e|
                                h[eval(e.member.k.text_value)] = e.member.value.obj
                                h
                              }
                          end
                        }
  end

  rule member           # key/value pair of an object
    k:string s ':' s value
  end

  rule array
    '[' s e:elements? s ']'
                        { def obj
                            e.empty? ? [] : e.obj
                          end
                        }
  end

  rule elements         # elements of an array
    value rest:(s ',' s value)*
                        { def obj
                            rest.elements.inject([value.obj]) { |a, e|
                                a << e.value.obj
                              }
                          end
                        }
  end

  rule value
    s alt:(string / number / object / array
    / 'true'            { def obj; true; end }
    / 'false'           { def obj; false; end }
    / 'null'            { def obj; nil; end }
    )
                        { def obj; alt.obj; end }
  end

  rule string
    '"' char* '"'       { def obj
                            eval(
                              # Strip Unicode characters down to the chr equivalent.
                              # Note that I'm cheating here: '"\\u4321"' should assert,
                              # and there are cases that will succeed but corrupt the data.
                              # This should be handled in the "char" rule.
                              text_value.gsub(/\\u..../) { |unicode|
                                eval("0x"+unicode[2..-1]).chr
                              }
                            )
                          end
                        }
  end

  rule char
    '\\' [\"\\\/bfnrt]
    / '\\u' hex hex hex hex
    / (![\\"] .)
  end

  rule hex
    [0-9A-Fa-f]
  end

  rule number
    int frac? exp?      { def obj; eval(text_value); end }
  end

  rule int              # Any integer
    '-'? ([1-9] [0-9]* / '0')
      { def obj; eval(text_value); end }
  end

  rule frac             # The fractional part of a floating-point number
    '.' [0-9]+
  end

  rule exp              # An exponent
    [eE] [-+]? [0-9]+
  end

  rule s                # Any amount of whtespace
    [ \t\n\t]*
  end

end