Skip to content

Instantly share code, notes, and snippets.

@appetizermonster
Created December 26, 2017 08:51
Show Gist options
  • Save appetizermonster/2726e2037b099a8832cde206ccd223cf to your computer and use it in GitHub Desktop.
Save appetizermonster/2726e2037b099a8832cde206ccd223cf to your computer and use it in GitHub Desktop.
SimpleJsonParser in Ruby
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