# \[QUIZ\] Parsing JSON (#155)

**URL:** <https://rubytalk.org/t/quiz-parsing-json-155/44197>\
**Category:** ruby-talk\
**Created:** [1 February 2008 13:55 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197 "2008-02-01T13:55:39Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [1 February 2008 13:55 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/1 "2008-02-01T13:55:39Z")

</div>

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until  
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

[http://www.rubyquiz.com/](http://www.rubyquiz.com/)

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone  
on Ruby Talk follow the discussion. Please reply to the original quiz message,  
if you can.

> **···**
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> 
> There has been a lot of talk recently about parsing with Ruby. We're seeing  
> some parser generator libraries pop up that make the task that much easier and  
> they've been stirring up interest.
> 
> In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> 
> JSON turns out to turns out to be a great little example for writing parsers for  
> two reasons. First, it's pretty easy stuff. You can hand-roll a JSON parser in  
> under 100 lines of Ruby. The second advantage is that the data format is  
> wonderfully documented:
> 
> &nbsp;&nbsp;[http://json.org/](http://json.org/)
> 
> Since JSON is just a data format and Ruby supports all of the data types, I vote  
> we just use Ruby itself as the abstract syntax tree produced by the parse.
> 
> Feel free to show off your favorite parser generator, if you don't want to roll  
> your own. Anything goes.
> 
> Here are a few tests to get you started:
> 
> &nbsp;&nbsp;require "test/unit"  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;class TestJSONParser \< Test::Unit::TestCase  
> &nbsp;&nbsp;&nbsp;&nbsp;def setup  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser = JSONParser.new  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_keyword\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(true, @parser.parse("true"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(false, @parser.parse("false"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(nil, @parser.parse("null"))  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_number\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(42, @parser.parse("42"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-13, @parser.parse("-13"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(3.1415, @parser.parse("3.1415"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-0.01, @parser.parse("-0.01"))  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e1, @parser.parse("0.2e1"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e+1, @parser.parse("0.2e+1"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e-1, @parser.parse("0.2e-1"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2E1, @parser.parse("0.2e1"))  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_string\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(String.new, @parser.parse(%Q{""}))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("JSON", @parser.parse(%Q{"JSON"}))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( %Q{nested "quotes"},  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse('"nested \"quotes\""') )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("\n", @parser.parse(%Q{"\\n"}))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( "a",  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{"\\u#{"%04X" % ?a}"}) )  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_array\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Array.new, @parser.parse(%Q{[]}))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( ["JSON", 3.1415, true],  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{["JSON", 3.1415, true]}) )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal([1, [2, [3]]], @parser.parse(%Q{[1, [2, [3]]]}))  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_object\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Hash.new, @parser.parse(%Q{{}}))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( {"JSON" =\> 3.1415, "data" =\> true},  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{{"JSON": 3.1415, "data": true}}) )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( { "Array" =\> [1, 2, 3],  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Object" =\> {"nested" =\> "objects"} },  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(\<\<-END\_OBJECT) )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"Array": [1, 2, 3], "Object": {"nested": "objects"}}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;END\_OBJECT  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;def test\_parse\_errors  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("{") }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%q{{"key": true false}}) }  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[") }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[1,2]") }  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"}) }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"\\i"}) }  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("$1,000") }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1\_000") }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1K") }  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("unknown") }  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end

---

<div class="post-metadata">

**Author:** ![Eric\_Mahurin](https://avatars.discourse-cdn.com/v4/letter/e/b4bc9f/32.png) [@Eric\_Mahurin](https://rubytalk.org/u/Eric_Mahurin)\
**Post date:** [1 February 2008 16:09 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/2 "2008-02-01T16:09:05Z")

</div>

I definitely want to find time to do this one. What would be nice to have  
is performance benchmark to compare parsers. Maybe just have a little ruby  
script that generates a stream of repeatable random (but valid) JSON.

Eric

> **···**
>
> On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> > 
> > JSON turns out to turns out to be a great little example for writing  
> > parsers for  
> > two reasons. First, it's pretty easy stuff. You can hand-roll a JSON  
> > parser in  
> > under 100 lines of Ruby. The second advantage is that the data format is  
> > wonderfully documented:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://json.org/](http://json.org/)

---

<div class="post-metadata">

**Author:** ![7rans](https://avatars.discourse-cdn.com/v4/letter/7/a8b319/32.png) [@7rans](https://rubytalk.org/u/7rans)\
**Post date:** [1 February 2008 16:23 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/3 "2008-02-01T16:23:37Z")

</div>

A bit aside, but it seems a good place to plug the thought: JSON is so  
close to valid Ruby syntax. It would be great if Ruby could support  
the syntax 100%. Then a parse would be as simple as,

&nbsp;&nbsp;data = eval(json)

Or, safety levels withstanding, we could conceive a safe\_eval(json).

T.

---

<div class="post-metadata">

**Author:** ![steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@steve](https://rubytalk.org/u/steve)\
**Post date:** [3 February 2008 14:14 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/4 "2008-02-03T14:14:38Z")

</div>

Hey guys

This is my first parser. I used Nathan Sobo's Treetop parsing library (  
[http://treetop.rubyforge.org/](http://treetop.rubyforge.org/), gem install treetop):

[http://pastie.caboo.se/146906](http://pastie.caboo.se/146906)

require 'treetop'

File.open("json.treetop", "w") {|f| f.write GRAMMAR }

Treetop.load "json"

parser = JsonParser.new

pp parser.parse(STDIN.read).value if $0 == \_\_FILE\_\_

BEGIN {

GRAMMAR = %q{

grammar Json  
&nbsp;&nbsp;rule json  
&nbsp;&nbsp;&nbsp;&nbsp;space json\_value space { def value; json\_value.value; end }  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule json\_value  
&nbsp;&nbsp;&nbsp;&nbsp;string / numeric / keyword / object / array  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule string  
&nbsp;&nbsp;&nbsp;&nbsp;'"' chars:char\* '"' {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;chars.elements.map {|e| e.value }.join

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule char  
&nbsp;&nbsp;&nbsp;&nbsp;!'"' ('\\\\' ( ( [nbfrt"] / '\\\\' / '/' ) / 'u' hex hex hex hex )  
/ !'\\\\' .) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if text\_value[0..0] == '\\\\'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case c = text\_value[1..1]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when /[nbfrt]/

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{'n' =\> "\n", 'b' =\> "\b", 'f' =\> "\f", 'r' =\> "\r", 't' =\> "\t"}[c]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 'u'

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[text\_value[2,4].to\_i(16)].pack("L").gsub(/\0\*$/,'')  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text\_value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule hex  
&nbsp;&nbsp;&nbsp;&nbsp;[0-9a-fA-F]  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule numeric  
&nbsp;&nbsp;&nbsp;&nbsp;exp / float / integer  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule exp  
&nbsp;&nbsp;&nbsp;&nbsp;(float / integer) ('e' / 'E') ('+' / '-')? integer { def value;  
text\_value.to\_f; end }

&nbsp;&nbsp;end

&nbsp;&nbsp;rule float  
&nbsp;&nbsp;&nbsp;&nbsp;integer '.' [0-9]+ { def value; text\_value.to\_f; end }  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule integer  
&nbsp;&nbsp;&nbsp;&nbsp;'-'? ('0' / [1-9] [0-9]\*) { def value; text\_value.to\_i; end }  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule keyword  
&nbsp;&nbsp;&nbsp;&nbsp;('true' / 'false' / 'null') {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ 'true' =\> true, 'false' =\> false, 'null' =\> nil }[text\_value]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule object  
&nbsp;&nbsp;&nbsp;&nbsp;'{' space pairs:pair\* space '}' {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pairs.elements.map {|p| p.value }.inject({}) {|h,p| h.merge p }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule pair  
&nbsp;&nbsp;&nbsp;&nbsp;space string space ':' space json\_value space (',' &pair / !pair) {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ string.value =\> json\_value.value }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule array

&nbsp;&nbsp;&nbsp;&nbsp;'[' space array\_values:array\_value\* space ']' {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array\_values.elements.map {|e| e.value }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule array\_value  
&nbsp;&nbsp;&nbsp;&nbsp;space json\_value space (',' &array\_value / !array\_value) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;json\_value.value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule space  
&nbsp;&nbsp;&nbsp;&nbsp;[\t\r\n]\*  
&nbsp;&nbsp;end

end

}

}

- steve

> **···**
>
> On Feb 1, 2008 8:55 PM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > The three rules of Ruby Quiz:
> > 
> > 1. Please do not post any solutions or spoiler discussion for this quiz  
> > until  
> > 48 hours have passed from the time on this message.
> > 
> > 2. Support Ruby Quiz by submitting ideas as often as you can:
> > 
> > [http://www.rubyquiz.com/](http://www.rubyquiz.com/)
> > 
> > 3. Enjoy!
> > 
> > Suggestion: A [QUIZ] in the subject of emails about the problem helps  
> > everyone  
> > on Ruby Talk follow the discussion. Please reply to the original quiz  
> > message,  
> > if you can.
> > 
> > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> > 
> > There has been a lot of talk recently about parsing with Ruby. We're  
> > seeing  
> > some parser generator libraries pop up that make the task that much easier  
> > and  
> > they've been stirring up interest.
> > 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> > 
> > JSON turns out to turns out to be a great little example for writing  
> > parsers for  
> > two reasons. First, it's pretty easy stuff. You can hand-roll a JSON  
> > parser in  
> > under 100 lines of Ruby. The second advantage is that the data format is  
> > wonderfully documented:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://json.org/](http://json.org/)
> > 
> > Since JSON is just a data format and Ruby supports all of the data types,  
> > I vote  
> > we just use Ruby itself as the abstract syntax tree produced by the parse.
> > 
> > Feel free to show off your favorite parser generator, if you don't want to  
> > roll  
> > your own. Anything goes.
> > 
> > Here are a few tests to get you started:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require "test/unit"
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class TestJSONParser \< Test::Unit::TestCase  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def setup  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser = JSONParser.new  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_keyword\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(true, @parser.parse("true"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(false, @parser.parse("false"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(nil, @parser.parse("null"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_number\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(42, @parser.parse("42"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-13, @parser.parse("-13"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(3.1415, @parser.parse("3.1415"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-0.01, @parser.parse("-0.01"))
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e1, @parser.parse("0.2e1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e+1, @parser.parse("0.2e+1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e-1, @parser.parse("0.2e-1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2E1, @parser.parse("0.2e1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_string\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(String.new, @parser.parse(%Q{""}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("JSON", @parser.parse(%Q{"JSON"}))
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( %Q{nested "quotes"},  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse('"nested \"quotes\""') )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("\n", @parser.parse(%Q{"\\n"}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( "a",  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{"\\u#{"%04X" % ?a}"}) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_array\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Array.new, @parser.parse(%Q{}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( ["JSON", 3.1415, true],  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{["JSON", 3.1415, true]}) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal([1, [2, [3]]], @parser.parse(%Q{[1, [2, [3]]]}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_object\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Hash.new, @parser.parse(%Q{{}}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( {"JSON" =\> 3.1415, "data" =\> true},  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{{"JSON": 3.1415, "data": true}})  
> > )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( { "Array" =\> [1, 2, 3],  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Object" =\> {"nested" =\> "objects"} },  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(\<\<-END\_OBJECT) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"Array": [1, 2, 3], "Object": {"nested": "objects"}}  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;END\_OBJECT  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_parse\_errors  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("{") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%q{{"key": true  
> > false}}) }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[1,2]") }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"}) }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"\\i"}) }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("$1,000") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1\_000") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1K") }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("unknown") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

---

<div class="post-metadata">

**Author:** ![Justin\_Ethier](https://avatars.discourse-cdn.com/v4/letter/j/c67d28/32.png) [@Justin\_Ethier](https://rubytalk.org/u/Justin_Ethier)\
**Post date:** [3 February 2008 15:12 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/5 "2008-02-03T15:12:46Z")

</div>

Here is my solution. I do a first pass to tokenize the input and perform  
basic syntax checks. Then the expression is fully converted into ruby syntax  
and eval is used to load it into Ruby. It passes each of the test cases,  
although some improvements could still be made.

class JSONParser  
&nbsp;&nbsp;# Parse a given JSON expression  
&nbsp;&nbsp;def parse(expr)  
&nbsp;&nbsp;&nbsp;&nbsp;# Tokenize the input  
&nbsp;&nbsp;&nbsp;&nbsp;tokens = lex(expr)

&nbsp;&nbsp;&nbsp;&nbsp;# Load the expression into ruby  
&nbsp;&nbsp;&nbsp;&nbsp;# Takes advantage of the fact ruby syntax is so close to that of JSON.  
&nbsp;&nbsp;&nbsp;&nbsp;# However, it would be nice to have a safe\_eval to prevent against  
potential injection attacks  
&nbsp;&nbsp;&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eval(ruby\_convert(tokens))  
&nbsp;&nbsp;&nbsp;&nbsp;rescue SyntaxError, NameError  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise RuntimeError  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end

&nbsp;&nbsp;# Converts tokens into a single ruby expression  
&nbsp;&nbsp;def ruby\_convert(tokens)  
&nbsp;&nbsp;&nbsp;&nbsp;expr = ""  
&nbsp;&nbsp;&nbsp;&nbsp;for token in tokens  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;token = "=\>" if token == ":" # Ruby hash syntax  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;token = "nil" if token == "null"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expr += token  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;expr  
&nbsp;&nbsp;end

&nbsp;&nbsp;# Parses the input expression into a series of tokens  
&nbsp;&nbsp;# Performs some limited forms of conversion where necessary  
&nbsp;&nbsp;def lex(expr)  
&nbsp;&nbsp;&nbsp;&nbsp;tokens = []  
&nbsp;&nbsp;&nbsp;&nbsp;i = -1  
&nbsp;&nbsp;&nbsp;&nbsp;while i \< expr.size - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tok ||= ""  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case expr[i].chr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when '[', ']', '{', '}', ':', ','  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tokens \<\< tok if tok.size \> 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tokens \<\< expr[i].chr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tok = ""  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# String processing  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when '"'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Unexpected quote" if tok.size \> 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len = 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;escaped = false  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (len + i) \< expr.size  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break if expr[len + i].chr == '"' and not escaped  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if escaped  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case expr[len + i].chr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when '"', '/', '\\', 'b', 'f', 'n', 'r', 't', 'u'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Unable to escape #{expr[len + i].chr}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;escaped = expr[len + i].chr == "\\"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "No matching endquote for string" if (len + i) \> expr.size  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tokens \<\< convert\_unicode(expr.slice(i, len+1))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += len  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Number processing  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when '-', /[0-9]/  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len = 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (len + i) \< expr.size and /[0-9eE+-.]/.match(expr[len +  
i].chr)!= nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num = expr.slice(i, len)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Verify syntax of the number using the JSON state machine  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Invalid number #{num}" if  
/[-]?([1-9]|(0\.))[0-9]\*[eE]?[+-]?[0-9]\*/.match(num) == nil

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tokens \<\< num  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += len - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Skip whitespace  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ' ', '\t'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tok \<\< expr[i].chr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;tokens \<\< tok if tok.size \> 0  
&nbsp;&nbsp;&nbsp;&nbsp;tokens  
&nbsp;&nbsp;end

&nbsp;&nbsp;# Convert unicode characters from hex (currently only handles ASCII set)  
&nbsp;&nbsp;def convert\_unicode(str)  
&nbsp;&nbsp;&nbsp;&nbsp;while true  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u\_idx = str.index(/\\u[0-9a-fA-F]{4}/)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break if u\_idx == nil

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u\_str = str.slice(u\_idx, 6)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str.sub!(u\_str, u\_str[2..5].hex.chr)  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;str  
&nbsp;&nbsp;end  
end

Thanks,

Justin

---

<div class="post-metadata">

**Author:** ![ThoML](https://avatars.discourse-cdn.com/v4/letter/t/d78d45/32.png) [@ThoML](https://rubytalk.org/u/ThoML)\
**Post date:** [3 February 2008 16:24 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/6 "2008-02-03T16:24:57Z")

</div>

I liked this quiz because it made me look into treetop, ragel and  
some  
other libraries I wanted to examine a little bit closer for quite a  
while now. Anyway, for my (official) solution I took the easy road  
and  
rely on ruby to do the actual work.

My solution is ruby19 only, since I used the opportunity to explore  
some  
of the new regexp features. Since it uses eval(), there is a  
possibility  
for ruby code injection like the ultimatively relieving "#{`sudo rm -  
rf  
/`}". I think my solution catches such attacks though.

BTW, what do you all think should be the canonic output of the  
following  
JSON snippet:

json1 = \<\<JSON  
{"a":2,"b":3.141,"TIME":"2007-03-14T11:52:40","c":"c","d":[1,"b",  
3.14],"COUNT":666,"e":{"foo":"bar"},"foo":"B\\u00e4r","g":"\\u677e\  
\u672c\\u884c\\u5f18","h":1000.0,"bar":"\\u00a9 \\u2260 \\u20ac!","i":  
0.001,"j":"\\ud840\\udc01"}  
JSON

I get conflicting results between various versions of my solution and  
the official ruby19 parser with respect to these utf characters. This  
snippet is taken (IIRC) from the ruby-json parser.

Regards,  
Thomas.

#!/usr/bin/env ruby19  
# Author:: Thomas Link (micathom AT gmail com)  
# Created:: 2008-02-01.

# The string (in JSON format) is tokenized and pre-validated. Minor  
# replacements are made in order to transform the JSON into valid  
ruby  
# input. The transformed string is then evaluated by ruby, which will  
# throw an exception on syntactic errors.

> **···**
>
> #  
> # PROBLEMS:  
> # - The "parser" doesn't per se detect something like {"foo": 1,} or  
> # [1,2,] since this is valid in ruby. I'm not sure about JSON. Anyway,  
> I  
> # included another "invalid" clause in order to catch these cases of  
> # which I'm not sure how they are handled properly. If you want the  
> # parser to be more permissive, remove the first "invalid" clause.  
> #  
> # REFERENCES:  
> # [http://json.org](http://json.org)  
> # [http://www.ietf.org/rfc/rfc4627.txt](http://www.ietf.org/rfc/rfc4627.txt)  
> class JSONParser
> 
> &nbsp;&nbsp;&nbsp;&nbsp;RXE = /  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\[|\]|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\{|\}|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?\<name\_sep\>:)|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?\<invalid\>,\s\*[}\]])|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;,|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?\<string\>"([^"\\]++|\\(u[0-9a-fA-F]{4}|[bfnrt"\/\\]))\*")|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-?(0|[1-9]\d\*+)(\.\d++)?([Ee][+-]?\d++)?(?=\D|$)|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;false\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?\<null\>null)|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[:space:][:cntrl:]]++|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?\<invalid\>.++)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/xmu
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def parse(json)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ruby = json.gsub(RXE) do |t|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m = $~  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if m['invalid'] then invalid(m['invalid'])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif m['null'] then 'nil'  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif m['name\_sep'] then '=\>'  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif m['string'] then m['string'].gsub(/#/, '\\\\#')  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;begin  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return eval(ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rescue Exception =\> e  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalid(json)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def invalid(string)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise RuntimeError, 'Invalid JSON: %s' % string  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> end
> 
> if \_\_FILE\_\_ == $0  
> &nbsp;&nbsp;&nbsp;&nbsp;a = ARGV.join  
> &nbsp;&nbsp;&nbsp;&nbsp;p a  
> &nbsp;&nbsp;&nbsp;&nbsp;p JSONParser.new.parse(a)  
> end

---

<div class="post-metadata">

**Author:** ![Eric\_Mahurin](https://avatars.discourse-cdn.com/v4/letter/e/b4bc9f/32.png) [@Eric\_Mahurin](https://rubytalk.org/u/Eric_Mahurin)\
**Post date:** [3 February 2008 16:51 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/7 "2008-02-03T16:51:44Z")

</div>

> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://json.org/](http://json.org/)

This doc is missing two things: 1) exactly what is allowed for the top-level  
json, and 2) where can whitespace appear. This is more complete:

[http://www.ietf.org/rfc/rfc4627.txt](http://www.ietf.org/rfc/rfc4627.txt)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_keyword\_parsing

> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(true, @parser.parse("true"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(false, @parser.parse("false"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(nil, @parser.parse("null"))  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_number\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(42, @parser.parse("42"))

...

> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_string\_parsing  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(String.new, @parser.parse(%Q{""}))

...

> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

The above isn't legal JSON. An array or object (hash) should be at the  
top-level. Surround these by brackets and it will be legal.

> **···**
>
> On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:

---

<div class="post-metadata">

**Author:** ![Pawel\_Radecki](https://avatars.discourse-cdn.com/v4/letter/p/dec6dc/32.png) [@Pawel\_Radecki](https://rubytalk.org/u/Pawel_Radecki)\
**Post date:** [3 February 2008 18:59 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/8 "2008-02-03T18:59:56Z")

</div>

And here's my solution. It passes all given tests but still there are  
some tricky cases not handled well (see: code comments). I used  
regular expressions extensively but it may not be the best idea in  
terms of performance.

Thanks for the quiz!!

#!/usr/bin/env ruby

# Solution to Ruby Quiz #155 (see [http://www.rubyquiz.com/quiz155.html](http://www.rubyquiz.com/quiz155.html))  
# by Paweł Radecki ([pawel.j.radecki@gmail.com](mailto:pawel.j.radecki@gmail.com)).

$KCODE='UTF-8'  
require 'jcode'

class JSONParser

&nbsp;&nbsp;def parse(input)  
&nbsp;&nbsp;&nbsp;&nbsp;case input  
&nbsp;&nbsp;&nbsp;&nbsp;# TODO: in every case we need to check if pattern matches the  
input thoroughly and nothing is left;  
&nbsp;&nbsp;&nbsp;&nbsp;# ex. "[3, 5] Pablos" still not handled well, it passes through  
instead of giving exception

&nbsp;&nbsp;&nbsp;&nbsp;when '' : raise RuntimeError

&nbsp;&nbsp;&nbsp;&nbsp;# TODO: There needs to be some smart way of choosing whether we  
found an object or an array;  
&nbsp;&nbsp;&nbsp;&nbsp;# now object has priority and it may be found instead of an  
array

&nbsp;&nbsp;&nbsp;&nbsp;#object  
&nbsp;&nbsp;&nbsp;&nbsp;when /\{(".+"):(.+)\s\*(,\s\*(".+"):(.+))+\}/ then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h = Hash.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$&[1...-1].split(/(.\*:.\*)?\s\*,\s\*(.\*:.\*)?/).each do |e|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a = e.split(/:\s\*(\{.\*\}\s\*)?/);  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[parse(a.first)] = parse(a.last) unless (a.first.nil? &&  
a.last.nil?)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h  
&nbsp;&nbsp;&nbsp;&nbsp;when /\{\s\*(".+")\s\*:\s\*(.+)\s\*\}/ then { parse($1) =\> parse($2) }  
&nbsp;&nbsp;&nbsp;&nbsp;when /\{\s\*\}/ : Hash.new

&nbsp;&nbsp;&nbsp;&nbsp;#array  
&nbsp;&nbsp;&nbsp;&nbsp;when /\[.+\]/ then $&[1...-1].split(/(\[.\*\])?\s\*,\s\*(\[.\*  
\])?/).collect{|e| parse(e)}  
&nbsp;&nbsp;&nbsp;&nbsp;when /\[\s\*\]/ then []

&nbsp;&nbsp;&nbsp;&nbsp;#constants  
&nbsp;&nbsp;&nbsp;&nbsp;when /true/ then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($`.strip.empty? && $'.strip.empty?) then true else raise  
RuntimeError end  
&nbsp;&nbsp;&nbsp;&nbsp;when /false/ then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($`.strip.empty? && $'.strip.empty?) then false else raise  
RuntimeError end  
&nbsp;&nbsp;&nbsp;&nbsp;when /null/ then nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($`.strip.empty? && $'.strip.empty?) then nil else raise  
RuntimeError end

&nbsp;&nbsp;&nbsp;&nbsp;#string  
&nbsp;&nbsp;&nbsp;&nbsp;when /"([A-Za-z]|(\s)|(\\")|(\\\\)|(\\\/)|(\\b)|(\\f)|(\\n)|(\\r)|  
(\\t)|(\\u[0-9a-fA-F]{4,4}))+"/ : $&[1...-1].gsub(/\\"/, '"').gsub(/\  
\n/, "\n").gsub(/\\u([0-9a-fA-F]{4,4})/u){["#$1".hex].pack('U\*')}  
&nbsp;&nbsp;&nbsp;&nbsp;when /""/ then ""

&nbsp;&nbsp;&nbsp;&nbsp;#number  
&nbsp;&nbsp;&nbsp;&nbsp;when /-?(0|([1-9][0-9]\*))(\.[0-9]+)?([e|E][+|-]?[0-9]+)?/ then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($`.strip.empty? && $'.strip.empty?) then eval($&) else raise  
RuntimeError end  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise RuntimeError  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
end

#puts JSONParser.new.parse(ARGV.first)

> **···**
>
> --  
> Paweł Radecki  
> e: [pawel.j.radecki@gmail.com](mailto:pawel.j.radecki@gmail.com)  
> w: [http://radeckimarch.blogspot.com/](http://radeckimarch.blogspot.com/)

---

<div class="post-metadata">

**Author:** ![Eric\_Mahurin](https://avatars.discourse-cdn.com/v4/letter/e/b4bc9f/32.png) [@Eric\_Mahurin](https://rubytalk.org/u/Eric_Mahurin)\
**Post date:** [4 February 2008 00:30 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/9 "2008-02-04T00:30:24Z")

</div>

My first solution uses an parser generator API similar to my rubyforge  
"grammar" package. The parser generator is only 89 lines (excluding  
comment/blank lines). The API is fairly complete to build a variety of  
parsers. The same API can be use to build a parser, lexer, preprocessor,  
etc (and you could multi-thread them). The reason this is so simple is that  
I'm using Ruby as a DSL to specify the language grammar. The file  
specifying the JSONParser class is only 58 lines.

Here is the simple Grammar0 parsing DSL class and the JSONParser class using  
it:

[http://pastie.caboo.se/147074](http://pastie.caboo.se/147074)

[http://pastie.caboo.se/147075](http://pastie.caboo.se/147075)

[grammar0.rb](https://rubytalk.org/uploads/short-url/aV3nMAjS9q2xuAgiNWykbgov9Fj.rb) (3.78 KB)

[jsonparser.grammar0.rb](https://rubytalk.org/uploads/short-url/jwNEUbwIkRIoVlCJP1N1MMg5nRs.rb) (2.17 KB)

> **···**
>
> On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.

---

<div class="post-metadata">

**Author:** ![Eric\_Mahurin](https://avatars.discourse-cdn.com/v4/letter/e/b4bc9f/32.png) [@Eric\_Mahurin](https://rubytalk.org/u/Eric_Mahurin)\
**Post date:** [4 February 2008 00:50 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/10 "2008-02-04T00:50:16Z")

</div>

Here is another solution that uses my (somewhat) optimized 'grammar' package  
directly. The JSON grammar is similar to before. The next release will use  
an API closer to the simple Grammar0 package.

[http://pastie.caboo.se/147078](http://pastie.caboo.se/147078)

Also, I ported this to my development code (which is not checked in to CVS  
yet), to see the performance. I grabbed all of the current submissions  
along with the fjson and json gems to see how they compare in terms of  
performance. I used the previous benchmark that I posted. It also revealed  
bugs in these submissions. Here is the performance I found on my machine  
with ruby 1.8.6:

ch/s author/gem  
---- ----------  
- oksteev (TreeTop, couldn't get it to parse a string)  
- Pawel Radecki (RE, mismatch)  
- Justin Ethier (RE lexer + parser, 71: Invalid number 0)  
4054 Eric Mahurin (Grammar0, no lexer, no parser generation)  
54586 Eric Mahurin (Grammar, no lexer, v0.5)  
166041 Thomas Link (RE, ruby 1.9 results)  
220289 json  
223486 Eric Mahurin (Grammar, no lexer, unreleased)  
224823 fjson (uses C extensions)  
553081 Eric Mahurin (Grammar, no lexer, unreleased, w/ ruby2cext)  
1522250 json (w/ C extensions)

Note that the Grammar variants don't have the advantage of using RegExp  
where you get some C performance. But, in my dev code, I'm using ruby2cext  
to get a little more performance. You could integrate a RegExp lexer with a  
Grammar parser, also.

> **···**
>
> On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.

---

<div class="post-metadata">

**Author:** ![Clifford\_Heath3](https://avatars.discourse-cdn.com/v4/letter/c/ec9cab/32.png) [@Clifford\_Heath3](https://rubytalk.org/u/Clifford_Heath3)\
**Post date:** [4 February 2008 01:09 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/11 "2008-02-04T01:09:59Z")

</div>

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("? ", [])  
&nbsp;&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tree = parser.parse(line)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if tree  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p tree.obj  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts parser.failure\_reason  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;rescue =\> e  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts e  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p e.backtrace  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p tree if tree  
&nbsp;&nbsp;&nbsp;end  
end  
puts

Now, my test adapter:

class JSONParser  
&nbsp;&nbsp;&nbsp;def parse(text)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parser = JsonParser.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p = parser.parse(text)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise parser.failure\_reason unless p  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.obj  
&nbsp;&nbsp;&nbsp;end  
end

Finally, the grammar itself:

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

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

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

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

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

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

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

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

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

&nbsp;&nbsp;rule hex  
&nbsp;&nbsp;&nbsp;&nbsp;[0-9A-Fa-f]  
&nbsp;&nbsp;end

&nbsp;&nbsp;rule number  
&nbsp;&nbsp;&nbsp;&nbsp;int frac? exp? { def obj; eval(text\_value); end }  
&nbsp;&nbsp;end

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

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

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

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

end

---

<div class="post-metadata">

**Author:** ![steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@steve](https://rubytalk.org/u/steve)\
**Post date:** [4 February 2008 04:14 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/12 "2008-02-04T04:14:45Z")

</div>

Here are some extra unit tests i wrote. Let me know if you think any of  
them are incorrect (per the spec):

def test\_more\_numbers  
assert\_equal(5, @parser.parse("5"))  
assert\_equal(-5, @parser.parse("-5"))  
assert\_equal 45.33, @parser.parse("45.33")  
assert\_equal 0.33, @parser.parse("0.33")  
assert\_equal 0.0, @parser.parse("0.0")  
assert\_equal 0, @parser.parse("0")  
assert\_raises(RuntimeError) { @parser.parse("-5.-4") }  
assert\_raises(RuntimeError) { @parser.parse("01234") }  
assert\_equal(0.2e1, @parser.parse("0.2E1"))  
assert\_equal(42e10, @parser.parse("42E10"))  
end

def test\_more\_string  
&nbsp;&nbsp;assert\_equal("abc\befg", @parser.parse(%Q{"abc\\befg"}))  
&nbsp;&nbsp;assert\_equal("abc\nefg", @parser.parse(%Q{"abc\\nefg"}))  
&nbsp;&nbsp;assert\_equal("abc\refg", @parser.parse(%Q{"abc\\refg"}))  
&nbsp;&nbsp;assert\_equal("abc\fefg", @parser.parse(%Q{"abc\\fefg"}))  
&nbsp;&nbsp;assert\_equal("abc\tefg", @parser.parse(%Q{"abc\\tefg"}))  
&nbsp;&nbsp;assert\_equal("abc\\efg", @parser.parse(%Q{"abc\\\\efg"}))  
&nbsp;&nbsp;assert\_equal("abc/efg", @parser.parse(%Q{"abc\\/efg"}))  
end

def test\_more\_object\_parsing  
&nbsp;&nbsp;assert\_equal({'a'=\>2,'b'=\>4}, @parser.parse(%Q{{ "a" : 2 , "b":4 }}))  
&nbsp;&nbsp;assert\_raises(RuntimeError) { @parser.parse(%Q{{ "a" : 2, }}) }  
&nbsp;&nbsp;assert\_raises(RuntimeError) { @parser.parse(%Q{["a" , 2,]}) }  
end

> **···**
>
> On Feb 1, 2008 8:55 PM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > The three rules of Ruby Quiz:
> > 
> > 1. Please do not post any solutions or spoiler discussion for this quiz  
> > until  
> > 48 hours have passed from the time on this message.
> > 
> > 2. Support Ruby Quiz by submitting ideas as often as you can:
> > 
> > [http://www.rubyquiz.com/](http://www.rubyquiz.com/)
> > 
> > 3. Enjoy!
> > 
> > Suggestion: A [QUIZ] in the subject of emails about the problem helps  
> > everyone  
> > on Ruby Talk follow the discussion. Please reply to the original quiz  
> > message,  
> > if you can.
> > 
> > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> > 
> > There has been a lot of talk recently about parsing with Ruby. We're  
> > seeing  
> > some parser generator libraries pop up that make the task that much easier  
> > and  
> > they've been stirring up interest.
> > 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> > 
> > JSON turns out to turns out to be a great little example for writing  
> > parsers for  
> > two reasons. First, it's pretty easy stuff. You can hand-roll a JSON  
> > parser in  
> > under 100 lines of Ruby. The second advantage is that the data format is  
> > wonderfully documented:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://json.org/](http://json.org/)
> > 
> > Since JSON is just a data format and Ruby supports all of the data types,  
> > I vote  
> > we just use Ruby itself as the abstract syntax tree produced by the parse.
> > 
> > Feel free to show off your favorite parser generator, if you don't want to  
> > roll  
> > your own. Anything goes.
> > 
> > Here are a few tests to get you started:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require "test/unit"
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class TestJSONParser \< Test::Unit::TestCase  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def setup  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser = JSONParser.new  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_keyword\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(true, @parser.parse("true"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(false, @parser.parse("false"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(nil, @parser.parse("null"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_number\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(42, @parser.parse("42"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-13, @parser.parse("-13"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(3.1415, @parser.parse("3.1415"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(-0.01, @parser.parse("-0.01"))
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e1, @parser.parse("0.2e1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e+1, @parser.parse("0.2e+1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2e-1, @parser.parse("0.2e-1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(0.2E1, @parser.parse("0.2e1"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_string\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(String.new, @parser.parse(%Q{""}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("JSON", @parser.parse(%Q{"JSON"}))
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( %Q{nested "quotes"},  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse('"nested \"quotes\""') )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("\n", @parser.parse(%Q{"\\n"}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( "a",  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{"\\u#{"%04X" % ?a}"}) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_array\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Array.new, @parser.parse(%Q{}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( ["JSON", 3.1415, true],  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{["JSON", 3.1415, true]}) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal([1, [2, [3]]], @parser.parse(%Q{[1, [2, [3]]]}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_object\_parsing  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(Hash.new, @parser.parse(%Q{{}}))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( {"JSON" =\> 3.1415, "data" =\> true},  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(%Q{{"JSON": 3.1415, "data": true}})  
> > )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal( { "Array" =\> [1, 2, 3],  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Object" =\> {"nested" =\> "objects"} },  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@parser.parse(\<\<-END\_OBJECT) )  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"Array": [1, 2, 3], "Object": {"nested": "objects"}}  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;END\_OBJECT  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_parse\_errors  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("{") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%q{{"key": true  
> > false}}) }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("[1,2]") }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"}) }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse(%Q{"\\i"}) }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("$1,000") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1\_000") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("1K") }
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_raise(RuntimeError) { @parser.parse("unknown") }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

---

<div class="post-metadata">

**Author:** ![Paolo\_Bonzini](https://avatars.discourse-cdn.com/v4/letter/p/49beb7/32.png) [@Paolo\_Bonzini](https://rubytalk.org/u/Paolo_Bonzini)\
**Post date:** [4 February 2008 10:05 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/13 "2008-02-04T10:05:09Z")

</div>

Here is my try using regexes. I use the "copy-on-write trick" from  
the suffix tree quiz: the regex is always anchored to the beginning of  
the string using \A, and the matched text is discarded using  
post\_match. In some places where I don't want to discard I use (?  
=...).

Using Eric's benchmark I get 36kb/sec, but I haven't benchmarked any  
other solution.

[http://pastie.caboo.se/147201](http://pastie.caboo.se/147201)

Paolo

---

<div class="post-metadata">

**Author:** ![7rans](https://avatars.discourse-cdn.com/v4/letter/7/a8b319/32.png) [@7rans](https://rubytalk.org/u/7rans)\
**Post date:** [4 February 2008 13:51 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/14 "2008-02-04T13:51:06Z")

</div>

Just an FYI solution, JSON is a subset of YAML. So

&nbsp;&nbsp;data = YAML.load(json)

T.

---

<div class="post-metadata">

**Author:** ![Eric\_Mahurin](https://avatars.discourse-cdn.com/v4/letter/e/b4bc9f/32.png) [@Eric\_Mahurin](https://rubytalk.org/u/Eric_Mahurin)\
**Post date:** [4 February 2008 23:07 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/15 "2008-02-04T23:07:52Z")

</div>

Here is another solution of mine:

[http://pastie.caboo.se/147505](http://pastie.caboo.se/147505)

In this one, I just made a fast hand-built recursive-descent/LL(1) parser.  
This is the kind of parser that I'm trying to get my 'grammar' package to  
approach (using lots of optimizations). It uses no Regexp or ruby eval  
(both of which have compiled C to help speed). And yet, it is the fastest  
pure-ruby JSON parser we've seen (see the recursive descent line below):

ch/s author/gem  
---- ----------  
- Pawel Radecki (RE, mismatch)  
3214 Justin Ethier (RE lexer + ruby eval, fixed number parsing)  
4054 Eric Mahurin (Grammar0, no lexer, no parser generation)  
4078 Eric I (Treetop, unicode broken)  
6534 oksteev (Treetop, mismatches in benchmark)  
8313 Clifford Heath (Treetop, had to remove handling of "\/")  
17320 Alexander Stedile (RE)  
54586 Eric Mahurin (Grammar, no lexer, v0.5)  
137989 Paolo Bonzini (RE)  
166041 Thomas Link (RE lexer + ruby eval, ruby 1.9 results)  
220289 json  
223486 Eric Mahurin (Grammar, no lexer, unreleased)  
224823 fjson (uses C extensions)  
333368 Thomas Link & Paolo Bonzini (RE + eval, unicode broken)  
388670 Eric Mahurin (hand-built recursive descent)  
553081 Eric Mahurin (Grammar, no lexer, unreleased, w/ ruby2cext)  
1522250 json (w/ C extensions)

> **···**
>
> On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> 
> #  
> # JSON hand-built recursive descent/LL(1) parser, by Eric Mahurin  
> #  
> require 'stringio'
> 
> class JSONParser
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def parse(s)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = (@io=StringIO.new(s)).getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value(out=)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise("EOF expected") if @next  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise(out.inspect) unless out.length==1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out[0]  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def error(expected, found)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise("expected #{expected}, found #{found ? ("'"\<\<found\<\<?\') :  
> 'EOF'}")  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def value(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ?\[.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# array  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a =   
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unless ?\].equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value(a)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;until ?\].equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?\,.equal?(@next) ? (@next=@io.getc) : error("','",  
> @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value(a)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out \<\< a  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif ?\{.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# object  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h = {}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unless ?\}.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?\".equal?(@next) ? string(kv=) : error("a string", @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?\:.equal?(@next) ? (@next=@io.getc) : error("':'", @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value(kv)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[kv[0]] = kv[1]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;until ?\}.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?,.equal?(@next) ? (@next=@io.getc) : error("','",  
> @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?\".equal?(@next) ? string(kv.clear) : error("a string",  
> @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?\:.equal?(@next) ? (@next=@io.getc) : error("':'",  
> @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value(kv)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[kv[0]] = kv[1]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out \<\< h  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif (?a..?z)===(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# boolean  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s="")\<\<@next  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (?a..?z)===(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s\<\<@next;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out \<\< case s  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "true" then true  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "false" then false  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "null" then nil  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else error("'true' or 'false' or 'null'", s)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif ?\".equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# number  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n = ""  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(n\<\<@next;@next=@io.getc) if ?-.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?0.equal?(@next) ? (n\<\<@next;@next=@io.getc) : digits(n)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?..equal?(@next) ?  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(n\<\<@next;@next=@io.getc;digits(n);exp(n);true) :  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exp(n)) ?  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(out \<\< n.to\_f) :  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(out \<\< n.to\_i)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;# Flattening any of the methods below will improve performance further
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def ws  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc while (case @next;when ?\s,?\t,?\n,?\r;true;end)  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def digits(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?0..?9)===@next ? (out\<\<@next;@next=@io.getc) : error("a digit",  
> @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (?0..?9)===@next; (out\<\<@next;@next=@io.getc); end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def exp(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case @next;when ?e,?E;true;end) ? (out\<\<@next;@next=@io.getc) :  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(out\<\<@next;@next=@io.getc) if (case @next;when ?-,?+;true;end)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digits(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;def string(out)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# we've already verified the starting "  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s = ""  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;until ?\".equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ?\\.equal?(@next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case @next  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?\",?\\,?\/ then (s\<\<@next;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?b then (s\<\<?\b;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?f then (s\<\<?\f;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?n then (s\<\<?\n;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?r then (s\<\<?\r;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?t then (s\<\<?\t;@next=@io.getc)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?u  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u = ""  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.times {  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case @next  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?0..?9, ?a..?f, ?A..?F  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u\<\<@next;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error("a hex character", @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s \<\< u.to\_i(16)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error("a valid escape", @next)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error("a character", @next) unless @next  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s\<\<@next;@next=@io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@next = @io.getc  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out \<\< s  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> end

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [1 February 2008 16:12 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/16 "2008-02-01T16:12:54Z")

</div>

Neat idea.

Just FYI though, I'm probably going to focus more on the parsing in the summary that the speed.

James Edward Gray II

> **···**
>
> On Feb 1, 2008, at 10:09 AM, Eric Mahurin wrote:
> 
> > On Feb 1, 2008 7:55 AM, Ruby Quiz \<james@grayproductions.net\> wrote:
> > 
> > > In honor of that, this week's Ruby Quiz is to write a parser for JSON.
> > > 
> > > JSON turns out to turns out to be a great little example for writing  
> > > parsers for  
> > > two reasons. First, it's pretty easy stuff. You can hand-roll a JSON  
> > > parser in  
> > > under 100 lines of Ruby. The second advantage is that the data format is  
> > > wonderfully documented:
> > > 
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://json.org/](http://json.org/)
> > 
> > I definitely want to find time to do this one. What would be nice to have  
> > is performance benchmark to compare parsers. Maybe just have a little ruby  
> > script that generates a stream of repeatable random (but valid) JSON.

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [1 February 2008 16:28 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/17 "2008-02-01T16:28:44Z")

</div>

Conversion is pretty easy and definitely one way to solve this quiz.

James Edward Gray II

> **···**
>
> On Feb 1, 2008, at 10:23 AM, Trans wrote:
> 
> > A bit aside, but it seems a good place to plug the thought: JSON is so  
> > close to valid Ruby syntax. It would be great if Ruby could support  
> > the syntax 100%.

---

<div class="post-metadata">

**Author:** ![PA1](https://avatars.discourse-cdn.com/v4/letter/p/71c47a/32.png) [@PA1](https://rubytalk.org/u/PA1)\
**Post date:** [1 February 2008 16:34 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/18 "2008-02-01T16:34:53Z")

</div>

Brilliant!

But perhaps the other way around: bridge the JSON syntax discrepencies to valid Ruby syntax, e.g:

eval( to\_ruby( json ) )

Cheers,

PA.

> **···**
>
> On Feb 1, 2008, at 5:23 PM, Trans wrote:
> 
> > A bit aside, but it seems a good place to plug the thought: JSON is so  
> > close to valid Ruby syntax. It would be great if Ruby could support  
> > the syntax 100%. Then a parse would be as simple as,
> > 
> > data = eval(json)

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [1 February 2008 17:30 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/19 "2008-02-01T17:30:26Z")

</div>

cfp2:~ \> cat a.rb  
require 'rubygems'  
require 'json'

def random\_json  
&nbsp;&nbsp;&nbsp;case rand  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 0 ... 1/3.0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top = Hash.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add = lambda{|obj| top[obj] = obj}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 1/3.0 ... 2/3.0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top = Array.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add = lambda{|obj| top.push obj}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 2/3.0 .. 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top = String.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add = lambda{|obj| top += obj}  
&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;10.times{ add[rand.to\_s] }  
&nbsp;&nbsp;&nbsp;top.to\_json  
end

puts random\_json

cfp2:~ \> for i in `seq 1 3`;do ruby a.rb ;done  
"0.3786779826911330.2475380034343990.7052927081471540.2056530009384740.1367079874315110.6433874613518640.5329060341883540.8932613322492760.9233991888762390.561470121133217  
"  
{"0.758942077040095":"0.758942077040095","0.740998718448961":"0.740998718448961","0.581975309640819":"0.581975309640819","0.471066491788047":"0.471066491788047","0.150752108985123":"0.150752108985123","0.679712508205116":"0.679712508205116","0.265444532310993":"0.265444532310993","0.43229805237576":"0.43229805237576","0.880407977937905":"0.880407977937905","0.91896885679168":"0.91896885679168"}  
["0.140526101058637  
","0.647296447390116  
","0.419874655921874  
","0.67320818546074  
","0.847043108967541  
","0.479385904117001  
","0.378678170026127  
","0.707315391952609","0.26064520446906","0.460184583302929"]

a @ [http://codeforpeople.com/](http://codeforpeople.com/)

> **···**
>
> On Feb 1, 2008, at 9:09 AM, Eric Mahurin wrote:
> 
> > &nbsp;&nbsp;Maybe just have a little ruby  
> > script that generates a stream of repeatable random (but valid) JSON.
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![ThoML](https://avatars.discourse-cdn.com/v4/letter/t/d78d45/32.png) [@ThoML](https://rubytalk.org/u/ThoML)\
**Post date:** [1 February 2008 18:14 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44197/20 "2008-02-01T18:14:57Z")

</div>

> A bit aside, but it seems a good place to plug the thought: JSON is so  
> close to valid Ruby syntax. It would be great if Ruby could support  
> the syntax 100%.

I hoped ruby19 would already support this use of colons as in {"key":  
"value"} but unfortunately not.

[Next page](https://rubytalk.org/t/quiz-parsing-json-155/44197.md?page=2)
