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 })