Skip to content

Instantly share code, notes, and snippets.

@acarapetis
Last active July 24, 2019 13:47
Show Gist options
  • Save acarapetis/c13383ba44c83ae34e7cb10636728e40 to your computer and use it in GitHub Desktop.
Save acarapetis/c13383ba44c83ae34e7cb10636728e40 to your computer and use it in GitHub Desktop.
grammar NotJSON
rule document
(space value space) {
capture(:value).value
}
end
rule value
number | str | hash
end
rule hash
('{' pairs '}') {
capture(:pairs).value
} | ('{' space '}') {
{}
}
end
rule pairs
(space pair space
more:(more_pairs | no_more_pairs)
space) {
capture(:pair).value.merge(capture(:more).value)
}
end
rule pair
(key space ':' space value) {
{capture(:key).value => capture(:value).value}
}
end
rule more_pairs
(',' pairs) {
capture(:pairs).value
}
end
rule no_more_pairs
(',' | '') {
{}
}
end
rule float
(/[0-9]+\.[0-9]+/) {
to_str.to_f
}
end
rule int
(/[0-9]+/) {
to_str.to_i
}
end
rule number
float | int
end
rule dqstr
(/"([^"\\]|\\.)*"/) {
eval to_str
# this is lazy!
# but I can't be bothered writing out the full grammar for strings with escape sequences.
# if you use this somewhere real, I'd first prove that this regex only matches things that
# Ruby itself interprets as string literals, or someone might be able to inject some code.
}
end
rule sqstr
(/'([^'\\]|\\.)*'/) {
eval to_str
}
end
rule str
dqstr | sqstr
end
rule key
([\w]+) {
to_str
}
end
rule space
[\s\n]*
end
end
require 'citrus'
Citrus.load('grammar')
doc = <<-'TEXT'
{
Number: 0,
Name: "some \"string\"",
Nested: {
It: 'works!',
}
}
TEXT
r = NotJSON.parse(doc)
p r.value # {"Number"=>0, "Name"=>"some \"string\"", "Nested"=>{"It"=>"works!"}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment