------art_570_22697686.1202048084623
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hey guys

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

http://pastie.caboo.se/146906

require 'treetop'


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


Treetop.load "json"

parser  sonParser.new


pp parser.parse(STDIN.read).value  if $0 __FILE__



BEGIN {

GRAMMAR  q{

grammar Json
  rule json
    space json_value space { def value; json_value.value; end }
  end


  rule json_value
    string / numeric / keyword / object / array
  end



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

      end
    }
  end

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

      def value
        if text_value[0..0] '\\\\'
          case c  ext_value[1..1]
          when /[nbfrt]/

            {'n' "\n", 'b' "\b", 'f' "\f", 'r' "\r", 't' "\t"}[c]
          when 'u'

            [text_value[2,4].to_i(16)].pack("L").gsub(/\0*$/,'')
          else
            c
          end

        else
          text_value
        end
      end
    }
  end


  rule hex
    [0-9a-fA-F]
  end



  rule numeric
    exp / float / integer
  end

  rule exp
    (float / integer) ('e' / 'E') ('+' / '-')? integer  { def value;
text_value.to_f; end }

  end

  rule float
    integer '.' [0-9]+  { def value; text_value.to_f; end }
  end


  rule integer
    '-'? ('0' / [1-9] [0-9]*)  { def value; text_value.to_i; end }
  end



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

        { 'true' true, 'false' false, 'null' nil }[text_value]
      end
    }
  end



  rule object
    '{' space pairs:pair* space '}' {
      def value

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


  rule pair
    space string space ':' space json_value space (',' &pair / !pair) {
      def value
        { string.value json_value.value }

      end
    }
  end


  rule array

    '[' space array_values:array_value* space ']' {
      def value
        array_values.elements.map {|e| e.value }

      end
    }
  end

  rule array_value
    space json_value space (',' &array_value / !array_value) {

      def value
        json_value.value
      end
    }
  end



  rule space
    [ \t\r\n]*
  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/
>
> 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:
>
>        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:
>
>        require "test/unit"
>
>        class TestJSONParser < Test::Unit::TestCase
>          def setup
>            @parser  SONParser.new
>          end
>
>          def test_keyword_parsing
>            assert_equal(true,  @parser.parse("true"))
>            assert_equal(false, @parser.parse("false"))
>            assert_equal(nil,   @parser.parse("null"))
>          end
>
>          def test_number_parsing
>            assert_equal(42,      @parser.parse("42"))
>            assert_equal(-13,     @parser.parse("-13"))
>            assert_equal(3.1415,  @parser.parse("3.1415"))
>            assert_equal(-0.01,   @parser.parse("-0.01"))
>
>            assert_equal(0.2e1,   @parser.parse("0.2e1"))
>            assert_equal(0.2e+1,  @parser.parse("0.2e+1"))
>            assert_equal(0.2e-1,  @parser.parse("0.2e-1"))
>            assert_equal(0.2E1,   @parser.parse("0.2e1"))
>          end
>
>          def test_string_parsing
>            assert_equal(String.new,          @parser.parse(%Q{""}))
>            assert_equal("JSON",              @parser.parse(%Q{"JSON"}))
>
>            assert_equal( %Q{nested "quotes"},
>                          @parser.parse('"nested \"quotes\""') )
>            assert_equal("\n",                @parser.parse(%Q{"\\n"}))
>            assert_equal( "a",
>                          @parser.parse(%Q{"\\u#{"%04X" % ?a}"}) )
>          end
>
>          def test_array_parsing
>            assert_equal(Array.new, @parser.parse(%Q{[]}))
>            assert_equal( ["JSON", 3.1415, true],
>                          @parser.parse(%Q{["JSON", 3.1415, true]}) )
>            assert_equal([1, [2, [3]]], @parser.parse(%Q{[1, [2, [3]]]}))
>          end
>
>          def test_object_parsing
>            assert_equal(Hash.new, @parser.parse(%Q{{}}))
>            assert_equal( {"JSON" 3.1415, "data" true},
>                          @parser.parse(%Q{{"JSON": 3.1415, "data": true}})
> )
>            assert_equal( { "Array"  [1, 2, 3],
>                            "Object" {"nested" "objects"} },
>                          @parser.parse(<<-END_OBJECT) )
>            {"Array": [1, 2, 3], "Object": {"nested": "objects"}}
>            END_OBJECT
>          end
>
>          def test_parse_errors
>            assert_raise(RuntimeError) { @parser.parse("{") }
>            assert_raise(RuntimeError) { @parser.parse(%q{{"key": true
> false}}) }
>
>            assert_raise(RuntimeError) { @parser.parse("[") }
>            assert_raise(RuntimeError) { @parser.parse("[1,,2]") }
>
>            assert_raise(RuntimeError) { @parser.parse(%Q{"}) }
>            assert_raise(RuntimeError) { @parser.parse(%Q{"\\i"}) }
>
>            assert_raise(RuntimeError) { @parser.parse("$1,000") }
>            assert_raise(RuntimeError) { @parser.parse("1_000") }
>            assert_raise(RuntimeError) { @parser.parse("1K") }
>
>            assert_raise(RuntimeError) { @parser.parse("unknown") }
>          end
>        end
>
>

------art_570_22697686.1202048084623--