Coverage for phml\core\compile\convert.py: 48%
69 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-30 09:38 -0600
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-30 09:38 -0600
1from copy import deepcopy
2from typing import Optional
4from phml.nodes import AST, All_Nodes, Element, Position, Root
5from phml.utils import find_all, remove_nodes, visit_children
6from phml.virtual_python import VirtualPython
8from .util import apply_conditions, apply_python, replace_components
11def phml(ast: AST, indent: int = 0) -> str:
12 """Compile a given phml ast to a phml string with a certain indent amount."""
13 return __to_html(ast, indent)
16def html(
17 ast: AST,
18 components: Optional[dict[str, dict[str, list | All_Nodes]]] = None,
19 indent: int = 0,
20 **kwargs,
21) -> str:
22 """Compile a given phml ast to a html string with a certain indent amount.
24 Can provide components that replace certain elements in the ast tree along with additional
25 kwargs that are exposed to executed python blocks.
27 Args:
28 ast (AST): The phml ast to compile
29 components (dict[str, dict[str, list | All_Nodes]] | None): key value pairs of element name
30 and the replacement mapping. The replacement mapping holds reference to a components python, script,
31 and style elements along with the root replacement node.
32 indent (int): The offset amount to every indent
33 **kwargs (Any): Additional information that will be exposed to executed python blocks.
34 """
35 components = components or {}
36 src = deepcopy(ast)
38 # 1. Search for all python elements and get source info.
39 # - Remove when done
40 vp = VirtualPython()
42 for pb in find_all(src, {"tag": "python"}):
43 if len(pb.children) == 1:
44 if pb.children[0].type == "text":
45 vp += VirtualPython(pb.children[0].value)
47 remove_nodes(src, ["element", {"tag": "python"}])
49 # 2. Replace specific element node with given replacement components
50 replace_components(src, components, vp, **kwargs)
52 for pb in find_all(src, {"tag": "python"}):
53 if len(pb.children) == 1:
54 if pb.children[0].type == "text":
55 vp += VirtualPython(pb.children[0].value)
57 remove_nodes(src, ["element", {"tag": "python"}])
59 # 3. Search each element and find py-if, py-elif, py-else, and py-for
60 # - Execute those statements
62 apply_conditions(src, vp, **kwargs)
64 # 4. Search for python blocks and process them.
66 apply_python(src, vp, **kwargs)
68 return __to_html(src, indent)
71def json(ast: AST, indent: int = 0) -> str:
72 """Compile a given phml ast to a json string with a certain indent amount."""
73 from json import dumps
75 def compile_children(node: Root | Element) -> dict:
76 data = {"type": node.type}
78 if data["type"] == "root":
79 if node.parent is not None:
80 raise Exception("Root nodes must only occur as the root of an ast/tree.")
82 for attr in vars(node):
83 if attr not in ["parent", "children"]:
84 value = getattr(node, attr)
85 if isinstance(value, Position):
86 data[attr] = value.as_dict()
87 else:
88 data[attr] = value
90 if hasattr(node, "children"):
91 data["children"] = []
92 for child in visit_children(node):
93 data["children"].append(compile_children(child))
95 return data
97 data = compile_children(ast.tree)
98 return dumps(data, indent=indent)
101def markdown(ast: AST) -> str:
102 """Compile a given phml ast to a markdown string with a certain indent amount."""
103 raise NotImplementedError("Markdown is not supported.")
106def __to_html(ast: AST, offset: int = 0) -> str:
107 def compile_children(node: All_Nodes, indent: int = 0) -> list[str]:
108 data = []
109 if node.type == "element":
110 if node.startend:
111 data.append(" " * indent + node.start_tag())
112 else:
113 if (
114 len(node.children) == 1
115 and node.children[0].type == "text"
116 and node.children[0].num_lines == 1
117 ):
118 data.append(
119 "".join(
120 [
121 " " * indent + node.start_tag(),
122 node.children[0].stringify(
123 indent + offset if node.children[0].num_lines > 1 else 0
124 ),
125 node.end_tag(),
126 ]
127 )
128 )
129 else:
130 data.append(" " * indent + node.start_tag())
131 for c in visit_children(node):
132 if c.type == "element":
133 data.extend(compile_children(c, indent + offset))
134 else:
135 data.append(c.stringify(indent + offset))
136 data.append(" " * indent + node.end_tag())
137 elif node.type == "root":
138 for child in visit_children(node):
139 data.extend(compile_children(child))
140 else:
141 data.append(node.stringify(indent + offset))
142 return data
144 data = compile_children(ast.tree)
146 return "\n".join(data)