Skip to content

Instantly share code, notes, and snippets.

@zimbatm
Created February 28, 2013 15:10
Show Gist options
  • Save zimbatm/1e9a4021c885e096fcbb to your computer and use it in GitHub Desktop.
Save zimbatm/1e9a4021c885e096fcbb to your computer and use it in GitHub Desktop.
require 'parslet'
# Mostly copied over from the JSON example:
# https://github.com/kschiess/parslet/blob/master/example/json.rb
#
# TODO:
# ISO8601 dates
module Scrolls
class Parser < Parslet::Parser
rule(:spaces) { match(' ').repeat(1) }
rule(:spaces?) { spaces.maybe }
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(:time) {
(
(digit >> digit >> digit >> digit).as(:year) >> str('-') >>
(digit >> digit).as(:month) >> str('-') >>
(digit >> digit).as(:day) >>
str('T') >>
(digit >> digit).as(:hour) >> str(':') >>
(digit >> digit).as(:minute) >> str(':') >>
(digit >> digit).as(:second) >> str('Z')
).as(:time)
}
rule(:singlequoted_string) {
str("'") >> (
str('\\') >> any | str("'").absent? >> any
).repeat.as(:string) >> str("'")
}
rule(:doublequoted_string) {
str('"') >> (
str('\\') >> any | str('"').absent? >> any
).repeat.as(:string) >> str('"')
}
rule(:simple_string) {
match['a-zA-Z_\-:'].repeat.as(:string)
}
rule(:string) {
singlequoted_string | doublequoted_string | simple_string
}
rule(:array) {
str('[') >> spaces? >>
(value >> (spaces >> value).repeat).maybe.as(:array) >>
spaces? >> str(']')
}
rule(:object) {
str('{') >> spaces? >>
(entry >> (spaces >> entry).repeat).maybe.as(:object) >>
spaces? >> str('}')
}
rule(:key) {
match['a-zA-Z0-9_'].repeat
}
rule(:value) {
str('true').as(:true) | str('false').as(:false) |
str('nil').as(:nil) |
object | array |
number | time |
string
}
rule(:entry) {
(
key.as(:key) >>
str('=') >>
value.as(:val)
).as(:entry)
}
rule(:top) { spaces? >> (entry >> (spaces >> entry).repeat).maybe.as(:object) >> spaces? }
#rule(:top) { (digit >> digit).as(:digit) }
#rule(:top) { time }
root(:top)
end
class Transformer < Parslet::Transform
class Entry < Struct.new(:key, :val); end
rule(array: subtree(:ar)) {
case ar
when nil
[]
when Array
ar
else
[ar]
end
}
rule(object: subtree(:ob)) {
case ob
when nil
[]
when Array
ob
else
[ob]
end.inject({}) { |h, e|
h[e[:entry][:key].to_s] = e[:entry][:val]; h
}
}
# rule(entry: { key: simple(:ke), val: simple(:va) }) {
# Entry.new(ke.to_s, va)
# }
rule(time: { year: simple(:ye), month: simple(:mo), day: simple(:da), hour: simple(:ho), minute: simple(:min), second: simple(:sec)}) {
Time.new(ye.to_i, mo.to_i, da.to_i, ho.to_i, min.to_i, sec.to_i, "+00:00")
}
rule(string: simple(:st)) {
st.to_s
}
rule(number: simple(:nb)) {
nb.match(/[eE\.]/) ? Float(nb) : Integer(nb)
}
rule(nil: simple(:ni)) { 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 => failure
puts failure.cause.ascii_tree
end
end
if __FILE__ == $0
p Scrolls.parse('foo=3 bar=true funky="true" zap=tire single=\'quoted\' baz=[1 2 5] glo={foo=nil} sfsfds={} sfdsfgfdsg=[] when=2011-10-10T10:22:50Z')
#p Scrolls.parse('foo=3 bar=true funky="true" zap=tire single=\'quoted\' baz=[1 2 5] glo={foo=nil} sfsfds={} sfdsfgfdsg=[]')
#p Scrolls.parse('2011-10-10T10:22:50Z')
#p Scrolls.parse('32')
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment