phml.core.formats.json_format

  1from json import dumps, loads
  2from typing import Optional
  3
  4from phml.core.nodes import AST, NODE, Comment, DocType, Element, Position, Root, Text
  5from phml.utilities import visit_children
  6
  7from .format import Format
  8
  9
 10def __construct_node_type(node_type: str):
 11    """Takes a node type and returns a base constructed instance of the node."""
 12    if node_type == "root":
 13        return Root()
 14
 15    if node_type == "element":
 16        return Element()
 17
 18    if node_type == "doctype":
 19        return DocType()
 20
 21    if node_type == "text":
 22        return Text()
 23
 24    if node_type == "comment":
 25        return Comment()
 26
 27    return None
 28
 29
 30def __construct_attributes(node, obj):
 31    for key in obj:
 32        if key not in ["children", "type", "position", "num_lines"] and hasattr(node, key):
 33            setattr(node, key, obj[key])
 34
 35
 36def __construct_children(node, obj):
 37    for child in obj["children"]:
 38        new_child = construct_tree(child)
 39        new_child.parent = node
 40        node.children.append(new_child)
 41
 42
 43def __construct_position(node, obj):
 44    start = obj["position"]["start"]
 45    end = obj["position"]["end"]
 46    node.position = Position(
 47        (start["line"], start["column"], start["offset"]),
 48        (end["line"], end["column"], end["offset"]),
 49        obj["position"]["indent"],
 50    )
 51
 52
 53def __construct_node(node, obj):
 54    __construct_attributes(node, obj)
 55
 56    if 'children' in obj and hasattr(node, "children"):
 57        __construct_children(node, obj)
 58
 59    if 'position' in obj and hasattr(node, 'position') and obj["position"] is not None:
 60        __construct_position(node, obj)
 61
 62    return node
 63
 64
 65def construct_tree(obj: dict):
 66    """Recursivly construct ast from json."""
 67    if 'type' not in obj:
 68        raise Exception(
 69            'Invalid json for phml. Every node must have a type. Nodes may only have the types; \
 70root, element, doctype, text, or comment'
 71        )
 72
 73    val = __construct_node_type(obj['type'])
 74    if val is None:
 75        raise Exception(f"Unkown node type <{obj['type']}>")
 76
 77    return __construct_node(val, obj)
 78
 79
 80class JSONFormat(Format):
 81    """Logic for parsing and compiling html files."""
 82
 83    extension: str = "json"
 84
 85    @classmethod
 86    def parse(cls, data: dict | str) -> str:
 87        if isinstance(data, str):
 88            data = loads(data)
 89
 90        if isinstance(data, dict):
 91            node = construct_tree(data)
 92            if not isinstance(node, Root):
 93                node = Root(children=[node])
 94            return AST(node)
 95
 96        raise Exception("Data passed to JSONFormat.parse must be either a str or a dict")
 97
 98    @classmethod
 99    def render(
100        cls,
101        ast: AST,
102        components: Optional[dict[str, dict[str, list | NODE]]] = None,
103        indent: int = 2,
104        **kwargs,
105    ) -> str:
106        indent = indent or 2
107
108        def compile_children(node: Root | Element) -> dict:
109            data = {"type": node.type}
110
111            if node.type == "root":
112                if node.parent is not None:
113                    raise Exception("Root nodes must only occur as the root of an ast/tree")
114
115            for attr in vars(node):
116                if attr not in ["parent", "children", "num_lines"]:
117                    value = getattr(node, attr)
118                    if isinstance(value, Position):
119                        data[attr] = value.as_dict()
120                    else:
121                        data[attr] = value
122
123            if hasattr(node, "children"):
124                data["children"] = []
125                for child in visit_children(node):
126                    data["children"].append(compile_children(child))
127
128            return data
129
130        data = compile_children(ast.tree)
131        return dumps(data, indent=indent)
def construct_tree(obj: dict):
66def construct_tree(obj: dict):
67    """Recursivly construct ast from json."""
68    if 'type' not in obj:
69        raise Exception(
70            'Invalid json for phml. Every node must have a type. Nodes may only have the types; \
71root, element, doctype, text, or comment'
72        )
73
74    val = __construct_node_type(obj['type'])
75    if val is None:
76        raise Exception(f"Unkown node type <{obj['type']}>")
77
78    return __construct_node(val, obj)

Recursivly construct ast from json.

class JSONFormat(phml.core.formats.format.Format):
 81class JSONFormat(Format):
 82    """Logic for parsing and compiling html files."""
 83
 84    extension: str = "json"
 85
 86    @classmethod
 87    def parse(cls, data: dict | str) -> str:
 88        if isinstance(data, str):
 89            data = loads(data)
 90
 91        if isinstance(data, dict):
 92            node = construct_tree(data)
 93            if not isinstance(node, Root):
 94                node = Root(children=[node])
 95            return AST(node)
 96
 97        raise Exception("Data passed to JSONFormat.parse must be either a str or a dict")
 98
 99    @classmethod
100    def render(
101        cls,
102        ast: AST,
103        components: Optional[dict[str, dict[str, list | NODE]]] = None,
104        indent: int = 2,
105        **kwargs,
106    ) -> str:
107        indent = indent or 2
108
109        def compile_children(node: Root | Element) -> dict:
110            data = {"type": node.type}
111
112            if node.type == "root":
113                if node.parent is not None:
114                    raise Exception("Root nodes must only occur as the root of an ast/tree")
115
116            for attr in vars(node):
117                if attr not in ["parent", "children", "num_lines"]:
118                    value = getattr(node, attr)
119                    if isinstance(value, Position):
120                        data[attr] = value.as_dict()
121                    else:
122                        data[attr] = value
123
124            if hasattr(node, "children"):
125                data["children"] = []
126                for child in visit_children(node):
127                    data["children"].append(compile_children(child))
128
129            return data
130
131        data = compile_children(ast.tree)
132        return dumps(data, indent=indent)

Logic for parsing and compiling html files.

JSONFormat()
extension: str = 'json'

The extension or extensions for the file format. When writing to a file and extensions is a list then the first extensions in the list is used for the file extension.

@classmethod
def parse(cls, data: dict | str) -> str:
86    @classmethod
87    def parse(cls, data: dict | str) -> str:
88        if isinstance(data, str):
89            data = loads(data)
90
91        if isinstance(data, dict):
92            node = construct_tree(data)
93            if not isinstance(node, Root):
94                node = Root(children=[node])
95            return AST(node)
96
97        raise Exception("Data passed to JSONFormat.parse must be either a str or a dict")

Parse the given data into a phml.core.nodes.AST.

@classmethod
def render( cls, ast: phml.core.nodes.AST.AST, components: Optional[dict[str, dict[str, list | phml.core.nodes.nodes.Root | phml.core.nodes.nodes.Element | phml.core.nodes.nodes.Text | phml.core.nodes.nodes.Comment | phml.core.nodes.nodes.DocType | phml.core.nodes.nodes.Parent | phml.core.nodes.nodes.Node | phml.core.nodes.nodes.Literal]]] = None, indent: int = 2, **kwargs) -> str:
 99    @classmethod
100    def render(
101        cls,
102        ast: AST,
103        components: Optional[dict[str, dict[str, list | NODE]]] = None,
104        indent: int = 2,
105        **kwargs,
106    ) -> str:
107        indent = indent or 2
108
109        def compile_children(node: Root | Element) -> dict:
110            data = {"type": node.type}
111
112            if node.type == "root":
113                if node.parent is not None:
114                    raise Exception("Root nodes must only occur as the root of an ast/tree")
115
116            for attr in vars(node):
117                if attr not in ["parent", "children", "num_lines"]:
118                    value = getattr(node, attr)
119                    if isinstance(value, Position):
120                        data[attr] = value.as_dict()
121                    else:
122                        data[attr] = value
123
124            if hasattr(node, "children"):
125                data["children"] = []
126                for child in visit_children(node):
127                    data["children"].append(compile_children(child))
128
129            return data
130
131        data = compile_children(ast.tree)
132        return dumps(data, indent=indent)

Compile the given phml.core.nodes.AST into string of a given format.