Skip to content

Commit

Permalink
Add JSON decoder example (see erikrose#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Keleshev committed Apr 7, 2013
1 parent bf7376c commit f951451
Showing 1 changed file with 112 additions and 0 deletions.
112 changes: 112 additions & 0 deletions example_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
r"""# JSON grammar
value = space (string / number / object / array / true_false_null) space
object = "{" members "}"
members = (pair ("," pair)*)?
pair = string ":" value
array = "[" elements "]"
elements = (value ("," value)*)?
true_false_null = "true" / "false" / "null"
string = space "\"" char* "\"" space
char = ~"[^\"]" # TODO implement the real thing
number = (int frac exp) / (int exp) / (int frac) / int
int = "-"? ((digit1to9 digits) / digit)
frac = "." digits
exp = e digits
digits = digit+
e = "e+" / "e-" / "e" / "E+" / "E-" / "E"
digit1to9 = ~"[1-9]"
digit = ~"[0-9]"
space = ~"\s*"
"""
from parsimonious.grammar import Grammar


class JSONDecoder(object):

def loads(self, node):
if isinstance(node, str):
node = Grammar(__doc__).parse(node)
method = getattr(self, node.expr_name, self.default)
return method(node, [self.loads(n) for n in node])

def default(self, node, children):
return children

def value(self, node, children):
return children[1][0]

def object(self, node, children):
return children[1]

def members(self, node, children):
if not children:
return {}
head, tail = children[0][:1], children[0][1:][0]
return dict(head + [el[1] for el in tail])

def pair(self, node, children):
return children[0], children[2]

def array(self, node, children):
return children[1]

def elements(self, node, children):
if not children:
return []
head, tail = children[0][:1], children[0][1:][0]
return head + [el[1] for el in tail]

def true_false_null(self, node, children):
return {'true': True, 'false': False, 'null': None}[node.text.strip()]

def number(self, node, children):
return float(node.text.strip())

def string(self, node, children):
return node.text.strip().strip('"') # TODO implement the real thing


json = JSONDecoder()


def test_numbers():
assert json.loads('5') == 5
assert json.loads('55') == 55
assert json.loads('-5') == -5
assert json.loads('5.5') == 5.5
assert json.loads('5e-2') == 5e-2
assert json.loads('5.3e-2') == 5.3e-2


def test_strings():
assert json.loads('""') == ''
assert json.loads('"hai"') == 'hai'


def test_true_false_null():
assert json.loads('true') == True
assert json.loads('false') == False
assert json.loads('null') == None


def test_arrays():
assert json.loads('[]') == []
assert json.loads('[1]') == [1]
assert json.loads('["a",1,2,3]') == ['a', 1, 2, 3]
assert json.loads('["a",[1,2],3]') == ['a', [1, 2], 3]


def test_objects():
assert json.loads('{}') == {}
assert json.loads('{"a":1}') == {'a': 1}
assert json.loads('{"a":1,"b":2}') == {'a': 1, 'b': 2}


def test_whitespace():
source = ' { "a" : 1 , "b" : [ 2 , 3 ] } '
assert json.loads(source) == {'a': 1, 'b': [2, 3]}

0 comments on commit f951451

Please sign in to comment.