Module skims.parse_json

Expand source code
# Standard library
import ast
from typing import (
    Optional,
)

# Third party libraries
from aioextensions import (
    in_process,
)
from frozendict import (
    frozendict
)
import lark


# Constants
GRAMMAR = r"""
    ?start: value

    ?value: object
            | array
            | string
            | SIGNED_NUMBER      -> number
            | single

    array  : "[" [value ("," value)*] "]"
    object : "{" [pair ("," pair)*] "}"
    pair   : string ":" value

    false : "false"
    true : "true"
    null : "null"
    single : false | true | null
    string : ESCAPED_STRING

    %import common.ESCAPED_STRING
    %import common.SIGNED_NUMBER
    %import common.WS

    %ignore WS
"""


def blocking_loads(
    stream: str,
    *,
    default: Optional[frozendict] = None,
) -> frozendict:
    json_parser = lark.Lark(
        grammar=GRAMMAR,
        parser='lalr',
        lexer='standard',
        propagate_positions=True,
        maybe_placeholders=False,
        transformer=JSONBuilder(),
    )

    try:
        return json_parser.parse(stream)
    except lark.exceptions.LarkError:
        if default is None:
            raise

        return default


async def loads(
    stream: str,
    *,
    default: Optional[frozendict] = None,
) -> frozendict:
    return await in_process(blocking_loads, stream, default=default)


class JSONBuilder(lark.Transformer):

    pair = tuple
    object = frozendict
    single_map = {
        "false": False,
        "null": None,
        "true": True,
    }

    @staticmethod
    @lark.v_args(tree=True)
    def single(tree: lark.Tree) -> frozendict:
        children: lark.Tree = tree.children[0]
        return frozendict({
            'column': children.column,
            'item': JSONBuilder.single_map[children.data],
            'line': children.line,
        })

    @staticmethod
    @lark.v_args(inline=True)
    def string(token: lark.Token) -> frozendict:
        return frozendict({
            'column': token.column,
            'item': ast.literal_eval(token),
            'line': token.line
        })

    @staticmethod
    @lark.v_args(inline=True)
    def number(token: lark.Token) -> frozendict:
        return frozendict({
            'column': token.column,
            'item': ast.literal_eval(token),
            'line': token.line,
        })

    @staticmethod
    @lark.v_args(tree=True)
    def array(tree: lark.Tree) -> frozendict:
        return frozendict({
            'column': 0,
            'item': tuple(tree.children),
            'line': 0,
        })

Functions

def blocking_loads(stream: str, *, default: Union[frozendict.frozendict, NoneType] = None) ‑> frozendict.frozendict
Expand source code
def blocking_loads(
    stream: str,
    *,
    default: Optional[frozendict] = None,
) -> frozendict:
    json_parser = lark.Lark(
        grammar=GRAMMAR,
        parser='lalr',
        lexer='standard',
        propagate_positions=True,
        maybe_placeholders=False,
        transformer=JSONBuilder(),
    )

    try:
        return json_parser.parse(stream)
    except lark.exceptions.LarkError:
        if default is None:
            raise

        return default
async def loads(stream: str, *, default: Union[frozendict.frozendict, NoneType] = None) ‑> frozendict.frozendict
Expand source code
async def loads(
    stream: str,
    *,
    default: Optional[frozendict] = None,
) -> frozendict:
    return await in_process(blocking_loads, stream, default=default)

Classes

class JSONBuilder (visit_tokens=False)

Visits the tree recursively, starting with the leaves and finally the root (bottom-up)

Calls its methods (provided by user via inheritance) according to tree.data The returned value replaces the old one in the structure.

Can be used to implement map or reduce.

Expand source code
class JSONBuilder(lark.Transformer):

    pair = tuple
    object = frozendict
    single_map = {
        "false": False,
        "null": None,
        "true": True,
    }

    @staticmethod
    @lark.v_args(tree=True)
    def single(tree: lark.Tree) -> frozendict:
        children: lark.Tree = tree.children[0]
        return frozendict({
            'column': children.column,
            'item': JSONBuilder.single_map[children.data],
            'line': children.line,
        })

    @staticmethod
    @lark.v_args(inline=True)
    def string(token: lark.Token) -> frozendict:
        return frozendict({
            'column': token.column,
            'item': ast.literal_eval(token),
            'line': token.line
        })

    @staticmethod
    @lark.v_args(inline=True)
    def number(token: lark.Token) -> frozendict:
        return frozendict({
            'column': token.column,
            'item': ast.literal_eval(token),
            'line': token.line,
        })

    @staticmethod
    @lark.v_args(tree=True)
    def array(tree: lark.Tree) -> frozendict:
        return frozendict({
            'column': 0,
            'item': tuple(tree.children),
            'line': 0,
        })

Ancestors

  • lark.visitors.Transformer

Class variables

var object

An immutable wrapper around dictionaries that implements the complete :py:class:collections.Mapping interface. It can be used as a drop-in replacement for dictionaries where immutability is desired.

var pair

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

var single_map

Static methods

def array(tree: lark.tree.Tree) ‑> frozendict.frozendict
Expand source code
@staticmethod
@lark.v_args(tree=True)
def array(tree: lark.Tree) -> frozendict:
    return frozendict({
        'column': 0,
        'item': tuple(tree.children),
        'line': 0,
    })
def number(token: lark.lexer.Token) ‑> frozendict.frozendict
Expand source code
@staticmethod
@lark.v_args(inline=True)
def number(token: lark.Token) -> frozendict:
    return frozendict({
        'column': token.column,
        'item': ast.literal_eval(token),
        'line': token.line,
    })
def single(tree: lark.tree.Tree) ‑> frozendict.frozendict
Expand source code
@staticmethod
@lark.v_args(tree=True)
def single(tree: lark.Tree) -> frozendict:
    children: lark.Tree = tree.children[0]
    return frozendict({
        'column': children.column,
        'item': JSONBuilder.single_map[children.data],
        'line': children.line,
    })
def string(token: lark.lexer.Token) ‑> frozendict.frozendict
Expand source code
@staticmethod
@lark.v_args(inline=True)
def string(token: lark.Token) -> frozendict:
    return frozendict({
        'column': token.column,
        'item': ast.literal_eval(token),
        'line': token.line
    })