Skip to content

Instantly share code, notes, and snippets.

@jmettraux
Created May 11, 2011 06:51
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save jmettraux/966020 to your computer and use it in GitHub Desktop.
#
# MIT License - (c) 2011 John Mettraux
#
require 'rubygems'
require 'parslet' # gem install parslet
module MyJson
class Parser < Parslet::Parser
rule(:spaces) { match('\s').repeat(1) }
rule(:spaces?) { spaces.maybe }
rule(:comma) { spaces? >> str(',') >> spaces? }
rule(:digit) { match('[0-9]') }
rule(:number) {
(
str('-').maybe >> (
str('0') | (match('[1-9]') >> digit.repeat)
) >> (
str('.') >> digit.repeat(1)
).maybe >> (
match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)
).maybe
).as(:number)
}
rule(:string) {
str('"') >> (
str('\\') >> any | str('"').absent? >> any
).repeat.as(:string) >> str('"')
}
rule(:array) {
str('[') >> spaces? >>
(value >> (comma >> value).repeat).maybe.as(:array) >>
spaces? >> str(']')
}
rule(:object) {
str('{') >> spaces? >>
(entry >> (comma >> entry).repeat).maybe.as(:object) >>
spaces? >> str('}')
}
rule(:value) {
string | number |
object | array |
str('true').as(:true) | str('false').as(:false) |
str('null').as(:null)
}
rule(:entry) {
(
string.as(:key) >> spaces? >>
str(':') >> spaces? >>
value.as(:val)
).as(:entry)
}
rule(:attribute) { (entry | value).as(:attribute) }
rule(:top) { spaces? >> value >> spaces? }
root(:top)
end
class Transformer < Parslet::Transform
class Entry < Struct.new(:key, :val); end
rule(:array => subtree(:ar)) {
ar.is_a?(Array) ? ar : [ ar ]
}
rule(:object => subtree(:ob)) {
(ob.is_a?(Array) ? ob : [ ob ]).inject({}) { |h, e| h[e.key] = e.val; h }
}
rule(:entry => { :key => simple(:ke), :val => simple(:va) }) {
Entry.new(ke, va)
}
rule(:string => simple(:st)) {
st.to_s
}
rule(:number => simple(:nb)) {
nb.match(/[eE\.]/) ? Float(nb) : Integer(nb)
}
rule(:null => simple(:nu)) { nil }
rule(:true => simple(:tr)) { true }
rule(:false => simple(:fa)) { false }
end
def self.parse(s)
parser = Parser.new
transformer = Transformer.new
tree = parser.parse(s)
puts; p tree; puts
out = transformer.apply(tree)
out
rescue Parslet::ParseFailed => e
puts e, parser.root.error_tree
end
end
s = %{
[ 1, 2, 3, null,
"asdfasdf asdfds", { "a": -1.2 }, { "b": true, "c": false },
0.1e24, true, false, [ 1 ] ]
}
out = MyJson.parse(s)
p out; puts
out == [
1, 2, 3, nil,
"asdfasdf asdfds", { "a" => -1.2 }, { "b" => true, "c" => false },
0.1e24, true, false, [ 1 ]
] || raise("MyJson is a failure")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment