Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Forked from jmettraux/my_json.rb
Last active December 15, 2015 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterhellberg/5196149 to your computer and use it in GitHub Desktop.
Save peterhellberg/5196149 to your computer and use it in GitHub Desktop.
Initial experiments with Parslet
#
# MIT License - (c) 2011 John Mettraux
#
# Slightly modified - 2013 Peter Hellberg
#
require '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 Transform < Parslet::Transform
class Entry < Struct.new(:key, :val); end
rule(:array => subtree(:ar)) { Array(ar) }
rule(:object => subtree(:ob)) {
if ob.is_a?(Hash)
ob[:entry].values.inject({}) { |h, e| h[e[0]] = e[1]; h }
else
(ob.is_a?(Array) ? ob : [ ob ]).inject({}) { |h, e| h[e[0]] = e[1]; h }
end
}
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
transform = Transform.new
tree = parser.parse(s)
transform.apply(tree)
rescue Parslet::ParseFailed => e
puts e, parser.root.error_tree if debug
end
end
parser = MyJSON::Parser.new
transform = MyJSON::Transform.new
tree = parser.parse('{ "foo":[1,2,"3"] }')
puts "#{tree} \n#=> #{transform.apply(tree)}"
s = %{
[ 1, 2, 3, null,
"asdfasdf asdfds", { "a": -1.2 }, { "b": true, "c": false },
0.1e24, true, false, [ 1 ] ]
}
puts "---------"
puts MyJSON.parse(s).inspect
@peterhellberg
Copy link
Author

Still quite buggy, but I’ve never used Parslet before :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment