view pcombinators/json_test.py @ 31:084d753d5b6a draft

Update parser examples to new model.
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 21 May 2019 15:46:41 +0200
parents 25aab2c2a695
children 02cf2ee329a3
line wrap: on
line source

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Example on how to write a JSON parser.

@author: lbo
"""

from combinators import *
from primitives import *

JString = Last(Skip(String('"')) + NoneInSet('"') + Skip(String('"')))

wrapl = lambda l: [l]

class Value(Parser):
    """Bare-bones, but fully functioning, JSON parser. Doesn't like escaped quotes.

    Example:
        >>> Value().parse(ParseState(my_json_string))
        ({'id': 1.0,
          'name': 'Foo',
          'price': 123.0,
          'tags': ['Bar', 'Eek'],
          'stock': {'warehouse': 300.0, 'retail': 20.0}},
         ParseState({"id":1,"name":"Foo","price":123,"tags":["Bar","Eek"],"stock":{"warehouse":300, "retail":20}}<>))
    """
    def parse(self, st):
        return Last(Skip(Whitespace()) + (Dict | List | JString | Float()) + Skip(Whitespace())).parse(st)

# We moved out all the piece parsers out of functions to reduce allocation overhead.
# It improves performance by roughly 2x.

# LISTS

# An entry is any value.
entry = Value()
# A mid entry is a value followed by a comma.
midentry = entry + Skip(String(','))
# A list is a [, followed by mid entries, followed by a final entry, and a
# closing ]. The list is wrapped in a list to prevent merging in other parsers.
List = (Skip(String('[')) +
        Repeat(midentry, -1) +
        entry +
        Skip(String(']'))) >> wrapl # Wrap list inside another list to protect it from flattening.

# DICTS

# A separator is whitespace, a colon, and more whitespace (Whitespace() also accepts empty string)
separator = Skip((Whitespace() + String(":") + Whitespace()))
# Entry is a String followed by a separator and a value. Wrap the value in a list to prevent merging.
# The two-element list is converted to a tuple.
entry = (JString + separator + (Value() >> wrapl)) >> (lambda l: tuple(l))
# A mid entry is followed by a comma.
midentry = Last(entry + Skip(String(',') + Skip(Whitespace())))
# A dict is a {, followed by entries, followed by a final entry, followed by a closing }
dct = Flatten(
        Skip(String("{")) + ((Repeat(midentry, -1) + entry)) + Skip(String("}")))
# Convert the list of tuples into a dict.
Dict = dct >> dict