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.
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.
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.