[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Fwd: Please Forward: Ruby Quiz Submission

James Gray

2/4/2008 6:58:00 PM

Begin forwarded message:

> From: Alexander Stedile <as_news@gmx.at>
> Date: February 3, 2008 6:30:39 PM CST
> To: submission@rubyquiz.com
> Subject: Please Forward: Ruby Quiz Submission
>
> Hi!
>
> Here is my try for my second ruby quiz. I proved that it is possible
> to write a JSON parser in less than 100 lines. My (handmade)
> solution has 94. This would leave space for five more comment
> lines. ;-)
> Looking forward to seeing your parsers and of course the nice quiz
> summary which I really appreciate. Thanks for that.
>
> By the way, I added a missing test. I had to do an extra check for
> that situation to my program. The reason is that the type is decided
> from the first character. And if there is a closing quotation mark
> at the end, it could be mistakenly interpreted as valid string 'a"
> "b'.
> assert_raise(RuntimeError) { @parser.parse(%Q{"a" "b"}) }
> I also do not check for control characters in strings, but anyway.
>
> As you see, I'm beginning to like eval. :) And my solution heavily
> relies on regexp matching. The most important/tricky part is the
> String#split_stateful method at the top of the code.
>
> Have fun,
> Alex
> ______
> class String
> # Splits into sub-strings separated by ',' characters. Does not split
> # contents within {}, [], or "". \" does not end a string, \\" does.
> # Checks if closing characters match previous opening ones.
> def split_stateful
> memb = [] # list of members identified
> delims = [] # stack of delimiters
> split('').each { |c|
> memb << "" if memb.empty?
> case delims.last
> when '"' # quote mode
> c == '\\' and delims.push c
> c == '"' and delims.pop
> when '\\' # escape mode
> delims.pop
> else
> case c
> when '{', '[', '"' then delims.push c
> when ',' then ( memb << ""; c="" ) if delims.empty? # next
> element
> when '}' then delims.pop == '{' or raise RuntimeError, "Non-
> matching }."
> when ']' then delims.pop == '[' or raise RuntimeError, "Non-
> matching ]."
> end
> end
> memb[-1] += c
> }
> delims.empty? or raise RuntimeError, "No closing delimiter for
> #{delims.join(', ')}."
> memb
> end
> end
>
> class JSONParser
>
> NUM_FORMAT = /^(-)?(0|[1-9][0-9]*)(\.[0-9]+)?(E[+-]?([0-9]+))?$/i
>
> # parse_value
> def parse(code)
> code.strip!
> case code[0,1]
> when '"' then parse_string(code)
> when /[-0-9]/ then parse_number(code)
> when '{' then parse_object(code)
> when '[' then parse_array(code)
> else parse_keyword(code)
> end
> end
>
> def parse_string(code)
> code =~ /^"(.*)"$/ or raise RuntimeError, "String has no closing
> quotation mark."
> $_ = $1
> $_ =~ /([^\\]|(\\\\)+)"/ and raise RuntimeError, "Non-escaped \"
> not allowed in string #{$_}."
> gsub(/\\(.)/) { |m|
> case $1
> when 'b', 'f', 'n', 'r', 't'
> eval('"\\%s"' % $1)
> when 'u'
> m # no change, handled later
> when '"', '/', '\\'
> $1 # strip \ character
> else
> raise RuntimeError, "No such escape sequence \\#{$1}."
> end
> }
> gsub(/\\u([A-F0-9]{4})/i) { "%c" % $1.hex }
> end
>
> def parse_number(code)
> code =~ NUM_FORMAT or raise RuntimeError, "Invalid number #{code}."
> eval code
> end
>
> def parse_array(code)
> code =~ /^\[(.*)\]$/ or raise RuntimeError, "No closing bracket
> for array #{code}."
> $1.split_stateful.collect { |m| parse(m) }
> end
>
> def parse_object(code)
> code =~ /^\{(.*)\}$/ or raise RuntimeError, "No closing bracket
> for object #{code}."
> object = {}
> $1.split_stateful.each do |m|
> key, value = m.split(":", 2)
> object[parse_string(key.strip)] = parse(value)
> end
> object
> end
>
> def parse_keyword(code)
> case code
> when 'true', 'false' then eval(code)
> when 'null' then nil
> else
> raise RuntimeError, "Syntax error: #{code}."
> end
> end
> end