Skip to content

Instantly share code, notes, and snippets.

@iliabylich
Created June 15, 2017 23:50
Show Gist options
  • Save iliabylich/a2c484609577bac419565b5e3c0deb1a to your computer and use it in GitHub Desktop.
Save iliabylich/a2c484609577bac419565b5e3c0deb1a to your computer and use it in GitHub Desktop.
json parser
# racc json_parser.rb -o out.rb -v -g && ruby -rpry -rstrscan out.rb
class JsonParser
token tDOT tQUOTE
tLCURLY tRCURLY
tLBRACK tRBRACK
tCOMMA tCOLON
tSTRING tNUMBER
kTRUE kFALSE kNULL
rule
object : tLCURLY tRCURLY
{
result = {}
}
| tLCURLY kv_pairs tRCURLY
{
result = val[1]
}
kv_pairs : kv_pair
| kv_pair tCOMMA kv_pairs
{
result = val[0].merge(val[2])
}
kv_pair : key tCOLON value
{
result = { val[0] => val[2] }
}
key : string
value : string
| integer
| float
| object
| array
| true
| false
| null
string : tSTRING
integer : tNUMBER
{
result = val[0].to_i
}
float : tNUMBER tDOT tNUMBER
{
result = val.join.to_f
}
array : tLBRACK tRBRACK
{
result = []
}
| tLBRACK array_items tRBRACK
{
result = val[1]
}
array_items : array_item
{
result = val
}
| array_item tCOMMA array_items
{
result = [val[0]] + val[2]
}
array_item : value
true : kTRUE
{
result = true
}
false : kFALSE
{
result = false
}
null : kNULL
{
result = nil
}
end
---- inner
def initialize(raw_json)
@raw_json = raw_json
@yydebug = true
end
def parse
@tokens = JsonLexer.new(@raw_json).tokens
do_parse
end
def next_token
@tokens.shift
end
class JsonLexer
TOKENS = {
/\./ => :tDOT,
/\{/ => :tLCURLY,
/\}/ => :tRCURLY,
/\[/ => :tLBRACK,
/\]/ => :tRBRACK,
/,/ => :tCOMMA,
/:/ => :tCOLON,
/true/ => :kTRUE,
/false/ => :kFALSE,
/null/ => :kNULL,
/\d+/ => :tNUMBER
}.freeze
attr_reader :source
def initialize(source)
@source = StringScanner.new(source)
@result = []
end
def tokens
while (@source.skip(/\s+/); !@source.empty?) do
# string is a special case
if str = @source.scan(/"(.*?)"/)
@result << [:tSTRING, str[1..-2]]
else
found = false
TOKENS.each do |regexp, token|
if value = @source.scan(regexp)
@result << [token, value]
found = true
end
end
unless found
raise "Lex error : " + @source.inspect
end
end
end
@result
end
end
---- footer
p JsonParser.new(DATA.read).parse
__END__
{
"number": 123,
"float": 2.3,
"string": "str",
"array": [4, 5.6, "str", [7], {"8": "9"}],
"true": true,
"false": false,
"null": null
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment