Created
December 26, 2017 08:51
-
-
Save appetizermonster/2726e2037b099a8832cde206ccd223cf to your computer and use it in GitHub Desktop.
SimpleJsonParser in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'strscan' | |
class SimpleJsonParser < StringScanner | |
WHITESPACE = /\s+/ | |
SINGLE_LINE_COMMENT = /\/\/[^\n\r]*\s*/ | |
NULL = /null/ | |
NUMBER = /\-?\d+(\.\d+)?([eE][\+\-]?\d+)?/ | |
NUMBER_START = /\-?\d/ | |
BOOLEAN = /(true|false)/ | |
BOOLEAN_START = /[tf]/ | |
STRING = /"(\\.|[^"\\])*"/ | |
STRING_START = /\"/ | |
ARRAY_START = /\[/ | |
ARRAY_END = /\]/ | |
ARRAY_VALUE_DELIMITER = /\,/ | |
OBJECT_START = /\{/ | |
OBJECT_END = /\}/ | |
OBJECT_KEY_DELIMITER = /\:/ | |
OBJECT_VALUE_DELIMITER = /\,/ | |
def parse | |
value = parse_value | |
_skip_ignore | |
raise 'Parse Error' unless eos? | |
value | |
end | |
private | |
def parse_value | |
_skip_ignore | |
value = | |
case | |
when match?(NULL) then parse_null | |
when match?(NUMBER_START) then parse_number | |
when match?(BOOLEAN_START) then parse_boolean | |
when match?(STRING_START) then parse_string | |
when match?(ARRAY_START) then parse_array | |
when match?(OBJECT_START) then parse_object | |
else raise 'Value Error' | |
end | |
end | |
def parse_null | |
raise 'Null Error' unless scan(NULL) | |
nil | |
end | |
def parse_number | |
num_str = scan(NUMBER) | |
raise 'Number Error' if num_str == nil | |
num_str.to_f | |
end | |
def parse_boolean | |
bool_str = scan(BOOLEAN) | |
case | |
when bool_str == 'true' then true | |
when bool_str == 'false' then false | |
else raise 'Boolean Error' | |
end | |
end | |
def parse_string | |
str = scan(STRING) | |
raise 'String Error' if str == nil | |
str[1..-2] | |
end | |
def parse_array | |
skip(ARRAY_START) | |
_skip_ignore | |
return [] if scan(ARRAY_END) | |
arr = [] | |
while true | |
_skip_ignore | |
value = parse_value | |
arr.push(value) | |
_skip_ignore | |
case | |
when scan(ARRAY_END) then break | |
when scan(ARRAY_VALUE_DELIMITER) then next | |
else raise 'Array Error' | |
end | |
end | |
arr | |
end | |
def parse_object | |
skip(OBJECT_START) | |
_skip_ignore | |
return {} if scan(OBJECT_END) | |
obj = {} | |
while true | |
_skip_ignore | |
key = parse_string | |
_skip_ignore | |
raise 'Object Error' unless scan(OBJECT_KEY_DELIMITER) | |
_skip_ignore | |
value = parse_value | |
obj[key] = value | |
_skip_ignore | |
case | |
when scan(OBJECT_END) then break | |
when scan(OBJECT_VALUE_DELIMITER) then next | |
else raise 'Object Error' | |
end | |
end | |
obj | |
end | |
def _skip_ignore | |
skip(WHITESPACE) | |
skip(SINGLE_LINE_COMMENT) | |
end | |
end | |
json = <<~JSON | |
{ | |
"test": null, | |
"key": "poor parser", | |
"key2": 123e3, // Hello, World | |
"key3": [1, 210.5, 3, { | |
"hello": "world", | |
"world": null | |
}] | |
} | |
JSON | |
parser = SimpleJsonParser.new(json) | |
puts(parser.parse) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment